From 304e01b5f9f108be17ec0443d9e6d463572febd0 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 24 Jul 2026 00:04:23 +0800 Subject: [PATCH 01/46] =?UTF-8?q?=E9=87=8D=E5=90=AF=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allmusic/client/core/AllMusicCore.java | 7 +- .../allmusic/client/core/AllMusicHud.java | 10 +- .../core/{player => }/AllMusicPlayer.java | 163 +++++++++--------- .../core/player/decoder/flac/FlacDecoder.java | 2 +- .../decoder/flac/SeekableFileFlacInput.java | 2 +- .../core/player/decoder/mp3/Bitstream.java | 2 +- .../core/player/decoder/mp3/Mp3Decoder.java | 2 +- .../core/player/decoder/ogg/OggDecoder.java | 2 +- settings.gradle.kts | 26 +-- 9 files changed, 105 insertions(+), 111 deletions(-) rename client/src/main/java/com/coloryr/allmusic/client/core/{player => }/AllMusicPlayer.java (75%) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicCore.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicCore.java index 3c94e5f9..523bcb7d 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicCore.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicCore.java @@ -1,7 +1,6 @@ package com.coloryr.allmusic.client.core; import com.coloryr.allmusic.client.core.objs.ConfigObj; -import com.coloryr.allmusic.client.core.player.AllMusicPlayer; import com.coloryr.allmusic.codec.CommandType; import com.coloryr.allmusic.codec.HudPosObj; import com.coloryr.allmusic.codec.MusicPack; @@ -26,6 +25,8 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; /** * AllMusic核心 @@ -52,6 +53,8 @@ public class AllMusicCore { */ private static AllMusicHud hud; + public static final ScheduledExecutorService service = Executors.newScheduledThreadPool(4); + /** * 更新音频缓存 */ @@ -135,6 +138,8 @@ public static void init(Path file, AllMusicBridge bridge, IntBuffer source) { } public static void stop() { + service.close(); + if (player != null) { player.stop(); } diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java index 40d83237..f8fb9dad 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java @@ -82,8 +82,6 @@ public class AllMusicHud { private BufferedImage bg2; private boolean isRun; - private final ScheduledExecutorService service1; - /** * 是否有图片 */ @@ -114,10 +112,9 @@ public AllMusicHud(int size) { thread.setName("allmusic_pic"); thread.start(); - service1 = Executors.newScheduledThreadPool(3); - service1.scheduleAtFixedRate(this::picRotateTick, 0, 1, TimeUnit.MILLISECONDS); - service1.scheduleAtFixedRate(this::lyricTick, 0, 10, TimeUnit.MILLISECONDS); - service1.scheduleAtFixedRate(this::loopTick, 0, 100, TimeUnit.MILLISECONDS); + AllMusicCore.service.scheduleAtFixedRate(this::picRotateTick, 0, 1, TimeUnit.MILLISECONDS); + AllMusicCore.service.scheduleAtFixedRate(this::lyricTick, 0, 10, TimeUnit.MILLISECONDS); + AllMusicCore.service.scheduleAtFixedRate(this::loopTick, 0, 100, TimeUnit.MILLISECONDS); picRender = AllMusicCore.bridge.makePictureRender(size); @@ -149,7 +146,6 @@ public AllMusicHud(int size) { public void stop() { isRun = false; - service1.close(); semaphore.release(); } diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java similarity index 75% rename from client/src/main/java/com/coloryr/allmusic/client/core/player/AllMusicPlayer.java rename to client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 73257eef..47e5b07b 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -1,6 +1,5 @@ -package com.coloryr.allmusic.client.core.player; +package com.coloryr.allmusic.client.core; -import com.coloryr.allmusic.client.core.AllMusicCore; import com.coloryr.allmusic.client.core.objs.PlayTaskObj; import com.coloryr.allmusic.client.core.player.decoder.BuffPack; import com.coloryr.allmusic.client.core.player.decoder.IDecoder; @@ -41,21 +40,16 @@ public class AllMusicPlayer extends InputStream { private boolean isPlay = false; private boolean wait = false; private int index = -1; - private IntBuffer source; + private final IntBuffer source; private long local; private boolean isRun; private boolean isChat; - private ScheduledExecutorService scheduler; + private int chatCount = 0; public AllMusicPlayer(IntBuffer source) { - try { - this.source = source; - new Thread(this::run, "allmusic_run").start(); - scheduler = Executors.newSingleThreadScheduledExecutor(); - scheduler.scheduleAtFixedRate(this::timerTick, 0, 10, TimeUnit.MILLISECONDS); - } catch (Exception e) { - e.printStackTrace(); - } + this.source = source; + new Thread(this::run, "allmusic_run").start(); + AllMusicCore.service.scheduleAtFixedRate(this::timerTick, 0, 10, TimeUnit.MILLISECONDS); } public void stop() { @@ -63,7 +57,6 @@ public void stop() { isClose = true; semaphore.release(); semaphoreReload.release(); - scheduler.close(); } public void setChat() { @@ -84,12 +77,9 @@ public void setTime(int time) { if (nowTask == null) { return; } - String url = nowTask.url; - closePlayer(); - PlayTaskObj task = new PlayTaskObj(); - task.url = url; - task.time = time; - tasks.push(task); + isClose = true; + nowTask.time = time; + tasks.push(nowTask); semaphore.release(); } @@ -110,23 +100,49 @@ public void connect() throws IOException { } private void resetSource() { - if (index != -1) { - AL10.alSourceStop(index); - AL10.alSourcei(index, AL10.AL_BUFFER, AL10.AL_NONE); - - int queued; - do { - queued = AL10.alGetSourcei(index, AL10.AL_BUFFERS_QUEUED); - if (queued > 0) { - int buffer = AL10.alSourceUnqueueBuffers(index); - if (buffer != 0) { - AL10.alDeleteBuffers(buffer); - } + AL10.alSourceStop(index); + AL10.alSourcei(index, AL10.AL_BUFFER, AL10.AL_NONE); + + int queued; + do { + queued = AL10.alGetSourcei(index, AL10.AL_BUFFERS_QUEUED); + if (queued > 0) { + int buffer = AL10.alSourceUnqueueBuffers(index); + if (buffer != 0) { + AL10.alDeleteBuffers(buffer); } - } while (queued > 0); + } + } while (queued > 0); + } + + private void checkChat() { + if (isChat) { + chatCount++; + if (chatCount >= 200) { + isChat = false; + chatCount = 0; + } + } + } - AL10.alSourcef(index, AL10.AL_GAIN, AllMusicCore.bridge.getVolume()); - AL10.alSourcef(index, AL10.AL_PITCH, 1.0f); + private void dequeue() { + int processed = AL10.alGetSourcei(index, AL10.AL_BUFFERS_PROCESSED); + for (int i = 0; i < processed; i++) { + int buffer = AL10.alSourceUnqueueBuffers(index); + if (buffer != 0) { + AL10.alDeleteBuffers(buffer); + } + } + } + + private void checkVolume() { + float temp = AllMusicCore.bridge.getVolume(); + float now = AL10.alGetSourcef(index, AL10.AL_GAIN); + if (isChat) { + temp *= 0.2F; + } + if (now != temp) { + AL10.alSourcef(index, AL10.AL_GAIN, temp); } } @@ -156,10 +172,11 @@ private void run() { } } + dequeue(); resetSource(); - PlayTaskObj task = tasks.pop(); - if (task == null || task.url == null || task.url.isEmpty()) continue; + nowTask = tasks.pop(); + if (nowTask == null || nowTask.url == null || nowTask.url.isEmpty()) continue; tasks.clear(); try { local = 0; @@ -194,25 +211,27 @@ private void run() { int frequency = decoder.getOutputFrequency(); int channels = decoder.getOutputChannels(); if (channels != 1 && channels != 2) continue; - if (task.time != 0) { - decoder.set(task.time); + if (nowTask.time != 0) { + decoder.set(nowTask.time); } reload = false; isClose = false; - int chatCount = 0; while (true) { if (!isRun) { return; } + if (isClose) { + break; + } try { - if (isClose) break; - while (AL10.alGetSourcei(index, AL10.AL_BUFFERS_QUEUED) < AllMusicCore.config.queueSize) { if (!isRun) { return; } - if (isClose) break; + if (isClose) { + break; + } BuffPack output = decoder.decodeFrame(); if (output == null) break; ByteBuffer byteBuffer = BufferUtils.createByteBuffer(output.len) @@ -238,32 +257,11 @@ private void run() { } } - float temp = AllMusicCore.bridge.getVolume(); - float now = AL10.alGetSourcef(index, AL10.AL_GAIN); - if (isChat) { - temp *= 0.2F; - } - if (now != temp) { - AL10.alSourcef(index, AL10.AL_GAIN, temp); - } - - int processed = AL10.alGetSourcei(index, AL10.AL_BUFFERS_PROCESSED); - for (int i = 0; i < processed; i++) { - int buffer = AL10.alSourceUnqueueBuffers(index); - if (buffer != 0) { - AL10.alDeleteBuffers(buffer); - } - } - Thread.sleep(5); - if (isChat) { - chatCount++; - if (chatCount >= 200) { - isChat = false; - chatCount = 0; - } - } + checkChat(); + checkVolume(); + dequeue(); } catch (Exception e) { if (!isClose) { e.printStackTrace(); @@ -277,6 +275,9 @@ private void run() { while (!isClose && AL10.alGetSourcei(index, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) { Thread.sleep(50); + checkChat(); + checkVolume(); + dequeue(); } if (!reload) { @@ -286,26 +287,18 @@ private void run() { break; } if (reload) { - tasks.push(task); + tasks.push(nowTask); semaphore.release(); continue; } } isPlay = false; - AL10.alSourceStop(index); - AL10.alSourcei(index, AL10.AL_BUFFER, AL10.AL_NONE); - int queued = AL10.alGetSourcei(index, AL10.AL_BUFFERS_QUEUED); - while (queued > 0) { - int buffer = AL10.alSourceUnqueueBuffers(index); - if (buffer != 0) { - AL10.alDeleteBuffers(buffer); - } - queued--; - } + dequeue(); + resetSource(); } else { - tasks.push(task); index = -1; + tasks.push(nowTask); semaphore.release(); } } catch (Exception e) { @@ -353,6 +346,13 @@ private void decodeClose() throws Exception { } } + public void setReload() { + if (isPlay) { + reload = true; + isClose = true; + } + } + @Override public int read() throws IOException { local++; @@ -406,11 +406,4 @@ public void setLocal(long local) throws IOException { this.local = local; connect(); } - - public void setReload() { - if (isPlay) { - reload = true; - isClose = true; - } - } } \ No newline at end of file diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/FlacDecoder.java b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/FlacDecoder.java index 9b1eaf62..d258a3c1 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/FlacDecoder.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/FlacDecoder.java @@ -18,7 +18,7 @@ package com.coloryr.allmusic.client.core.player.decoder.flac; import com.coloryr.allmusic.client.core.AllMusicCore; -import com.coloryr.allmusic.client.core.player.AllMusicPlayer; +import com.coloryr.allmusic.client.core.AllMusicPlayer; import com.coloryr.allmusic.client.core.player.decoder.BuffPack; import com.coloryr.allmusic.client.core.player.decoder.IDecoder; diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/SeekableFileFlacInput.java b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/SeekableFileFlacInput.java index c2b57d0d..fde53259 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/SeekableFileFlacInput.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/flac/SeekableFileFlacInput.java @@ -17,7 +17,7 @@ package com.coloryr.allmusic.client.core.player.decoder.flac; -import com.coloryr.allmusic.client.core.player.AllMusicPlayer; +import com.coloryr.allmusic.client.core.AllMusicPlayer; import java.io.BufferedInputStream; import java.io.IOException; diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Bitstream.java b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Bitstream.java index 7dd4dce1..fa2c0013 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Bitstream.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Bitstream.java @@ -27,7 +27,7 @@ package com.coloryr.allmusic.client.core.player.decoder.mp3; -import com.coloryr.allmusic.client.core.player.AllMusicPlayer; +import com.coloryr.allmusic.client.core.AllMusicPlayer; import java.io.ByteArrayInputStream; import java.io.IOException; diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Mp3Decoder.java b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Mp3Decoder.java index b0827adc..2499f783 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Mp3Decoder.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/mp3/Mp3Decoder.java @@ -18,7 +18,7 @@ package com.coloryr.allmusic.client.core.player.decoder.mp3; -import com.coloryr.allmusic.client.core.player.AllMusicPlayer; +import com.coloryr.allmusic.client.core.AllMusicPlayer; import com.coloryr.allmusic.client.core.player.decoder.BuffPack; import com.coloryr.allmusic.client.core.player.decoder.IDecoder; diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/ogg/OggDecoder.java b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/ogg/OggDecoder.java index 3b438698..e6c73202 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/ogg/OggDecoder.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/player/decoder/ogg/OggDecoder.java @@ -1,7 +1,7 @@ package com.coloryr.allmusic.client.core.player.decoder.ogg; import com.coloryr.allmusic.client.core.AllMusicCore; -import com.coloryr.allmusic.client.core.player.AllMusicPlayer; +import com.coloryr.allmusic.client.core.AllMusicPlayer; import com.coloryr.allmusic.client.core.player.decoder.BuffPack; import com.coloryr.allmusic.client.core.player.decoder.IDecoder; import com.coloryr.allmusic.client.core.player.decoder.ogg.jcraft.jogg.Packet; diff --git a/settings.gradle.kts b/settings.gradle.kts index 27136c01..93d8e9f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,19 +55,19 @@ include(":server:folia") include(":server:paper") include(":server:velocity") -include(":onejar") - -include(":onejar:fabric_1_16_5") -include(":onejar:fabric_1_20_1") -include(":onejar:fabric_1_21") -include(":onejar:fabric_1_21_6") -include(":onejar:fabric_1_21_11") -include(":onejar:fabric_26_1") -include(":onejar:fabric_26_2") -include(":onejar:neoforge_1_21") -include(":onejar:neoforge_1_21_6") -include(":onejar:neoforge_1_21_11") -include(":onejar:neoforge_26_1") +//include(":onejar") +// +//include(":onejar:fabric_1_16_5") +//include(":onejar:fabric_1_20_1") +//include(":onejar:fabric_1_21") +//include(":onejar:fabric_1_21_6") +//include(":onejar:fabric_1_21_11") +//include(":onejar:fabric_26_1") +//include(":onejar:fabric_26_2") +//include(":onejar:neoforge_1_21") +//include(":onejar:neoforge_1_21_6") +//include(":onejar:neoforge_1_21_11") +//include(":onejar:neoforge_26_1") pluginManagement { repositories { From 33bcc7f49b3b73118eabd0e18262355af9ea69c0 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 24 Jul 2026 19:52:06 +0800 Subject: [PATCH 02/46] Update AllMusicPlayer.java --- .../java/com/coloryr/allmusic/client/core/AllMusicPlayer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 47e5b07b..4a37cc12 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -233,7 +233,9 @@ private void run() { break; } BuffPack output = decoder.decodeFrame(); - if (output == null) break; + if (output == null) { + break; + } ByteBuffer byteBuffer = BufferUtils.createByteBuffer(output.len) .put(output.buff, 0, output.len); ((Buffer) byteBuffer).flip(); From 90cc858ca27d1e89dd7a3708404ab1ff9df219b2 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 24 Jul 2026 20:05:24 +0800 Subject: [PATCH 03/46] bug fixed #126 #123 --- build.gradle.kts | 2 +- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/mixins/SoundEvent.java | 5 ---- .../allmusic/client/AllMusicClient.java | 2 +- .../allmusic/client/AllMusicClient.java | 5 ---- .../allmusic/client/AllMusicClient.java | 7 +---- .../allmusic/client/AllMusicClient.java | 5 ---- .../allmusic/client/AllMusicClient.java | 5 ---- .../allmusic/client/AllMusicClient.java | 5 ---- .../allmusic/client/AllMusicClient.java | 5 ---- .../allmusic/client/AllMusicClient.java | 5 ---- .../allmusic/client/core/AllMusicPlayer.java | 4 +++ settings.gradle.kts | 26 +++++++++---------- 21 files changed, 20 insertions(+), 101 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d2c823db..4c0fba8c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.1.5" + version = "4.1.6" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index 37aca7ce..7ef8441b 100644 --- a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -36,9 +36,4 @@ public void play(SoundInstance soundInstance, int v, CallbackInfo info) { } } } - - @Inject(method = "resume", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } \ No newline at end of file diff --git a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index 35ac6f07..976549da 100644 --- a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -30,9 +30,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index 35ac6f07..976549da 100644 --- a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -30,9 +30,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index 61357802..24ee4a4f 100644 --- a/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -35,9 +35,4 @@ public void play(SoundInstance sound, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index 35ac6f07..976549da 100644 --- a/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -30,9 +30,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index 35ac6f07..976549da 100644 --- a/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -30,9 +30,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index fad60d82..0b9f1d3a 100644 --- a/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -32,9 +32,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index fad60d82..0b9f1d3a 100644 --- a/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -32,9 +32,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index ce02c756..5bd2ce83 100644 --- a/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -35,9 +35,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java index ce02c756..5bd2ce83 100644 --- a/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -35,9 +35,4 @@ public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { } } } - - @Inject(method = "reload", at = @At("RETURN")) - public void reload(CallbackInfo info) { - AllMusicCore.reload(); - } } diff --git a/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 0f5c7fc9..40277a7a 100644 --- a/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -149,7 +149,7 @@ public void sendMessage(String data) { } @Override - public TextFrameBuffer makeTextRender(String name) { + public TextFrameBuffer makeTextRender(String name) { return new CoreRenderTarget(name); } diff --git a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index aed652f6..100500c7 100644 --- a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -46,7 +46,6 @@ public AllMusicClient() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); modEventBus.addListener(this::setup); - modEventBus.addListener(this::onLoad); MinecraftForge.EVENT_BUS.register(this); } @@ -104,10 +103,6 @@ public int getFontHeight() { return Minecraft.getInstance().font.lineHeight; } - public void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - @SubscribeEvent public void onSound(final SoundEvent.SoundSourceEvent e) { if (!AllMusicCore.isPlay()) return; diff --git a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 0716ce7d..bff92c27 100644 --- a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -50,11 +50,6 @@ public static void setup(final FMLClientSetupEvent event) { event.enqueueWork(AllMusicCore::renderInit); } - @SubscribeEvent - public static void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - public void sendMessage(String data) { data = "[AllMusic Client]" + data; LOGGER.warn(data); @@ -102,7 +97,7 @@ public float getVolume() { } @Override - public TextFrameBuffer makeTextRender(String name) { + public TextFrameBuffer makeTextRender(String name) { return new CoreRenderTarget(name); } diff --git a/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 1a61ff23..e9e710f7 100644 --- a/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -49,11 +49,6 @@ public static void setup(final FMLClientSetupEvent event) { event.enqueueWork(AllMusicCore::renderInit); } - @SubscribeEvent - public static void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - public void sendMessage(String data) { data = "[AllMusic Client]" + data; LOGGER.warn(data); diff --git a/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 1a61ff23..e9e710f7 100644 --- a/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -49,11 +49,6 @@ public static void setup(final FMLClientSetupEvent event) { event.enqueueWork(AllMusicCore::renderInit); } - @SubscribeEvent - public static void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - public void sendMessage(String data) { data = "[AllMusic Client]" + data; LOGGER.warn(data); diff --git a/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 443f400f..d1d79080 100644 --- a/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -49,11 +49,6 @@ public static void setup(final FMLClientSetupEvent event) { event.enqueueWork(AllMusicCore::renderInit); } - @SubscribeEvent - public static void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - @SubscribeEvent public static void onSound(final PlaySoundSourceEvent e) { if (!AllMusicCore.isPlay()) return; diff --git a/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 3ac78cae..c2eed088 100644 --- a/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -79,11 +79,6 @@ public static void onServerQuit(final ClientPlayerNetworkEvent.LoggingOut e) { AllMusicCore.onServerQuit(); } - @SubscribeEvent - public static void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - @SubscribeEvent public static void onRenderOverlay(RenderGuiLayerEvent.Post e) { if (e.getName().equals(VanillaGuiLayers.CAMERA_OVERLAYS)) { diff --git a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 3ac78cae..c2eed088 100644 --- a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -79,11 +79,6 @@ public static void onServerQuit(final ClientPlayerNetworkEvent.LoggingOut e) { AllMusicCore.onServerQuit(); } - @SubscribeEvent - public static void onLoad(final SoundEngineLoadEvent e) { - AllMusicCore.reload(); - } - @SubscribeEvent public static void onRenderOverlay(RenderGuiLayerEvent.Post e) { if (e.getName().equals(VanillaGuiLayers.CAMERA_OVERLAYS)) { diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 4a37cc12..31c8c113 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -224,6 +224,10 @@ private void run() { if (isClose) { break; } + if (!AL10.alIsSource(index)) { + setReload(); + break; + } try { while (AL10.alGetSourcei(index, AL10.AL_BUFFERS_QUEUED) < AllMusicCore.config.queueSize) { if (!isRun) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 93d8e9f7..27136c01 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,19 +55,19 @@ include(":server:folia") include(":server:paper") include(":server:velocity") -//include(":onejar") -// -//include(":onejar:fabric_1_16_5") -//include(":onejar:fabric_1_20_1") -//include(":onejar:fabric_1_21") -//include(":onejar:fabric_1_21_6") -//include(":onejar:fabric_1_21_11") -//include(":onejar:fabric_26_1") -//include(":onejar:fabric_26_2") -//include(":onejar:neoforge_1_21") -//include(":onejar:neoforge_1_21_6") -//include(":onejar:neoforge_1_21_11") -//include(":onejar:neoforge_26_1") +include(":onejar") + +include(":onejar:fabric_1_16_5") +include(":onejar:fabric_1_20_1") +include(":onejar:fabric_1_21") +include(":onejar:fabric_1_21_6") +include(":onejar:fabric_1_21_11") +include(":onejar:fabric_26_1") +include(":onejar:fabric_26_2") +include(":onejar:neoforge_1_21") +include(":onejar:neoforge_1_21_6") +include(":onejar:neoforge_1_21_11") +include(":onejar:neoforge_26_1") pluginManagement { repositories { From 22bf1334afab8925b2b7f931395f93d5c94dff9b Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 24 Jul 2026 20:50:37 +0800 Subject: [PATCH 04/46] =?UTF-8?q?stop=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allmusic/client/core/AllMusicPlayer.java | 62 ++++++++++++------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 31c8c113..2dfe41d4 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -19,9 +19,8 @@ import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.IntBuffer; +import java.util.Objects; import java.util.Stack; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -79,6 +78,7 @@ public void setTime(int time) { } isClose = true; nowTask.time = time; + tasks.clear(); tasks.push(nowTask); semaphore.release(); } @@ -153,9 +153,6 @@ private void run() { isRun = true; while (true) { try { - if (!isRun) { - return; - } semaphore.acquire(); if (!isRun) { return; @@ -175,8 +172,14 @@ private void run() { dequeue(); resetSource(); + if (tasks.isEmpty()) { + continue; + } + nowTask = tasks.pop(); - if (nowTask == null || nowTask.url == null || nowTask.url.isEmpty()) continue; + if (nowTask == null || nowTask.url == null || nowTask.url.isEmpty()) { + continue; + } tasks.clear(); try { local = 0; @@ -208,13 +211,14 @@ private void run() { } isPlay = true; + int frequency = decoder.getOutputFrequency(); int channels = decoder.getOutputChannels(); if (channels != 1 && channels != 2) continue; if (nowTask.time != 0) { decoder.set(nowTask.time); } - reload = false; + isClose = false; while (true) { @@ -279,33 +283,35 @@ private void run() { streamClose(); decodeClose(); - while (!isClose && AL10.alGetSourcei(index, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) { - Thread.sleep(50); - checkChat(); - checkVolume(); + if (AL10.alIsSource(index)) { + while (!isClose && AL10.alGetSourcei(index, AL10.AL_SOURCE_STATE) == AL10.AL_PLAYING) { + Thread.sleep(50); + checkChat(); + checkVolume(); + dequeue(); + } + dequeue(); + resetSource(); } - if (!reload) { + if (reload) { wait = true; - if (semaphoreReload.tryAcquire(500, TimeUnit.MILLISECONDS)) { - if (!isRun) { - break; - } + if (semaphoreReload.tryAcquire(1, TimeUnit.SECONDS)) { if (reload) { + reload = false; + index = -1; tasks.push(nowTask); semaphore.release(); continue; } } - isPlay = false; + } - dequeue(); - resetSource(); - } else { - index = -1; - tasks.push(nowTask); - semaphore.release(); + isPlay = false; + + if (!isRun) { + return; } } catch (Exception e) { e.printStackTrace(); @@ -323,9 +329,19 @@ public void tick() { public void closePlayer() { isClose = true; nowTask = null; + tasks.clear(); } public void setMusic(String url) { + if (nowTask != null && nowTask.url.equalsIgnoreCase(url)) { + return; + } + for (PlayTaskObj item : tasks) { + if (item.url.equalsIgnoreCase(url)) { + return; + } + } + closePlayer(); PlayTaskObj taskObj = new PlayTaskObj(); taskObj.time = 0; From ed38ff7757afe2d63cce8841e9ec32dddbfd3fd5 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Thu, 30 Jul 2026 14:56:31 +0800 Subject: [PATCH 05/46] Update args handling in CommandEX.java --- .../com/coloryr/allmusic/server/core/command/CommandEX.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java index b433d781..eb66c3e6 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java @@ -119,10 +119,13 @@ public static void searchMusicApi(Object sender, String name, String[] args, boo return; } + String[] newArgs = new String[args.length - 1]; + System.arraycopy(args, 1, newArgs, 0, newArgs.length); + PlayerAddMusicObj obj = new PlayerAddMusicObj(); obj.sender = sender; obj.name = name; - obj.args = args; + obj.args = newArgs; obj.isDefault = isDefault; obj.api = apiname; From 1853b66d82ca816f5c92cf1a760f5eddd9733189 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 1 Aug 2026 15:36:41 +0800 Subject: [PATCH 06/46] 1.21.4 fixed #129 --- onejar/fabric_1_21_4/build.gradle.kts | 63 +++++++ .../main/resources/assets/allmusic/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 36 ++++ onejar/neoforge_1_21_4/build.gradle.kts | 66 +++++++ onejar/neoforge_1_21_4/gradle.properties | 1 + .../resources/META-INF/neoforge.mods.toml | 59 +++++++ .../src/main/resources/icon.png | Bin 0 -> 32781 bytes server/fabric_1_21_4/build.gradle.kts | 37 ++++ .../coloryr/allmusic/comm/AllMusicInit.java | 11 ++ .../com/coloryr/allmusic/comm/MusicCodec.java | 22 +++ .../allmusic/server/AllMusicServer.java | 34 ++++ .../allmusic/server/CommandFabric.java | 69 ++++++++ .../coloryr/allmusic/server/LogFabric.java | 11 ++ .../coloryr/allmusic/server/SideFabric.java | 157 +++++++++++++++++ .../com/coloryr/allmusic/server/TaskItem.java | 6 + .../com/coloryr/allmusic/server/Tasks.java | 27 +++ .../allmusic/server/event/MusicAddEvent.java | 32 ++++ .../allmusic/server/event/MusicPlayEvent.java | 29 +++ .../server/mixin/ServerPlayerMixin.java | 25 +++ .../resources/allmusic_server.mixins.json | 14 ++ .../resources/assets/allmusic_server/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 33 ++++ server/neoforge_1_21_4/build.gradle.kts | 44 +++++ server/neoforge_1_21_4/gradle.properties | 1 + .../coloryr/allmusic/comm/AllMusicInit.java | 33 ++++ .../com/coloryr/allmusic/comm/MusicCodec.java | 24 +++ .../allmusic/server/AllMusicServer.java | 67 +++++++ .../allmusic/server/CommandNeoForge.java | 66 +++++++ .../com/coloryr/allmusic/server/LogForge.java | 11 ++ .../coloryr/allmusic/server/SideNeoForge.java | 165 ++++++++++++++++++ .../com/coloryr/allmusic/server/TaskItem.java | 6 + .../com/coloryr/allmusic/server/Tasks.java | 27 +++ .../allmusic/server/event/MusicAddEvent.java | 39 +++++ .../allmusic/server/event/MusicPlayEvent.java | 25 +++ .../resources/META-INF/neoforge.mods.toml | 36 ++++ .../src/main/resources/icon.png | Bin 0 -> 32781 bytes settings.gradle.kts | 28 +-- 37 files changed, 1291 insertions(+), 13 deletions(-) create mode 100644 onejar/fabric_1_21_4/build.gradle.kts create mode 100644 onejar/fabric_1_21_4/src/main/resources/assets/allmusic/icon.png create mode 100644 onejar/fabric_1_21_4/src/main/resources/fabric.mod.json create mode 100644 onejar/neoforge_1_21_4/build.gradle.kts create mode 100644 onejar/neoforge_1_21_4/gradle.properties create mode 100644 onejar/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 onejar/neoforge_1_21_4/src/main/resources/icon.png create mode 100644 server/fabric_1_21_4/build.gradle.kts create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandFabric.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/LogFabric.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/SideFabric.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java create mode 100644 server/fabric_1_21_4/src/main/resources/allmusic_server.mixins.json create mode 100644 server/fabric_1_21_4/src/main/resources/assets/allmusic_server/icon.png create mode 100644 server/fabric_1_21_4/src/main/resources/fabric.mod.json create mode 100644 server/neoforge_1_21_4/build.gradle.kts create mode 100644 server/neoforge_1_21_4/gradle.properties create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/LogForge.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 server/neoforge_1_21_4/src/main/resources/icon.png diff --git a/onejar/fabric_1_21_4/build.gradle.kts b/onejar/fabric_1_21_4/build.gradle.kts new file mode 100644 index 00000000..ea7329f2 --- /dev/null +++ b/onejar/fabric_1_21_4/build.gradle.kts @@ -0,0 +1,63 @@ +plugins { + id("fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_21 +java.targetCompatibility = JavaVersion.VERSION_21 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:fabric_1_21").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:fabric_1_21").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:fabric_1_21").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:fabric_1_21").projectDir.resolve("src/main/resources") +val serverResDir = project(":server:fabric_1_21").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + serverResDir + )) + } + } +} + +dependencies { + minecraft("com.mojang:minecraft:1.21") + mappings(loom.officialMojangMappings()) + modImplementation("net.fabricmc:fabric-loader:0.16.10") + + modImplementation("net.fabricmc.fabric-api:fabric-api:0.100.1+1.21") + + modImplementation(include("net.kyori:adventure-platform-fabric:5.14.1")!!) +} + +tasks { + processResources { + // onejar 自己的 fabric.mod.json 优先(排在 srcDirs 第一位), + // client/server 的 fabric.mod.json 作为重复项被排除 + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + remapJar { + inputFile.set(shadowJar.get().archiveFile) + + archiveFileName.set("[fabric-1.21]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(remapJar) + } +} diff --git a/onejar/fabric_1_21_4/src/main/resources/assets/allmusic/icon.png b/onejar/fabric_1_21_4/src/main/resources/assets/allmusic/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/onejar/fabric_1_21_4/src/main/resources/fabric.mod.json b/onejar/fabric_1_21_4/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..ed57ca0b --- /dev/null +++ b/onejar/fabric_1_21_4/src/main/resources/fabric.mod.json @@ -0,0 +1,36 @@ +{ + "schemaVersion": 1, + "id": "allmusic", + "version": "${version}", + "name": "AllMusic", + "description": "AllMusic Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit", + "com.coloryr.allmusic.server.AllMusicServer" + ], + "client": [ + "com.coloryr.allmusic.client.AllMusicClient" + ] + }, + "mixins": [ + "allmusic_server.mixins.json", + "allmusic_client.mixins.json" + ], + "depends": { + "fabricloader": ">=0.19.3", + "fabric": "*", + "minecraft": ">=1.21" + }, + "suggests": {} +} diff --git a/onejar/neoforge_1_21_4/build.gradle.kts b/onejar/neoforge_1_21_4/build.gradle.kts new file mode 100644 index 00000000..dc6cf186 --- /dev/null +++ b/onejar/neoforge_1_21_4/build.gradle.kts @@ -0,0 +1,66 @@ +plugins { + id("dev.architectury.loom") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_21 +java.targetCompatibility = JavaVersion.VERSION_21 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:neoforge_1_21").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:neoforge_1_21").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:neoforge_1_21").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:neoforge_1_21").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + )) + } + } +} + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:1.21") + mappings(loom.officialMojangMappings()) + neoForge("net.neoforged:neoforge:21.0.167") + + modImplementation("net.kyori:adventure-platform-neoforge:6.0.1") + + implementation(include("net.kyori:adventure-platform-neoforge:6.0.1")!!) +} + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + remapJar { + inputFile.set(shadowJar.get().archiveFile) + archiveFileName.set("[neoforge-1.21]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(remapJar) + } +} diff --git a/onejar/neoforge_1_21_4/gradle.properties b/onejar/neoforge_1_21_4/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/onejar/neoforge_1_21_4/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/onejar/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml b/onejar/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..c3f00aeb --- /dev/null +++ b/onejar/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,59 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues/" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_client" +version = "${version}" +displayName = "AllMusic Client" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Client Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_client]] +modId = "neoforge" +type = "required" +versionRange = "[21.0.167,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId = "minecraft" +type = "required" +versionRange = "[1.21,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[21.0.167,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[1.21,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/onejar/neoforge_1_21_4/src/main/resources/icon.png b/onejar/neoforge_1_21_4/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/server/fabric_1_21_4/build.gradle.kts b/server/fabric_1_21_4/build.gradle.kts new file mode 100644 index 00000000..6d671418 --- /dev/null +++ b/server/fabric_1_21_4/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + id("fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_21 +java.targetCompatibility = JavaVersion.VERSION_21 + +dependencies { + minecraft("com.mojang:minecraft:1.21.4") + mappings(loom.officialMojangMappings()) + modImplementation("net.fabricmc:fabric-loader:0.19.3") + + modImplementation("net.fabricmc.fabric-api:fabric-api:0.119.4+1.21.4") + + modImplementation(include("net.kyori:adventure-platform-fabric:6.3.0")!!) +} + +tasks { + processResources { + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + remapJar { + inputFile.set(shadowJar.get().archiveFile) + + archiveFileName.set("[fabric-1.21.4]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(remapJar) + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..eab843aa --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.comm; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; + +public class AllMusicInit implements ModInitializer { + @Override + public void onInitialize() { + PayloadTypeRegistry.playS2C().register(MusicCodec.ID, MusicCodec.CODEC); + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..feb83760 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,22 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final ResourceLocation ChannelID = ResourceLocation.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type ID = new Type<>(ChannelID); + public static final StreamCodec CODEC = + StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + buf -> new MusicCodec(MusicPacketCodec.decode(buf))); + + @Override + public Type type() { + return ID; + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java new file mode 100644 index 00000000..18119e20 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java @@ -0,0 +1,34 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import net.fabricmc.api.DedicatedServerModInitializer; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.minecraft.server.MinecraftServer; + +import java.io.File; + +public class AllMusicServer implements ModInitializer { + public static MinecraftServer server; + + @Override + public void onInitialize() { + AllMusic.log = new LogFabric(); + AllMusic.side = new SideFabric(); + + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) + -> CommandFabric.instance.register(dispatcher)); + + ServerLifecycleEvents.SERVER_STARTED.register((a) -> { + server = a; + AllMusic.init(new File(AllMusic.SERVER_DIR)); + AllMusic.start(); + Tasks.init(); + }); + + ServerLifecycleEvents.SERVER_STOPPING.register((a) -> { + AllMusic.stop(); + }); + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandFabric.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandFabric.java new file mode 100644 index 00000000..5bf02199 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandFabric.java @@ -0,0 +1,69 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class CommandFabric implements Command, Predicate, SuggestionProvider { + public static CommandFabric instance = new CommandFabric(); + + public void register(CommandDispatcher dispatcher) { + dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) + .then(Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this).executes(this))); + } + + @Override + public int run(CommandContext context) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, arg.length); + + CommandEX.execute(item, context.getSource().getTextName(), arg); + + return 0; + } + + @Override + public boolean test(CommandSourceStack stack) { + return true; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, temp.length - 1); + + builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); + + List results = CommandEX.getTabList(item, item.getTextName(), arg); + + String remaining = builder.getRemaining().trim(); + + for (String s : results) { + if (s.startsWith(remaining)) { + builder.suggest(s); + } + } + + return builder.buildFuture(); + } +} \ No newline at end of file diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/LogFabric.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/LogFabric.java new file mode 100644 index 00000000..e2885068 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/LogFabric.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + +public class LogFabric implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicServer.server.sendMessage(data); + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/SideFabric.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/SideFabric.java new file mode 100644 index 00000000..927259ea --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/SideFabric.java @@ -0,0 +1,157 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.comm.MusicCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; + +import java.io.File; +import java.util.Collection; + +public class SideFabric extends BaseSide { + + @Override + public void runTask(Runnable run) { + AllMusicServer.server.execute(run); + } + + @Override + public void runTask(Runnable run1, int delay) { + Tasks.add(new TaskItem() {{ + tick = delay; + run = run1; + }}); + } + + @Override + public boolean checkPermission(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.hasPermission(2); + } + + @Override + public boolean isPlayer(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.isPlayer(); + } + + @Override + public boolean checkPermission(Object player, String permission) { + return checkPermission(player); + } + + @Override + public boolean needPlay(boolean islist) { + for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false, islist)) { + return true; + } + } + return false; + } + + @Override + public Collection getPlayers() { + return AllMusicServer.server.getPlayerList().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ServerPlayer player1) { + return player1.getName().getString(); + } + + return null; + } + + @Override + public String getPlayerServer(Object player) { + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ServerPlayer player1) { + send(player1, pack); + } + } + + @Override + public Object getPlayer(String player) { + return AllMusicServer.server.getPlayerList().getPlayerByName(player); + } + + @Override + public void sendBar(Object player, Component data) { + if (player instanceof ServerPlayer player1) { + player1.sendActionBar(data); + } + } + + @Override + public File getFolder() { + return new File(AllMusic.SERVER_DIR); + } + + @Override + public void broadcast(Component message) { + for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false)) { + player.sendMessage(message); + } + } + } + + @Override + public void sendMessage(Object obj, Component message) { + if (obj instanceof CommandSourceStack source) { + source.sendMessage(message); + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + return MusicPlayEvent.EVENT.invoker().interact(obj) != InteractionResult.PASS; + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + CommandSourceStack source = (CommandSourceStack) obj; + return MusicAddEvent.EVENT.invoker().interact(source.getPlayer(), music) != InteractionResult.PASS; + } + + @Override + public Component miniMessage(String input) { + MiniMessage mm = MiniMessage.miniMessage(); + return mm.deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.SUGGEST_COMMAND, command)); + } + + private void send(ServerPlayer players, MusicPack data) { + if (players == null) + return; + runTask(() -> ServerPlayNetworking.send(players, new MusicCodec(data))); + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java new file mode 100644 index 00000000..40f9a5b9 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java @@ -0,0 +1,6 @@ +package com.coloryr.allmusic.server; + +public class TaskItem { + public int tick; + public Runnable run; +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java new file mode 100644 index 00000000..d4e72d39 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java @@ -0,0 +1,27 @@ +package com.coloryr.allmusic.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tasks { + private static final List taskItems = new CopyOnWriteArrayList<>(); + + public static void init() { + AllMusicServer.server.addTickable(Tasks::tick); + } + + public static void tick() { + for (var item : new ArrayList<>(taskItems)) { + item.tick--; + if (item.tick <= 0) { + taskItems.remove(item); + item.run.run(); + } + } + } + + public static void add(TaskItem item) { + taskItems.add(item); + } +} diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..b1ff9ccb --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,32 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; + +public interface MusicAddEvent { + Event EVENT = EventFactory.createArrayBacked(MusicAddEvent.class, + (listeners) -> (player, music) -> { + for (MusicAddEvent listener : listeners) { + InteractionResult result = listener.interact(player, music); + + if (result != InteractionResult.PASS) { + return result; + } + } + + return InteractionResult.PASS; + }); + + /** + * 音乐添加事件,返回PASS表示通过 + * + * @param player 玩家 + * @param music 歌曲信息 + * @return 是否通过 + */ + InteractionResult interact(ServerPlayer player, PlayerAddMusicObj music); +} + diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..c4f27c93 --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,29 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.world.InteractionResult; + +public interface MusicPlayEvent { + Event EVENT = EventFactory.createArrayBacked(MusicPlayEvent.class, + (listeners) -> (music) -> { + for (MusicPlayEvent listener : listeners) { + InteractionResult result = listener.interact(music); + + if (result != InteractionResult.PASS) { + return result; + } + } + + return InteractionResult.PASS; + }); + + /** + * 音乐播放事件,返回PASS表示通过 + * + * @param music 歌曲信息 + * @return 是否通过 + */ + InteractionResult interact(SongInfoObj music); +} \ No newline at end of file diff --git a/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java new file mode 100644 index 00000000..4b48badb --- /dev/null +++ b/server/fabric_1_21_4/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java @@ -0,0 +1,25 @@ +package com.coloryr.allmusic.server.mixin; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import net.minecraft.network.Connection; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.CommonListenerCookie; +import net.minecraft.server.players.PlayerList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerList.class) +public class ServerPlayerMixin { + @Inject(at = @At("HEAD"), method = "remove") + private void remove(ServerPlayer entity, CallbackInfo info) { + PlayMusic.removeNowPlayPlayer(entity.getName().getString()); + } + + @Inject(at = @At("TAIL"), method = "placeNewPlayer") + private void add(Connection connection, ServerPlayer serverPlayer, CommonListenerCookie commonListenerCookie, CallbackInfo ci) { + AllMusic.joinPlay(serverPlayer.getName().getString()); + } +} diff --git a/server/fabric_1_21_4/src/main/resources/allmusic_server.mixins.json b/server/fabric_1_21_4/src/main/resources/allmusic_server.mixins.json new file mode 100644 index 00000000..2afc5263 --- /dev/null +++ b/server/fabric_1_21_4/src/main/resources/allmusic_server.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.coloryr.allmusic.server.mixin", + "compatibilityLevel": "JAVA_17", + "server": [ + ], + "mixins": [ + "ServerPlayerMixin" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/server/fabric_1_21_4/src/main/resources/assets/allmusic_server/icon.png b/server/fabric_1_21_4/src/main/resources/assets/allmusic_server/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/server/fabric_1_21_4/src/main/resources/fabric.mod.json b/server/fabric_1_21_4/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..7636c565 --- /dev/null +++ b/server/fabric_1_21_4/src/main/resources/fabric.mod.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "id": "allmusic_server", + "version": "${version}", + "name": "AllMusic Server", + "description": "AllMusic Server Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic_server/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit", + "com.coloryr.allmusic.server.AllMusicServer" + ] + }, + "mixins": [ + "allmusic_server.mixins.json" + ], + "depends": { + "fabricloader": ">=0.19.3", + "minecraft": ">=1.21.4", + "fabric-api": "*" + }, + "suggests": { + } +} diff --git a/server/neoforge_1_21_4/build.gradle.kts b/server/neoforge_1_21_4/build.gradle.kts new file mode 100644 index 00000000..4561d444 --- /dev/null +++ b/server/neoforge_1_21_4/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("dev.architectury.loom") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_21 +java.targetCompatibility = JavaVersion.VERSION_21 + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:1.21.4") + mappings(loom.officialMojangMappings()) + neoForge("net.neoforged:neoforge:21.4.141") + + modImplementation("net.kyori:adventure-platform-neoforge:6.3.0") + + implementation(include("net.kyori:adventure-platform-neoforge:6.3.0")!!) +} + +tasks { + processResources { + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + remapJar { + inputFile.set(shadowJar.get().archiveFile) + archiveFileName.set("[neoforge-1.21.4]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(remapJar) + } +} diff --git a/server/neoforge_1_21_4/gradle.properties b/server/neoforge_1_21_4/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/server/neoforge_1_21_4/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..d9282960 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.comm; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +@Mod(AllMusicInit.MODID) +public class AllMusicInit implements IPayloadHandler { + + public static final String MODID = "allmusic"; + + public static IPayloadHandler handler; + + public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { + modEventBus.addListener(this::register); + } + + public void register(final RegisterPayloadHandlersEvent event) { + event.registrar("1.0") + .optional() + .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); + } + + @Override + public void handle(MusicCodec data, IPayloadContext iPayloadContext) { + if (handler != null) { + handler.handle(data, iPayloadContext); + } + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..81ef9090 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final ResourceLocation channel = + ResourceLocation.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type TYPE = new Type<>(channel); + public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + (buf) -> { + return new MusicCodec(MusicPacketCodec.decode(buf)); + }); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java new file mode 100644 index 00000000..a20942f6 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java @@ -0,0 +1,67 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import com.mojang.brigadier.CommandDispatcher; +import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.MinecraftServer; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +@EventBusSubscriber(modid = "allmusic_server") +public class AllMusicServer { + public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Server"); + public static MinecraftServer server; + + public static MinecraftServerAudiences audiences; + + @SubscribeEvent + public static void commonSetup(final FMLCommonSetupEvent event) { + AllMusic.log = new LogForge(); + AllMusic.side = new SideNeoForge(); + } + + @SubscribeEvent + public static void onRegisterCommands(RegisterCommandsEvent event) { + CommandDispatcher dispatcher = event.getDispatcher(); + CommandNeoForge.instance.register(dispatcher); + } + + @SubscribeEvent + public static void onServerStarting(ServerStartedEvent event) { + server = event.getServer(); + audiences = MinecraftServerAudiences.of(server); + AllMusic.init(new File(AllMusic.SERVER_DIR)); + AllMusic.start(); + Tasks.init(); + } + + @SubscribeEvent + public static void onServerStopping(ServerStoppingEvent event) { + AllMusic.stop(); + } + + @SubscribeEvent + public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + if (server != null) { + PlayMusic.removeNowPlayPlayer(event.getEntity().getName().getString()); + } + } + + @SubscribeEvent + public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { + if (server != null) { + AllMusic.joinPlay(event.getEntity().getName().getString()); + } + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java new file mode 100644 index 00000000..f6275e4e --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java @@ -0,0 +1,66 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class CommandNeoForge implements Command, Predicate, SuggestionProvider { + public static CommandNeoForge instance = new CommandNeoForge(); + + public void register(CommandDispatcher dispatcher) { + dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) + .then(Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this).executes(this))); + } + + @Override + public int run(CommandContext context) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, arg.length); + + CommandEX.execute(item, context.getSource().getTextName(), arg); + + return 0; + } + + @Override + public boolean test(CommandSourceStack stack) { + return true; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, temp.length - 1); + + if (input.endsWith(" ")) + builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); + + List results = CommandEX.getTabList(item, item.getTextName(), arg); + + for (String s : results) { + builder.suggest(s); + } + + return builder.buildFuture(); + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/LogForge.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/LogForge.java new file mode 100644 index 00000000..f074711b --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/LogForge.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + +public class LogForge implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicServer.audiences.console().sendMessage(data); + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java new file mode 100644 index 00000000..4a99c3e7 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java @@ -0,0 +1,165 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.comm.MusicCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.network.PacketDistributor; + +import java.io.File; +import java.util.Collection; + +public class SideNeoForge extends BaseSide { + @Override + public void runTask(Runnable run) { + AllMusicServer.server.execute(run); + } + + @Override + public void runTask(Runnable run1, int delay) { + Tasks.add(new TaskItem() {{ + tick = delay; + run = run1; + }}); + } + + @Override + public boolean checkPermission(Object player, String permission) { + return checkPermission(player); + } + + @Override + public boolean checkPermission(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.hasPermission(2); + } + + @Override + public boolean isPlayer(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.isPlayer(); + } + + @Override + public boolean needPlay(boolean islist) { + for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false, islist)) { + return true; + } + } + return false; + } + + @Override + public Collection getPlayers() { + return AllMusicServer.server.getPlayerList().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ServerPlayer player1) { + return player1.getName().getString(); + } + + return null; + } + + @Override + public String getPlayerServer(Object player) { + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ServerPlayer player1) { + send(player1, pack); + } + } + + @Override + public Object getPlayer(String player) { + return AllMusicServer.server.getPlayerList().getPlayerByName(player); + } + + @Override + public void sendBar(Object player, Component data) { + if (player instanceof ServerPlayer player1) { + AllMusicServer.audiences.audience(player1).sendActionBar(data); + } + } + + @Override + public File getFolder() { + return new File(AllMusic.SERVER_DIR); + } + + @Override + public void broadcast(Component message) { + for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false)) { + AllMusicServer.audiences.audience(player).sendMessage(message); + } + } + } + + @Override + public void sendMessage(Object obj, Component message) { + if (obj instanceof CommandSourceStack source) { + AllMusicServer.audiences.audience(source).sendMessage(message); + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + MusicPlayEvent event = new MusicPlayEvent(obj); + NeoForge.EVENT_BUS.post(event); + return event.isCancel(); + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + CommandSourceStack source = (CommandSourceStack) obj; + MusicAddEvent event = new MusicAddEvent(music, source.getPlayer()); + NeoForge.EVENT_BUS.post(event); + return event.isCancel(); + } + + @Override + public Component miniMessage(String input) { + MiniMessage mm = MiniMessage.miniMessage(); + return mm.deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.runCommand(command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.suggestCommand(command)); + } + + private void send(ServerPlayer players, MusicPack data) { + if (players == null) + return; + try { + runTask(() -> PacketDistributor.sendToPlayer(players, new MusicCodec(data))); + } catch (Exception e) { + AllMusic.log.data("数据发送发生错误"); + e.printStackTrace(); + } + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java new file mode 100644 index 00000000..40f9a5b9 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/TaskItem.java @@ -0,0 +1,6 @@ +package com.coloryr.allmusic.server; + +public class TaskItem { + public int tick; + public Runnable run; +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java new file mode 100644 index 00000000..b861abcc --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/Tasks.java @@ -0,0 +1,27 @@ +package com.coloryr.allmusic.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tasks { + private static final List taskItems = new CopyOnWriteArrayList<>(); + + public static void init() { + AllMusicServer.server.addTickable(Tasks::tick); + } + + public static void tick() { + for (TaskItem item : new ArrayList<>(taskItems)) { + item.tick--; + if (item.tick <= 0) { + taskItems.remove(item); + item.run.run(); + } + } + } + + public static void add(TaskItem item) { + taskItems.add(item); + } +} \ No newline at end of file diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..7c730137 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,39 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.bus.api.Event; + +public class MusicAddEvent extends Event { + /** + * 添加的音乐 + */ + private final PlayerAddMusicObj music; + /** + * 添加者 + */ + private final ServerPlayer player; + + private boolean cancel; + + public MusicAddEvent(PlayerAddMusicObj id, ServerPlayer player) { + this.music = id; + this.player = player; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public ServerPlayer getPlayer() { + return player; + } + + public PlayerAddMusicObj getMusic() { + return music; + } +} diff --git a/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..b16e0f95 --- /dev/null +++ b/server/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,25 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.neoforged.bus.api.Event; + +public class MusicPlayEvent extends Event { + private final SongInfoObj music; + private boolean cancel; + + public MusicPlayEvent(SongInfoObj music) { + this.music = music; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public SongInfoObj getMusic() { + return music; + } +} diff --git a/server/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml b/server/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..e92a73ca --- /dev/null +++ b/server/neoforge_1_21_4/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,36 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues/" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[21.4.141,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[1.21.4,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/server/neoforge_1_21_4/src/main/resources/icon.png b/server/neoforge_1_21_4/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/settings.gradle.kts b/settings.gradle.kts index 27136c01..e6ae5385 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,6 +33,7 @@ include(":server") include(":server:fabric_1_16_5") include(":server:fabric_1_20_1") include(":server:fabric_1_21") +include(":server:fabric_1_21_4") include(":server:fabric_1_21_6") include(":server:fabric_1_21_11") include(":server:fabric_26_1") @@ -44,6 +45,7 @@ include(":server:forge_1_16_5") include(":server:forge_1_20_1") include(":server:neoforge_1_21") +include(":server:neoforge_1_21_4") include(":server:neoforge_1_21_6") include(":server:neoforge_1_21_11") include(":server:neoforge_26_1") @@ -55,19 +57,19 @@ include(":server:folia") include(":server:paper") include(":server:velocity") -include(":onejar") - -include(":onejar:fabric_1_16_5") -include(":onejar:fabric_1_20_1") -include(":onejar:fabric_1_21") -include(":onejar:fabric_1_21_6") -include(":onejar:fabric_1_21_11") -include(":onejar:fabric_26_1") -include(":onejar:fabric_26_2") -include(":onejar:neoforge_1_21") -include(":onejar:neoforge_1_21_6") -include(":onejar:neoforge_1_21_11") -include(":onejar:neoforge_26_1") +//include(":onejar") +// +//include(":onejar:fabric_1_16_5") +//include(":onejar:fabric_1_20_1") +//include(":onejar:fabric_1_21") +//include(":onejar:fabric_1_21_6") +//include(":onejar:fabric_1_21_11") +//include(":onejar:fabric_26_1") +//include(":onejar:fabric_26_2") +//include(":onejar:neoforge_1_21") +//include(":onejar:neoforge_1_21_6") +//include(":onejar:neoforge_1_21_11") +//include(":onejar:neoforge_26_1") pluginManagement { repositories { From de187bb66a8fefb37d10adce235b2a8d41f14dc3 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 1 Aug 2026 15:39:11 +0800 Subject: [PATCH 07/46] Update AllMusicPlayer.java --- .../com/coloryr/allmusic/client/core/AllMusicPlayer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 2dfe41d4..237e4a94 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -139,7 +139,10 @@ private void checkVolume() { float temp = AllMusicCore.bridge.getVolume(); float now = AL10.alGetSourcef(index, AL10.AL_GAIN); if (isChat) { - temp *= 0.2F; + temp *= 0.5F; + } + if (temp < 0.05F) { + temp = 0.05F; } if (now != temp) { AL10.alSourcef(index, AL10.AL_GAIN, temp); From c3b9b7eeddbf6e40a9b43dca3c78a51161aec85d Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 1 Aug 2026 15:39:19 +0800 Subject: [PATCH 08/46] Update build.gradle.kts --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4c0fba8c..6e84b0ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.1.6" + version = "4.1.7" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 From d5a1b25cea7909d65f4be95ae8f55f7aa4121b8a Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 1 Aug 2026 15:42:13 +0800 Subject: [PATCH 09/46] Fix badge links and enhance README content Updated badge links and improved README formatting. --- README.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8f6ea137..c35baa1d 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,17 @@ ![AllMusic](https://socialify.git.ci/Coloryr/AllMusic/image?description=1&font=Inter&forks=1&logo=https%3A%2F%2Fgithub.com%2FColoryr%2FAllMusic%2Fblob%2Fmain%2Fserver%2Fneoforge_26_1%2Fsrc%2Fmain%2Fresources%2Ficon.png%3Fraw%3Dtrue&name=1&owner=1&pattern=Signal&stargazers=1&theme=Auto) ![](https://img.shields.io/badge/Version-3.1.4-blue?style=for-the-badge) -![](https://img.shields.io/github/actions/workflow/status/Coloryr/AllMusic/gradle.yml?style=for-the-badge) +![](https://img.shields.io/github/actions/workflow/status/Coloryr/AllMusic/client.yml?style=for-the-badge) +![](https://img.shields.io/github/actions/workflow/status/Coloryr/AllMusic/server.yml?style=for-the-badge) +![](https://img.shields.io/github/actions/workflow/status/Coloryr/AllMusic/onejar.yml?style=for-the-badge) ![](https://img.shields.io/github/license/Coloryr/AllMusic?style=for-the-badge) ![GIF.gif](img/GIF.gif) -注:需要配合客户端Mod使用 +**注:需要配合客户端Mod使用** +**1.7.10和1.12.2没有技术更新** **不兼容旧版客户端Mod,需要新版客户端Mod** **如果面板服有问题,请自己去问服务器商给不给你用** **4.0服务器只能跟4.0客户端用,不兼容3.0的服务器或客户端** @@ -29,16 +32,18 @@ 1. 安装AllMusic_Server Paper服务器 -复制`[paper]AllMusic_Server-4.0.0-all.jar`到你的`plugins`文件夹 +复制`[paper]AllMusic_Server-4.0.0.jar`到你的`plugins`文件夹 Velocity服务器 -复制`[velocity]AllMusic_Server-4.0.0-all.jar`到你的`plugins`文件夹 +复制`[velocity]AllMusic_Server-4.0.0.jar`到你的`plugins`文件夹 Forge/Fabric/NeoForge类服务器 -复制`[xxx-xxx]AllMusic_Server-4.0.0-all.jar`到你的`mods`文件夹 +复制`[xxx-xxx]AllMusic_Server-4.0.0.jar`到你的`mods`文件夹 2. 安装客户端mod -复制`[xxx-xxx]AllMusic_Client-4.0.0-all`到客户端的`mods`文件夹 +复制`[xxx-xxx]AllMusic_Client-4.0.0`到客户端的`mods`文件夹 重启客户端 +如果你是单人游戏使用或者联机使用OneJar版本 + ## 音乐API AllMusic不会内置音乐api,你想要正常使用需要自己安装 这里提供一个参考api https://github.com/Coloryr/netapi @@ -62,12 +67,6 @@ AllMusic不会内置音乐api,你想要正常使用需要自己安装 > %allmusic_lyric% 歌词 > %allmusic_tlyric% 翻译的歌词 -## 更新日志 - -``` -4.0.0:全新字体渲染,拆分音乐API -``` - ## 配置文件说明 配置文件采用json格式,需要遵守json编写的格式规范 - maxPlayList 最大歌曲数 From e88bf512b6c1625a504e3bbccaeb679c691452d6 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 1 Aug 2026 19:49:06 +0800 Subject: [PATCH 10/46] Update settings.gradle.kts --- settings.gradle.kts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index e6ae5385..92e16a3b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -57,19 +57,19 @@ include(":server:folia") include(":server:paper") include(":server:velocity") -//include(":onejar") -// -//include(":onejar:fabric_1_16_5") -//include(":onejar:fabric_1_20_1") -//include(":onejar:fabric_1_21") -//include(":onejar:fabric_1_21_6") -//include(":onejar:fabric_1_21_11") -//include(":onejar:fabric_26_1") -//include(":onejar:fabric_26_2") -//include(":onejar:neoforge_1_21") -//include(":onejar:neoforge_1_21_6") -//include(":onejar:neoforge_1_21_11") -//include(":onejar:neoforge_26_1") +include(":onejar") + +include(":onejar:fabric_1_16_5") +include(":onejar:fabric_1_20_1") +include(":onejar:fabric_1_21") +include(":onejar:fabric_1_21_6") +include(":onejar:fabric_1_21_11") +include(":onejar:fabric_26_1") +include(":onejar:fabric_26_2") +include(":onejar:neoforge_1_21") +include(":onejar:neoforge_1_21_6") +include(":onejar:neoforge_1_21_11") +include(":onejar:neoforge_26_1") pluginManagement { repositories { From 736d297b07c4498260984b9d49fd3c4f59849bd5 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Mon, 3 Aug 2026 18:59:56 +0800 Subject: [PATCH 11/46] fix bug fixed #131 --- build.gradle.kts | 2 +- .../com/coloryr/allmusic/server/core/command/CommandEX.java | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6e84b0ff..a691a8e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.1.7" + version = "4.1.8" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java index eb66c3e6..75355ad7 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java @@ -118,14 +118,11 @@ public static void searchMusicApi(Object sender, String name, String[] args, boo AllMusic.side.sendMessage(sender, AllMusic.getMessage().musicPlay.error2); return; } - - String[] newArgs = new String[args.length - 1]; - System.arraycopy(args, 1, newArgs, 0, newArgs.length); PlayerAddMusicObj obj = new PlayerAddMusicObj(); obj.sender = sender; obj.name = name; - obj.args = newArgs; + obj.args = args; obj.isDefault = isDefault; obj.api = apiname; From 7e15c9c8615d7717dcf4fe7607bbb86f25388b2f Mon Sep 17 00:00:00 2001 From: hutuyee Date: Wed, 19 Aug 2026 19:59:22 +0800 Subject: [PATCH 12/46] Add BungeeCord support --- server/bungeecord/build.gradle.kts | 31 ++ .../allmusic/server/AllMusicBungee.java | 42 ++ .../allmusic/server/CommandBungee.java | 22 + .../allmusic/server/ListenerBungee.java | 45 +++ .../coloryr/allmusic/server/LogBungee.java | 11 + .../coloryr/allmusic/server/SideBungee.java | 376 ++++++++++++++++++ .../allmusic/server/event/MusicAddEvent.java | 44 ++ .../allmusic/server/event/MusicPlayEvent.java | 34 ++ .../bungeecord/src/main/resources/bungee.yml | 4 + settings.gradle.kts | 1 + 10 files changed, 610 insertions(+) create mode 100644 server/bungeecord/build.gradle.kts create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/AllMusicBungee.java create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/CommandBungee.java create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/ListenerBungee.java create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/LogBungee.java create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/SideBungee.java create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/bungeecord/src/main/resources/bungee.yml diff --git a/server/bungeecord/build.gradle.kts b/server/bungeecord/build.gradle.kts new file mode 100644 index 00000000..9408c477 --- /dev/null +++ b/server/bungeecord/build.gradle.kts @@ -0,0 +1,31 @@ +dependencies { + // 使用较早且稳定的 API 编译,避免无意间调用新版本 BungeeCord 才提供的接口。 + compileOnly("net.md-5:bungeecord-api:1.16-R0.4") + + shadowImplementation("net.kyori:adventure-platform-bungeecord:4.4.1") { + exclude(group = "net.md-5", module = "bungeecord-api") + } + shadowImplementation("net.kyori:adventure-text-minimessage:${Versions.minimessage}") + shadowImplementation("net.kyori:adventure-api:${Versions.minimessage}") + shadowImplementation("net.kyori:adventure-text-serializer-gson:${Versions.minimessage}") + shadowImplementation("net.kyori:adventure-text-serializer-legacy:${Versions.minimessage}") + shadowImplementation("net.kyori:adventure-text-serializer-plain:${Versions.minimessage}") + shadowImplementation("net.kyori:adventure-key:${Versions.minimessage}") +} + +tasks { + processResources { + filesMatching("bungee.yml") { + expand( + "version" to project.version + ) + } + } + + shadowJar { + archiveFileName.set("[bungeecord]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + + relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/AllMusicBungee.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/AllMusicBungee.java new file mode 100644 index 00000000..da3d20bd --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/AllMusicBungee.java @@ -0,0 +1,42 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import net.kyori.adventure.platform.bungeecord.BungeeAudiences; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.plugin.Plugin; + +public class AllMusicBungee extends Plugin { + public static AllMusicBungee plugin; + public static BungeeAudiences adventure; + + @Override + public void onEnable() { + plugin = this; + adventure = BungeeAudiences.create(this); + AllMusic.log = new LogBungee(); + SideBungee side = new SideBungee(); + AllMusic.side = side; + AllMusic.economy = side; + + AllMusic.init(getDataFolder()); + if (!AllMusic.isRun) { + return; + } + + ProxyServer proxy = ProxyServer.getInstance(); + proxy.registerChannel(AllMusic.channelBC); + proxy.getPluginManager().registerCommand(this, new CommandBungee()); + proxy.getPluginManager().registerListener(this, new ListenerBungee()); + + AllMusic.start(); + } + + @Override + public void onDisable() { + AllMusic.stop(); + if (adventure != null) { + adventure.close(); + adventure = null; + } + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/CommandBungee.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/CommandBungee.java new file mode 100644 index 00000000..9f7aab7f --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/CommandBungee.java @@ -0,0 +1,22 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.api.plugin.TabExecutor; + +public class CommandBungee extends Command implements TabExecutor { + public CommandBungee() { + super("music"); + } + + @Override + public void execute(CommandSender sender, String[] args) { + CommandEX.execute(sender, sender.getName(), args); + } + + @Override + public Iterable onTabComplete(CommandSender sender, String[] args) { + return CommandEX.getTabList(sender, sender.getName(), args); + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/ListenerBungee.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/ListenerBungee.java new file mode 100644 index 00000000..8037e1ae --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/ListenerBungee.java @@ -0,0 +1,45 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteStreams; +import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.api.event.ServerConnectedEvent; +import net.md_5.bungee.api.plugin.Listener; +import net.md_5.bungee.event.EventHandler; + +public class ListenerBungee implements Listener { + @EventHandler + public void onPlayerDisconnectEvent(PlayerDisconnectEvent event) { + PlayMusic.removeNowPlayPlayer(event.getPlayer().getName()); + } + + @EventHandler + public void onServerConnectedEvent(ServerConnectedEvent event) { + AllMusic.joinPlay(event.getPlayer().getName()); + } + + @EventHandler + public void onPluginMessageEvent(PluginMessageEvent event) { + if (!event.getTag().equals(AllMusic.channelBC)) { + return; + } + + event.setCancelled(true); + ByteArrayDataInput data = ByteStreams.newDataInput(event.getData()); + int type = data.readInt(); + if (type == 255 && event.getSender() instanceof Server) { + Server server = (Server) event.getSender(); + SideBungee.TopServers.add(server); + SideBungee.sendAllToServer(server); + SideBungee.sendLyricToServer(server); + } else if (type == 12 || type == 13) { + String uuid = data.readUTF(); + int res = data.readInt(); + SideBungee.SendToBackend.put(uuid, res); + } + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/LogBungee.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/LogBungee.java new file mode 100644 index 00000000..c69ffcc8 --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/LogBungee.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + +public class LogBungee implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicBungee.adventure.console().sendMessage(data); + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/SideBungee.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/SideBungee.java new file mode 100644 index 00000000..03981b31 --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/SideBungee.java @@ -0,0 +1,376 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.IEconomy; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import io.netty.buffer.ByteBuf; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.connection.Server; + +import java.io.File; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.TimeUnit; + +public class SideBungee extends BaseSide implements IEconomy { + public static final Set TopServers = new CopyOnWriteArraySet<>(); + + public static final Map SendToBackend = new ConcurrentHashMap<>(); + + public static void sendAllToServer(Server server) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeInt(0); + if (PlayMusic.nowPlayMusic == null) { + out.writeUTF(AllMusic.getMessage().papi.emptyMusic); + } else { + out.writeUTF(PlayMusic.nowPlayMusic.getName()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(1); + if (PlayMusic.nowPlayMusic == null) { + out.writeUTF(""); + } else { + out.writeUTF(PlayMusic.nowPlayMusic.getAl()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(2); + if (PlayMusic.nowPlayMusic == null) { + out.writeUTF(""); + } else { + out.writeUTF(PlayMusic.nowPlayMusic.getAlia()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(3); + if (PlayMusic.nowPlayMusic == null) { + out.writeUTF(""); + } else { + out.writeUTF(PlayMusic.nowPlayMusic.getAuthor()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(4); + if (PlayMusic.nowPlayMusic == null) { + out.writeUTF(""); + } else { + out.writeUTF(PlayMusic.nowPlayMusic.getCall()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(5); + out.writeInt(PlayMusic.getListSize()); + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(6); + out.writeUTF(PlayMusic.getAllList()); + server.sendData(AllMusic.channelBC, out.toByteArray()); + } + + public static void sendLyricToServer(Server server) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeInt(7); + if (PlayMusic.lyric == null) { + out.writeUTF(""); + } else { + out.writeUTF(PlayMusic.lyric.getLyric()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(8); + if (PlayMusic.lyric == null || PlayMusic.lyric.getTlyric() == null) { + out.writeUTF(""); + } else { + out.writeUTF(PlayMusic.lyric.getTlyric()); + } + server.sendData(AllMusic.channelBC, out.toByteArray()); + + out = ByteStreams.newDataOutput(); + out.writeInt(9); + out.writeBoolean(PlayMusic.lyric != null && PlayMusic.lyric.getTlyric() != null); + server.sendData(AllMusic.channelBC, out.toByteArray()); + } + + @Override + public void broadcast(Component data) { + for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { + if (skip(player)) { + continue; + } + + AllMusicBungee.adventure.player(player).sendMessage(data); + } + } + + @Override + public boolean needPlay(boolean islist) { + for (ProxiedPlayer player : ProxyServer.getInstance().getPlayers()) { + String server = player.getServer() == null ? null : player.getServer().getInfo().getName(); + if (!AllMusic.isSkip(player.getName(), server, false, islist)) { + return true; + } + } + + return false; + } + + @Override + public Collection getPlayers() { + return ProxyServer.getInstance().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ProxiedPlayer) { + return ((ProxiedPlayer) player).getName(); + } + return null; + } + + @Override + public String getPlayerServer(Object player) { + if (player instanceof ProxiedPlayer) { + Server server = ((ProxiedPlayer) player).getServer(); + if (server != null) { + return server.getInfo().getName(); + } + } + + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ProxiedPlayer) { + send((ProxiedPlayer) player, MusicPacketCodec.pack(pack)); + } + } + + @Override + public Object getPlayer(String player) { + return ProxyServer.getInstance().getPlayer(player); + } + + @Override + public void sendBar(Object player, Component data) { + if (player instanceof ProxiedPlayer) { + AllMusicBungee.adventure.player((ProxiedPlayer) player).sendActionBar(data); + } + } + + @Override + public File getFolder() { + return AllMusicBungee.plugin.getDataFolder(); + } + + @Override + public void sendMessage(Object obj, Component message) { + if (obj instanceof CommandSender) { + AllMusicBungee.adventure.sender((CommandSender) obj).sendMessage(message); + } + } + + @Override + public void runTask(Runnable run) { + ProxyServer.getInstance().getScheduler().runAsync(AllMusicBungee.plugin, run); + } + + @Override + public boolean checkPermission(Object player, String permission) { + if (checkPermission(player)) { + return true; + } + if (player instanceof CommandSender) { + return ((CommandSender) player).hasPermission(permission); + } + return false; + } + + @Override + public boolean checkPermission(Object player) { + return player == ProxyServer.getInstance().getConsole(); + } + + @Override + public boolean isPlayer(Object source) { + return source instanceof ProxiedPlayer; + } + + @Override + public void runTask(Runnable run, int delay) { + ProxyServer.getInstance().getScheduler() + .schedule(AllMusicBungee.plugin, run, delay, TimeUnit.MILLISECONDS); + } + + @Override + public void ping() { + for (Server server : new HashSet<>(TopServers)) { + if (server.isConnected()) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeInt(200); + server.sendData(AllMusic.channelBC, out.toByteArray()); + } else { + TopServers.remove(server); + } + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + MusicPlayEvent event = new MusicPlayEvent(obj); + ProxyServer.getInstance().getPluginManager().callEvent(event); + return event.isCancel(); + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + MusicAddEvent event = new MusicAddEvent(music, (CommandSender) obj); + ProxyServer.getInstance().getPluginManager().callEvent(event); + return event.isCancel(); + } + + @Override + public void updateInfo() { + for (Server server : TopServers) { + if (server.isConnected()) { + sendAllToServer(server); + } else { + TopServers.remove(server); + } + } + } + + @Override + public void updateLyric() { + for (Server server : TopServers) { + if (server.isConnected()) { + sendLyricToServer(server); + } else { + TopServers.remove(server); + } + } + } + + private void send(ProxiedPlayer player, ByteBuf data) { + if (player == null) { + return; + } + runTask(() -> player.sendData(AllMusic.channel, data.array())); + } + + private boolean skip(ProxiedPlayer player) { + String server = player.getServer() == null ? null : player.getServer().getInfo().getName(); + return AllMusic.isSkip(player.getName(), server, false); + } + + @Override + public boolean check(String name, int cost) { + return topEconomy(name, cost, 12); + } + + @Override + public boolean cost(String name, int cost) { + return topEconomy(name, cost, 13); + } + + @Override + public Component miniMessage(String input) { + return MiniMessage.miniMessage().deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + return miniMessage(input).clickEvent(ClickEvent.runCommand(command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + return miniMessage(input).clickEvent(ClickEvent.suggestCommand(command)); + } + + private boolean topEconomy(String name, int cost, int type) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeInt(type); + String uuid; + do { + uuid = UUID.randomUUID().toString(); + } while (SendToBackend.containsKey(uuid)); + + SendToBackend.put(uuid, -1); + String server = AllMusic.getConfig().economy.backend; + Server toServer = null; + for (Server connection : TopServers) { + if (connection.isConnected() && connection.getInfo().getName().equalsIgnoreCase(server)) { + toServer = connection; + break; + } + } + if (toServer == null) { + AllMusic.log.data("[AllMusic]没有找到目标服务器"); + return false; + } + + out.writeUTF(uuid); + out.writeInt(cost); + out.writeUTF(name); + + toServer.sendData(AllMusic.channelBC, out.toByteArray()); + + Integer res; + int count = 0; + do { + try { + res = SendToBackend.get(uuid); + if (res == null) { + return false; + } else if (res == -1) { + Thread.sleep(1); + count++; + } else if (res == 0) { + AllMusic.log.data("[AllMusic]后端经济插件错误"); + SendToBackend.remove(uuid); + return false; + } else if (res == 1) { + SendToBackend.remove(uuid); + return false; + } else if (res == 2) { + SendToBackend.remove(uuid); + return true; + } + } catch (Exception e) { + AllMusic.log.data("[AllMusic]经济数据发送错误"); + e.printStackTrace(); + } + } while (count < 100); + + AllMusic.log.data("[AllMusic]经济数据请求超时"); + return false; + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..886937d8 --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,44 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Event; + +/** + * 音乐添加事件 + */ +public class MusicAddEvent extends Event { + /** + * 添加的音乐 + */ + private final PlayerAddMusicObj music; + /** + * 添加者 + */ + private final CommandSender player; + /** + * 是否取消添加 + */ + private boolean cancel = false; + + public MusicAddEvent(PlayerAddMusicObj id, CommandSender player) { + this.music = id; + this.player = player; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public CommandSender getPlayer() { + return player; + } + + public PlayerAddMusicObj getMusic() { + return music; + } +} diff --git a/server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..42e0487f --- /dev/null +++ b/server/bungeecord/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,34 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.md_5.bungee.api.plugin.Event; + +/** + * 音乐播发事件 + */ +public class MusicPlayEvent extends Event { + /** + * 音乐内容 + */ + private final SongInfoObj music; + /** + * 是否取消 + */ + private boolean cancel = false; + + public MusicPlayEvent(SongInfoObj music) { + this.music = music; + } + + public SongInfoObj getMusic() { + return music; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } +} diff --git a/server/bungeecord/src/main/resources/bungee.yml b/server/bungeecord/src/main/resources/bungee.yml new file mode 100644 index 00000000..fee41aa9 --- /dev/null +++ b/server/bungeecord/src/main/resources/bungee.yml @@ -0,0 +1,4 @@ +name: AllMusic +main: com.coloryr.allmusic.server.AllMusicBungee +author: Color_yr +version: ${version} diff --git a/settings.gradle.kts b/settings.gradle.kts index 92e16a3b..68c4b241 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -55,6 +55,7 @@ include(":server:spigot") include(":server:paper") include(":server:folia") include(":server:paper") +include(":server:bungeecord") include(":server:velocity") include(":onejar") From 447e100765993eaf5979b38fce5e36a1e07c70a4 Mon Sep 17 00:00:00 2001 From: hutuyee Date: Wed, 19 Aug 2026 20:27:51 +0800 Subject: [PATCH 13/46] Fix BungeeCord dependency resolution --- server/bungeecord/build.gradle.kts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/bungeecord/build.gradle.kts b/server/bungeecord/build.gradle.kts index 9408c477..f38808fa 100644 --- a/server/bungeecord/build.gradle.kts +++ b/server/bungeecord/build.gradle.kts @@ -1,6 +1,9 @@ +repositories { + maven("https://libraries.minecraft.net/") +} + dependencies { - // 使用较早且稳定的 API 编译,避免无意间调用新版本 BungeeCord 才提供的接口。 - compileOnly("net.md-5:bungeecord-api:1.16-R0.4") + compileOnly("net.md-5:bungeecord-api:1.21-R0.4") shadowImplementation("net.kyori:adventure-platform-bungeecord:4.4.1") { exclude(group = "net.md-5", module = "bungeecord-api") From 3a9cfb09749fd6f88585fa580535d3bc9b9eaeb8 Mon Sep 17 00:00:00 2001 From: hutuyee Date: Wed, 19 Aug 2026 21:54:00 +0800 Subject: [PATCH 14/46] Fix Forge 1.12.2 HUD initialization --- .../java/com/coloryr/allmusic/client/AllMusicClient.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 770240b8..cc00281f 100644 --- a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -18,6 +18,7 @@ import net.minecraftforge.client.event.sound.PlaySoundEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.event.FMLLoadCompleteEvent; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; @@ -71,6 +72,11 @@ public void preload(final FMLPreInitializationEvent evt) { } } + @Mod.EventHandler + public void loadComplete(final FMLLoadCompleteEvent event) { + AllMusicCore.renderInit(); + } + @SubscribeEvent public void onSound(final PlaySoundEvent e) { if (!AllMusicCore.isPlay()) return; From f3b45378c13e8236715ee990c8bd470318f3dbe3 Mon Sep 17 00:00:00 2001 From: hutuyee Date: Thu, 20 Aug 2026 10:02:16 +0800 Subject: [PATCH 15/46] Reobfuscate Forge 1.12.2 client artifact --- client/forge_1_12_2/build.gradle.kts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/forge_1_12_2/build.gradle.kts b/client/forge_1_12_2/build.gradle.kts index 669c18c4..621262ca 100644 --- a/client/forge_1_12_2/build.gradle.kts +++ b/client/forge_1_12_2/build.gradle.kts @@ -1,3 +1,5 @@ +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar + plugins { id("com.gtnewhorizons.retrofuturagradle") version "2.0.2" } @@ -22,12 +24,16 @@ tasks { shadowJar { archiveFileName.set("[forge-1.12.2]AllMusic_Client-${project.version}.jar") + } + + val reobfShadowJar = named("reobfShadowJar") { + archiveFileName.set("[forge-1.12.2]AllMusic_Client-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) } configurations.shadow.get().setExtendsFrom(listOf(configurations.named("shadowImplementation").get())) build { - dependsOn(shadowJar) + dependsOn(reobfShadowJar) } } From 7b0164168120663385895528fbaa02ae8984cbc0 Mon Sep 17 00:00:00 2001 From: hutuyee Date: Thu, 20 Aug 2026 11:34:42 +0800 Subject: [PATCH 16/46] Fix Forge 1.12.2 rotating album texture --- .../src/main/java/com/coloryr/allmusic/client/PicRender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/PicRender.java index 4df455cc..d21013ff 100644 --- a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/PicRender.java +++ b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/PicRender.java @@ -32,7 +32,7 @@ public void update(byte[] source, byte[] rotate) { image.getRGB(0, 0, image.getWidth(), image.getHeight(), sourceTexture.getTextureData(), 0, image.getWidth()); sourceTexture.updateDynamicTexture(); - stream = new ByteArrayInputStream(source); + stream = new ByteArrayInputStream(rotate); image = ImageIO.read(stream); image.getRGB(0, 0, image.getWidth(), image.getHeight(), rotateTexture.getTextureData(), 0, image.getWidth()); rotateTexture.updateDynamicTexture(); From 6b4fa5bd6369ac026f2d384e2fbe87f2ebbd7a3b Mon Sep 17 00:00:00 2001 From: hutuyee Date: Thu, 20 Aug 2026 12:29:38 +0800 Subject: [PATCH 17/46] Fix Forge 1.12.2 scaled texture UV --- .../src/main/java/com/coloryr/allmusic/client/TexRender.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/TexRender.java index a1a92078..ff47427b 100644 --- a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/TexRender.java +++ b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/TexRender.java @@ -138,7 +138,7 @@ public void drawPic(float x, float y, float width, float height, HudPosType dir, float y1 = h1; float z = 0; float u0 = 0; - float u1 = width; + float u1 = 1; float v0 = 0; float v1 = 1; From 04d1ed54be13a57e5a1f5403f149c09873fe5c16 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sun, 23 Aug 2026 09:48:55 +0800 Subject: [PATCH 18/46] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/forge_1_7_10/build.gradle.kts | 8 +++++++- server/forge_1_12_2/build.gradle.kts | 12 ++++++++---- server/forge_1_7_10/build.gradle.kts | 11 ++++++++--- settings.gradle.kts | 2 ++ 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/client/forge_1_7_10/build.gradle.kts b/client/forge_1_7_10/build.gradle.kts index 0c9269ba..3651d035 100644 --- a/client/forge_1_7_10/build.gradle.kts +++ b/client/forge_1_7_10/build.gradle.kts @@ -1,3 +1,5 @@ +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar + plugins { id("com.gtnewhorizons.retrofuturagradle") version "2.0.2" } @@ -27,12 +29,16 @@ tasks { shadowJar { archiveFileName.set("[forge-1.7.10]AllMusic_Client-${project.version}.jar") + } + + val reobfShadowJar = named("reobfShadowJar") { + archiveFileName.set("[forge-1.7.10]AllMusic_Client-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) } configurations.shadow.get().setExtendsFrom(listOf(configurations.named("shadowImplementation").get())) build { - dependsOn(shadowJar) + dependsOn(reobfShadowJar) } } diff --git a/server/forge_1_12_2/build.gradle.kts b/server/forge_1_12_2/build.gradle.kts index f139cca2..bb233b12 100644 --- a/server/forge_1_12_2/build.gradle.kts +++ b/server/forge_1_12_2/build.gradle.kts @@ -1,3 +1,5 @@ +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar + plugins { id("com.gtnewhorizons.retrofuturagradle") version "2.0.2" } @@ -31,15 +33,17 @@ tasks { shadowJar { archiveFileName.set("[forge-1.12.2]AllMusic_Server-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") -// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") + } + + val reobfShadowJar = named("reobfShadowJar") { + archiveFileName.set("[forge-1.12.2]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) } configurations.shadow.get().setExtendsFrom(listOf(configurations.named("shadowImplementation").get())) build { - dependsOn(shadowJar) + dependsOn(reobfShadowJar) } } diff --git a/server/forge_1_7_10/build.gradle.kts b/server/forge_1_7_10/build.gradle.kts index c06f1d50..f006510c 100644 --- a/server/forge_1_7_10/build.gradle.kts +++ b/server/forge_1_7_10/build.gradle.kts @@ -1,3 +1,5 @@ +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar + plugins { id("com.gtnewhorizons.retrofuturagradle") version "2.0.2" } @@ -31,15 +33,18 @@ tasks { shadowJar { archiveFileName.set("[forge-1.7.10]AllMusic_Server-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") -// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") + } + + val reobfShadowJar = named("reobfShadowJar") { + archiveFileName.set("[forge-1.7.10]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) } configurations.shadow.get().setExtendsFrom(listOf(configurations.named("shadowImplementation").get())) build { - dependsOn(shadowJar) + dependsOn(reobfShadowJar) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 68c4b241..c1a4fd26 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -63,11 +63,13 @@ include(":onejar") include(":onejar:fabric_1_16_5") include(":onejar:fabric_1_20_1") include(":onejar:fabric_1_21") +include(":onejar:fabric_1_21_4") include(":onejar:fabric_1_21_6") include(":onejar:fabric_1_21_11") include(":onejar:fabric_26_1") include(":onejar:fabric_26_2") include(":onejar:neoforge_1_21") +include(":onejar:neoforge_1_21_4") include(":onejar:neoforge_1_21_6") include(":onejar:neoforge_1_21_11") include(":onejar:neoforge_26_1") From 45d1d13c64ae76dbddd249f422ee42b0ce9520b6 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 28 Aug 2026 18:49:32 +0800 Subject: [PATCH 19/46] 4.1.9 --- build.gradle.kts | 2 +- .../java/com/coloryr/allmusic/client/core/AllMusicPlayer.java | 3 --- server/fabric_26_1/build.gradle.kts | 3 --- server/fabric_26_2/build.gradle.kts | 3 --- .../coloryr/allmusic/server/core/objs/config/ConfigObj.java | 4 ---- .../coloryr/allmusic/server/core/objs/config/EconomyObj.java | 3 --- 6 files changed, 1 insertion(+), 17 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a691a8e9..3dadd9fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.1.8" + version = "4.1.9" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 237e4a94..511b8825 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -141,9 +141,6 @@ private void checkVolume() { if (isChat) { temp *= 0.5F; } - if (temp < 0.05F) { - temp = 0.05F; - } if (now != temp) { AL10.alSourcef(index, AL10.AL_GAIN, temp); } diff --git a/server/fabric_26_1/build.gradle.kts b/server/fabric_26_1/build.gradle.kts index d9659b92..b4025f2f 100644 --- a/server/fabric_26_1/build.gradle.kts +++ b/server/fabric_26_1/build.gradle.kts @@ -26,9 +26,6 @@ tasks { shadowJar { archiveFileName.set("[fabric-26.1]AllMusic_Server-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) - -// relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") -// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") } build { diff --git a/server/fabric_26_2/build.gradle.kts b/server/fabric_26_2/build.gradle.kts index a123671c..2e44cb77 100644 --- a/server/fabric_26_2/build.gradle.kts +++ b/server/fabric_26_2/build.gradle.kts @@ -26,9 +26,6 @@ tasks { shadowJar { archiveFileName.set("[fabric-26.2]AllMusic_Server-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) - -// relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") -// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") } build { diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java index 801f3eb8..774fbd0b 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java @@ -83,10 +83,6 @@ public class ConfigObj { * K歌歌词 */ public boolean ktvMode; - /** - * 歌曲音质 - */ - public String musicBR; /** * 配置文件版本号 */ diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java index 98c0b7c6..138acc9b 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java @@ -1,7 +1,6 @@ package com.coloryr.allmusic.server.core.objs.config; public class EconomyObj { - // public String mysqlUrl; public String backend; public boolean vault; @@ -13,14 +12,12 @@ public static EconomyObj make() { } public boolean check() { -// boolean res = mysqlUrl == null; return false; } public void init() { backend = "Server1"; -// mysqlUrl = "jdbc:mysql://localhost:3306/minecraft?autoReconnect=true&autoReconnectForPools=true"; vault = true; } } From 126a32ab608ec9b50178902834c8022953154146 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 28 Aug 2026 20:55:43 +0800 Subject: [PATCH 20/46] =?UTF-8?q?=E6=8A=95=E7=A5=A8=E5=BA=8F=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 +- .../allmusic/server/core/AllMusic.java | 4 +- .../server/core/command/CommandEX.java | 3 +- .../server/core/command/sub/CommandAgree.java | 40 ++++ .../server/core/command/sub/CommandBan.java | 4 +- .../server/core/command/sub/CommandHelp.java | 2 + .../server/core/command/sub/CommandPush.java | 64 ++---- .../server/core/command/sub/CommandVote.java | 45 ++-- .../allmusic/server/core/music/PlayMusic.java | 216 +++++++++--------- .../server/core/music/PlayRuntime.java | 132 ++++++----- .../allmusic/server/core/music/VoteItem.java | 40 ++++ .../server/core/objs/config/ConfigObj.java | 29 +-- .../server/core/objs/config/LimitObj.java | 10 + .../server/core/objs/config/VoteObj.java | 24 ++ .../core/objs/message/HelpNormalObj.java | 5 + .../server/core/objs/message/PushObj.java | 47 ++-- .../server/core/objs/message/VoteObj.java | 43 ++-- .../server/core/objs/music/SongInfoObj.java | 2 +- 18 files changed, 404 insertions(+), 308 deletions(-) create mode 100644 server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java create mode 100644 server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java create mode 100644 server/src/main/java/com/coloryr/allmusic/server/core/objs/config/VoteObj.java diff --git a/build.gradle.kts b/build.gradle.kts index 3dadd9fa..720fb28b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.1.9" + version = "4.2.0" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java index eced95b0..5b1108fe 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java @@ -55,11 +55,11 @@ public class AllMusic { /** * 配置文件版本号 */ - public static final String configVersion = "401"; + public static final String configVersion = "402"; /** * 语言文件配置版本号 */ - public static final String messageVersion = "400"; + public static final String messageVersion = "401"; /** * 日志 */ diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java index 75355ad7..dccd8096 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java @@ -44,6 +44,7 @@ public class CommandEX { commandList.put("push", new CommandPush()); commandList.put("join", new CommandJoin()); commandList.put("cancel", new CommandCancel()); + commandList.put("agree", new CommandAgree()); commandAdminList.put("reload", new CommandReload()); commandAdminList.put("next", new CommandNext()); @@ -197,7 +198,7 @@ public static void addMusic(Object sender, String name, String api, String arg) musicID = api1.getMusicId(arg); if (api1.checkId(musicID)) { - if (PlayMusic.getListSize() >= AllMusic.getConfig().maxPlayList) { + if (PlayMusic.getListSize() >= AllMusic.getConfig().limit.maxPlayerList) { AllMusic.side.sendMessageTask(sender, AllMusic.getMessage().addMusic.listFull); } else if (BanSave.checkBanMusic(musicID, api)) { AllMusic.side.sendMessageTask(sender, AllMusic.getMessage().addMusic.banMusic); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java new file mode 100644 index 00000000..cf8a8ff8 --- /dev/null +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java @@ -0,0 +1,40 @@ +package com.coloryr.allmusic.server.core.command.sub; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.command.ACommand; +import com.coloryr.allmusic.server.core.command.PermissionList; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import com.coloryr.allmusic.server.core.music.PlayRuntime; +import com.coloryr.allmusic.server.core.music.VoteItem; +import com.coloryr.allmusic.server.core.objs.message.ARG; + +public class CommandAgree extends ACommand { + @Override + public void execute(Object sender, String name, String[] args) { + if (AllMusic.getConfig().needPermission && + !AllMusic.side.checkPermission(sender, PermissionList.PERMISSION_VOTE)) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.noPermission); + return; + } + + VoteItem vote = PlayMusic.getVote(); + + if (vote == null) { + AllMusic.side.sendMessage(name, AllMusic.getMessage().vote.err4); + return; + } + if (vote.votePlayer.contains(name)) { + AllMusic.side.sendMessage(name, AllMusic.getMessage().vote.err5); + return; + } + + AllMusic.side.sendMessage(name, AllMusic.getMessage().vote.agree); + String data = AllMusic.getMessage().vote.bqAgree; + data = data.replace(ARG.player, name) + .replace(ARG.count, String.valueOf(vote.votePlayer.size())) + .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); + AllMusic.side.broadcast(data); + + PlayMusic.addVote(name); + } +} diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandBan.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandBan.java index 46a6c628..795bdb1f 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandBan.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandBan.java @@ -50,10 +50,10 @@ public List tab(Object player, String name, String[] args, int index) { if (args.length == index || (args.length == index + 1)) { List list = new ArrayList<>(); if (PlayMusic.nowPlayMusic != null) { - list.add(PlayMusic.nowPlayMusic.getID()); + list.add(PlayMusic.nowPlayMusic.getId()); } for (SongInfoObj item : PlayMusic.getList()) { - list.add(item.getID()); + list.add(item.getId()); } return list; diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandHelp.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandHelp.java index c81cfbfb..83f66169 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandHelp.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandHelp.java @@ -24,6 +24,8 @@ public void execute(Object sender, String name, String[] args) { .append(AllMusic.side.miniMessageSuggest(AllMusic.getMessage().click.clickRun, "/music push "))); AllMusic.side.sendMessage(sender, AllMusic.side.miniMessage(AllMusic.getMessage().help.normal.push1) .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().click.clickRun, "/music push cancel"))); + AllMusic.side.sendMessage(sender, AllMusic.side.miniMessage(AllMusic.getMessage().help.normal.vote) + .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().click.clickRun, "/music vote"))); AllMusic.side.sendMessage(sender, AllMusic.side.miniMessage(AllMusic.getMessage().help.normal.mute) .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().click.clickRun, "/music mute"))); AllMusic.side.sendMessage(sender, AllMusic.side.miniMessage(AllMusic.getMessage().help.normal.mutelist) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java index ddac4869..b62f40e8 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java @@ -4,7 +4,7 @@ import com.coloryr.allmusic.server.core.command.ACommand; import com.coloryr.allmusic.server.core.command.PermissionList; import com.coloryr.allmusic.server.core.music.PlayMusic; -import com.coloryr.allmusic.server.core.music.PlayRuntime; +import com.coloryr.allmusic.server.core.music.VoteItem; import com.coloryr.allmusic.server.core.objs.message.ARG; import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; import com.coloryr.allmusic.server.core.saves.BanSave; @@ -32,22 +32,27 @@ public void execute(Object sender, String name, String[] args) { return; } SongInfoObj id1 = PlayMusic.findMusicIndex(1); - if (id1 != null && id1.getID().equalsIgnoreCase(music.getID())) { + if (id1 != null && id1.getId().equalsIgnoreCase(music.getId())) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.pushErr); return; } + + VoteItem item = new VoteItem(music.getApi(), music.getId(), name, VoteItem.VoteType.PUSH); + if (PlayMusic.startVote(item)) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); + } else { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err3); + } + + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); } else if (args.length == 2) { if (args[1].equalsIgnoreCase("cancel")) { - if (!name.equalsIgnoreCase(PlayMusic.getPushSender())) { + if (!PlayMusic.haveVote(name, VoteItem.VoteType.PUSH)) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err1); return; } - if (PlayMusic.getPushTime() <= 0) { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err2); - return; - } - PlayMusic.clearPush(); - AllMusic.side.broadcast(AllMusic.getMessage().push.cancel); + PlayMusic.removeVote(name, VoteItem.VoteType.PUSH); + AllMusic.side.sendMessage(name, AllMusic.getMessage().push.cancel1); return; } else { try { @@ -61,43 +66,20 @@ public void execute(Object sender, String name, String[] args) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.noId1.replace(ARG.index, args[1])); return; } + + VoteItem item = new VoteItem(music.getApi(), music.getId(), name, VoteItem.VoteType.PUSH); + if (PlayMusic.startVote(item)) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); + } else { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err3); + } + + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); } } else if (args.length > 2) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().command.error); return; } - if (PlayMusic.getPushTime() <= 0) { - if (music == null) { - return; - } - PlayMusic.startPush(name, music); - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); - String data = AllMusic.getMessage().push.bq; - data = data.replace(ARG.player, name) - .replace(ARG.time, String.valueOf(AllMusic.getConfig().voteTime)) - .replace(ARG.musicName, music.getName()) - .replace(ARG.musicAuthor, music.getAuthor()) - .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); - AllMusic.side.broadcast(data); - AllMusic.side.broadcast(AllMusic.side.miniMessage(AllMusic.getMessage().push.bq1) - .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().push.bq2, "/music push"))); - } else { - if (music != null) { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err3); - return; - } - if (!PlayMusic.containPush(name)) { - PlayMusic.addPush(name); - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.agree); - String data = AllMusic.getMessage().push.bqAgree; - data = data.replace(ARG.player, name) - .replace(ARG.count, String.valueOf(PlayMusic.getVoteCount())) - .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); - AllMusic.side.broadcast(data); - } else { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.arAgree); - } - } BanSave.removeMutePlayer(name); } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java index e3da747f..eef944d1 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java @@ -4,10 +4,11 @@ import com.coloryr.allmusic.server.core.command.ACommand; import com.coloryr.allmusic.server.core.command.PermissionList; import com.coloryr.allmusic.server.core.music.PlayMusic; -import com.coloryr.allmusic.server.core.music.PlayRuntime; -import com.coloryr.allmusic.server.core.objs.message.ARG; +import com.coloryr.allmusic.server.core.music.VoteItem; import com.coloryr.allmusic.server.core.saves.BanSave; +import java.util.Locale; + public class CommandVote extends ACommand { @Override public void execute(Object sender, String name, String[] args) { @@ -21,15 +22,17 @@ public void execute(Object sender, String name, String[] args) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().musicPlay.emptyPlayingMusic); } else if (args.length == 2) { if (args[1].equalsIgnoreCase("cancel")) { - if (!name.equalsIgnoreCase(PlayMusic.getVoteSender())) { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err1); + VoteItem vote = PlayMusic.getVote(); + if (vote == null) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err4); return; - } else if (PlayMusic.getVoteTime() <= 0) { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err2); + } + if (!PlayMusic.haveVote(name, VoteItem.VoteType.NEXT)) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err1); return; } - AllMusic.side.broadcast(AllMusic.getMessage().vote.cancel); - PlayMusic.clearVote(); + PlayMusic.removeVote(name, VoteItem.VoteType.NEXT); + AllMusic.side.sendMessage(name, AllMusic.getMessage().push.cancel1); } else { AllMusic.side.sendMessage(sender, AllMusic.getMessage().command.error); } @@ -37,27 +40,15 @@ public void execute(Object sender, String name, String[] args) { } else if (args.length > 2) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().command.error); return; - } else if (PlayMusic.getVoteTime() <= 0) { - PlayMusic.startVote(name); - AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.doVote); - String data = AllMusic.getMessage().vote.bq; - data = data.replace(ARG.player, name) - .replace(ARG.time, String.valueOf(AllMusic.getConfig().voteTime)) - .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); - AllMusic.side.broadcast(data); - AllMusic.side.broadcast(AllMusic.side.miniMessage(AllMusic.getMessage().vote.bq1) - .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().vote.bq2, "/music vote"))); } else { - if (!PlayMusic.containVote(name)) { - PlayMusic.addVote(name); - AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.agree); - String data = AllMusic.getMessage().vote.bqAgree; - data = data.replace(ARG.player, name) - .replace(ARG.count, String.valueOf(PlayMusic.getVoteCount())) - .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); - AllMusic.side.broadcast(data); + String player = name.toLowerCase(Locale.ROOT); + VoteItem item = new VoteItem(PlayMusic.nowPlayMusic.getApi(), PlayMusic.nowPlayMusic.getId(), player, VoteItem.VoteType.NEXT); + item.votePlayer.add(player); + + if (PlayMusic.startVote(item)) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.doVote); } else { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.arAgree); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err3); } } BanSave.removeMutePlayer(name); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java index dd62dfde..cf075b39 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java @@ -26,13 +26,14 @@ public class PlayMusic { */ private static final Set nowPlayPlayer = new HashSet<>(); /** - * 切歌投票的玩家 + * 投票序列 */ - private static final Set votePlayer = new HashSet<>(); + private static final Queue voteList = new ConcurrentLinkedQueue<>(); /** - * 插歌投票的玩家 + * 当前投票 */ - private static final Set pushPlayer = new HashSet<>(); + private static VoteItem vote; + /** * 总歌曲长度 */ @@ -65,22 +66,6 @@ public class PlayMusic { * 切歌投票时间 */ private static int voteTime = 0; - /** - * 切歌发起人 - */ - private static String voteSender; - /** - * 插歌投票时间 - */ - private static int pushTime = 0; - /** - * 插歌发起人 - */ - private static String pushSender; - /** - * 插歌目标 - */ - private static SongInfoObj push; /** * 空闲列表取出的歌曲序号 */ @@ -93,115 +78,140 @@ public static void start() { new Thread(PlayMusic::task, "allmusic_task").start(); } + public static boolean haveVote(String name, VoteItem.VoteType voteType) { + name = name.toLowerCase(Locale.ROOT); + for (VoteItem item : voteList) { + if (item.getType() == voteType && item.getVoteSender().equalsIgnoreCase(name)) { + return true; + } + } + + return false; + } + /** - * 添加投票的玩家 + * 通过投票 * * @param player 用户名 */ public static void addVote(String player) { - player = player.toLowerCase(); - votePlayer.add(player); - } + player = player.toLowerCase(Locale.ROOT); + if (vote == null) { + return; + } - public static void startVote(String player) { - player = player.toLowerCase(); - voteSender = player; - votePlayer.add(player); - voteTime = AllMusic.getConfig().voteTime; + vote.votePlayer.add(player); } /** - * 添加投票的玩家 + * 发起投票 * - * @param player 用户名 - */ - public static void addPush(String player) { - player = player.toLowerCase(); - pushPlayer.add(player); - } + * @param vote 投票内容 + */ + public static boolean startVote(VoteItem vote) { + String id = vote.getId(); + String api = vote.getApi(); + for (VoteItem item : voteList) { + if (item.getId().equalsIgnoreCase(id) && item.getApi().equalsIgnoreCase(api)) { + return false; + } + } - public static void startPush(String player, SongInfoObj music) { - player = player.toLowerCase(); - push = music; - pushSender = player; - pushPlayer.add(player); - pushTime = AllMusic.getConfig().voteTime; - } + voteList.add(vote); + voteTime = AllMusic.getConfig().vote.voteTime; - public static void pushTick() { - pushTime--; + return true; } - public static void voteTick() { - voteTime--; - } + public static SongInfoObj getMusic(String id, String api) { + for (SongInfoObj item : playList) { + if (item.getId().equalsIgnoreCase(id) && item.getApi().equalsIgnoreCase(api)) { + return item; + } + } - public static SongInfoObj getPush() { - return push; + return null; } - public static int getPushTime() { - return pushTime; + public static void doVote() { + if (vote == null) { + return; + } + + if (vote.getType() == VoteItem.VoteType.NEXT) { + if (nowPlayMusic != null && nowPlayMusic.getApi() + .equalsIgnoreCase(vote.getApi()) && nowPlayMusic.getId().equalsIgnoreCase(vote.getId())) { + musicLessTime = 0; + AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.next); + } + } else { + SongInfoObj obj = getMusic(vote.getId(), vote.getApi()); + if (obj != null) { + synchronized (playList) { + playList.remove(obj); + playList.add(0, obj); + } + AllMusic.side.broadcastInTask(AllMusic.getMessage().push.doPush); + } + } + + vote = null; } - public static String getPushSender() { - return pushSender; + public static void voteTick() { + voteTime--; } public static int getVoteTime() { return voteTime; } - public static String getVoteSender() { - return voteSender; + public static VoteItem getVote() { + return vote; } - /** - * 获取投票数量 - * - * @return 数量 - */ public static int getVoteCount() { - return votePlayer.size(); + return voteList.size(); } - public static int getPushCount() { - return pushPlayer.size(); - } + public static void removeVote(String name, VoteItem.VoteType voteType) { + if (vote != null) { + if (vote.getVoteSender().equalsIgnoreCase(name) && vote.getType() == voteType) { + removeVote(); + if (voteType == VoteItem.VoteType.NEXT) { + AllMusic.side.broadcast(AllMusic.getMessage().push.cancel); + } + else { + AllMusic.side.broadcast(AllMusic.getMessage().vote.cancel); + } + return; + } + } - /** - * 清空投票 - */ - public static void clearVote() { - voteTime = -1; - voteSender = null; - votePlayer.clear(); + VoteItem item1 = null; + for (VoteItem item : voteList) { + if (item.getType() == voteType && item.getVoteSender().equalsIgnoreCase(name)) { + item1 = item; + break; + } + } + + if (item1 != null) { + voteList.remove(item1); + } } - /** - * 清空插歌 - */ - public static void clearPush() { - pushTime = -1; - push = null; - pushSender = null; - pushPlayer.clear(); + public static void removeVote() { + vote = null; } /** - * 是否已经投票了 - * - * @param player 用户名 - * @return 结果 + * 下一个投票 + * @return 投票 */ - public static boolean containVote(String player) { - player = player.toLowerCase(); - return votePlayer.contains(player); - } - - public static boolean containPush(String player) { - player = player.toLowerCase(); - return pushPlayer.contains(player); + public static VoteItem nextVote() { + vote = voteList.poll(); + return vote; } /** @@ -231,11 +241,8 @@ private static void task() { } } nowPlayPlayer.clear(); - votePlayer.clear(); - pushPlayer.clear(); playList.clear(); - clearVote(); - clearPush(); + voteList.clear(); AllMusic.log.data("歌曲处理线程关闭"); } @@ -310,17 +317,6 @@ public static void addMusic(Object sender, String id, IMusicApi api, String play } } - /** - * 将歌曲移动到队列头 - */ - public static void pushMusic() { - SongInfoObj obj = push; - synchronized (playList) { - playList.remove(obj); - playList.add(0, obj); - } - } - /** * 获取播放列表长度 * @@ -420,11 +416,11 @@ public static boolean haveMusic(MusicObj music) { * @return 是否在列表种 */ public static boolean haveMusic(String id, String api) { - if (nowPlayMusic != null && nowPlayMusic.getID().equalsIgnoreCase(id) + if (nowPlayMusic != null && nowPlayMusic.getId().equalsIgnoreCase(id) && Objects.equals(nowPlayMusic.getApi(), api)) return true; for (SongInfoObj item : playList) { - if (item.getID().equalsIgnoreCase(id) && Objects.equals(item.getApi(), api)) { + if (item.getId().equalsIgnoreCase(id) && Objects.equals(item.getApi(), api)) { return true; } } @@ -438,7 +434,7 @@ public static boolean haveMusic(String id, String api) { * @return 是否超过上限 */ public static boolean isPlayerMax(String name) { - int list = AllMusic.getConfig().maxPlayerList; + int list = AllMusic.getConfig().limit.maxPlayerList; if (list == 0) { return false; } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java index 365707ea..c7fb9c75 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java @@ -7,7 +7,6 @@ import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; import com.coloryr.allmusic.server.core.utils.HudUtils; -import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -32,7 +31,7 @@ public class PlayRuntime { * 启动歌曲工作 */ public static void start() { - new Thread(PlayRuntime::musicPlayTask, "allMusic_play").start(); + new Thread(PlayRuntime::musicPlayTask, "allmusic_play").start(); service = Executors.newSingleThreadScheduledExecutor(); service.scheduleAtFixedRate(PlayRuntime::time1, 0, 10, TimeUnit.MILLISECONDS); @@ -60,6 +59,15 @@ public static void stop() { */ private static void clear() { isPlay = false; + + VoteItem vote = PlayMusic.getVote(); + if (vote != null && vote.getType() == VoteItem.VoteType.NEXT + && vote.getApi().equalsIgnoreCase(PlayMusic.nowPlayMusic.getApi()) + && vote.getId().equalsIgnoreCase(PlayMusic.nowPlayMusic.getId())) { + AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.cancel1); + PlayMusic.removeVote(); + } + PlayMusic.musicNowTime = 0; PlayMusic.musicAllTime = 0; PlayMusic.musicLessTime = 0; @@ -108,32 +116,31 @@ private static void time1() { } } - private static boolean checkPush() { - SongInfoObj music = PlayMusic.getPush(); - if (music != null) { - if (PlayMusic.nowPlayMusic.getID().equalsIgnoreCase(music.getID())) { - return false; - } - List list = PlayMusic.getList(); - if (list.isEmpty()) { - return false; - } - SongInfoObj id1 = list.get(0); - if (id1 != null && id1.getID().equalsIgnoreCase(music.getID())) { - return false; - } - for (int a = 1; a < list.size(); a++) { - id1 = list.get(a); - if (id1.getID().equalsIgnoreCase(music.getID())) - return true; - } - } + public static int getMiniVote() { + return Math.min(AllMusic.getConfig().vote.minVote, AllMusic.side.getPlayers().size()); + } - return false; + public static boolean checkMusic(String id, String api) { + if (PlayMusic.nowPlayMusic.getId().equalsIgnoreCase(id) + && PlayMusic.nowPlayMusic.getApi().equalsIgnoreCase(api)) { + return true; + } + return PlayMusic.getMusic(id, api) != null; } - public static int getMiniVote() { - return Math.min(AllMusic.getConfig().minVote, AllMusic.side.getPlayers().size()); + private static void sendVoteInfo(boolean timeout) { + if (timeout) { + VoteItem vote = PlayMusic.getVote(); + AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.timeOut + .replace(ARG.count, String.valueOf(vote.votePlayer.size())) + .replace(ARG.countAll, String.valueOf(getMiniVote()))); + } + + int count = PlayMusic.getVoteCount(); + if (count > 0) { + AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.list + .replace(ARG.count, String.valueOf(count))); + } } /** @@ -145,35 +152,46 @@ private static void time3() { if (ping >= 10) { AllMusic.side.ping(); } - if (PlayMusic.getPushTime() > 0) { - if (!checkPush()) { - PlayMusic.clearPush(); - AllMusic.side.broadcastInTask(AllMusic.getMessage().push.cancel); - } else { - PlayMusic.pushTick(); - if (PlayMusic.getPushTime() == 0) { - PlayMusic.clearPush(); - AllMusic.side.broadcastInTask(AllMusic.getMessage().push.timeOut); - } else { - if (PlayMusic.getPushCount() >= getMiniVote()) { - PlayMusic.pushMusic(); - PlayMusic.clearPush(); - AllMusic.side.broadcastInTask(AllMusic.getMessage().push.doPush); - } - } - } - } - if (PlayMusic.getVoteTime() > 0) { + VoteItem vote = PlayMusic.getVote(); + if (vote != null) { PlayMusic.voteTick(); - if (PlayMusic.getVoteTime() == 0) { - PlayMusic.clearVote(); - AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.timeOut); + if (PlayMusic.getVoteTime() <= 0) { + sendVoteInfo(true); + PlayMusic.removeVote(); } else { - if (PlayMusic.getVoteCount() >= getMiniVote()) { - PlayMusic.musicLessTime = 0; - PlayMusic.clearVote(); - AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.voteDone); + if (PlayMusic.getVote().votePlayer.size() >= getMiniVote()) { + sendVoteInfo(false); + PlayMusic.doVote(); + } + } + } else { + vote = PlayMusic.nextVote(); + if (vote != null) { + if (vote.getType() == VoteItem.VoteType.NEXT) { + String data = AllMusic.getMessage().vote.bq; + data = data.replace(ARG.player, vote.getVoteSender()) + .replace(ARG.time, String.valueOf(AllMusic.getConfig().vote.voteTime)) + .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); + AllMusic.side.broadcast(data); + AllMusic.side.broadcast(AllMusic.side.miniMessage(AllMusic.getMessage().vote.bq1) + .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().vote.bq2, "/music agree"))); + } else if (vote.getType() == VoteItem.VoteType.PUSH) { + SongInfoObj music = PlayMusic.getMusic(vote.getId(), vote.getApi()); + if (music == null) { + AllMusic.side.broadcast(AllMusic.getMessage().push.err4); + PlayMusic.removeVote(); + return; + } + String data = AllMusic.getMessage().push.bq; + data = data.replace(ARG.player, vote.getVoteSender()) + .replace(ARG.time, String.valueOf(AllMusic.getConfig().vote.voteTime)) + .replace(ARG.musicName, music.getName()) + .replace(ARG.musicAuthor, music.getAuthor()) + .replace(ARG.countAll, String.valueOf(PlayRuntime.getMiniVote())); + AllMusic.side.broadcast(data); + AllMusic.side.broadcast(AllMusic.side.miniMessage(AllMusic.getMessage().push.bq1) + .append(AllMusic.side.miniMessageRun(AllMusic.getMessage().push.bq2, "/music agree"))); } } } @@ -187,8 +205,9 @@ private static void musicPlayTask() { while (AllMusic.isRun) { try { if (PlayMusic.getListSize() == 0) { + Thread.sleep(1000); if (PlayMusic.error >= 10) { - Thread.sleep(1000); + Thread.sleep(10000); } else if (AllMusic.side.needPlay(true) && PlayMusic.getIdleListSize() > 0) { MusicObj music = PlayMusic.getIdleMusic(); if (music != null) { @@ -198,7 +217,6 @@ private static void musicPlayTask() { } } } - Thread.sleep(1000); } else { HudUtils.sendClearHud(); HudUtils.sendHudNowData(); @@ -214,17 +232,17 @@ private static void musicPlayTask() { IMusicApi api = AllMusic.MUSIC_APIS.get(PlayMusic.nowPlayMusic.getApi()); PlayMusic.url = PlayMusic.nowPlayMusic.getPlayerUrl() == null ? - api.getPlayUrl(PlayMusic.nowPlayMusic.getID()) : + api.getPlayUrl(PlayMusic.nowPlayMusic.getId()) : PlayMusic.nowPlayMusic.getPlayerUrl(); if (PlayMusic.url == null) { String data = AllMusic.getMessage().musicPlay.emptyCanPlay; - AllMusic.side.broadcastInTask(data.replace(ARG.musicId, PlayMusic.nowPlayMusic.getID())); + AllMusic.side.broadcastInTask(data.replace(ARG.musicId, PlayMusic.nowPlayMusic.getId())); PlayMusic.nowPlayMusic = null; continue; } if (PlayMusic.nowPlayMusic.getPlayerUrl() == null) - PlayMusic.lyric = api.getLyric(PlayMusic.nowPlayMusic.getID()); + PlayMusic.lyric = api.getLyric(PlayMusic.nowPlayMusic.getId()); else PlayMusic.lyric = new LyricSave(); @@ -274,7 +292,7 @@ private static void musicPlayTask() { AllMusic.side.sendStop(); } else { String data = AllMusic.getMessage().musicPlay.emptyCanPlay; - AllMusic.side.broadcastInTask(data.replace(ARG.musicId, PlayMusic.nowPlayMusic.getID())); + AllMusic.side.broadcastInTask(data.replace(ARG.musicId, PlayMusic.nowPlayMusic.getId())); } clear(); } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java new file mode 100644 index 00000000..7cc2d7d5 --- /dev/null +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java @@ -0,0 +1,40 @@ +package com.coloryr.allmusic.server.core.music; + +import java.util.HashSet; +import java.util.Set; + +public class VoteItem { + public enum VoteType { + NEXT, PUSH + } + + public final Set votePlayer = new HashSet<>(); + + private final String api; + private final String id; + private final VoteType type; + private final String voteSender; + + public String getVoteSender() { + return voteSender; + } + + public VoteType getType() { + return type; + } + + public String getApi() { + return api; + } + + public String getId() { + return id; + } + + public VoteItem(String api, String id, String voteSender, VoteType type) { + this.api = api; + this.id = id; + this.type = type; + this.voteSender = voteSender; + } +} diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java index 774fbd0b..11696d9e 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java @@ -11,22 +11,6 @@ * 配置文件对象 */ public class ConfigObj { - /** - * 最大歌曲数 - */ - public int maxPlayList; - /** - * 一个玩家最大可点数量 - */ - public int maxPlayerList; - /** - * 最小通过投票数 - */ - public int minVote; - /** - * 投票时间 - */ - public int voteTime; /** * 歌曲延迟 */ @@ -103,6 +87,10 @@ public class ConfigObj { * 花费点歌 */ public CostObj cost; + /** + * 投票配置 + */ + public VoteObj vote; /** * 默认音乐API */ @@ -153,6 +141,10 @@ public boolean check() { saveConfig = true; cost = CostObj.make(); } + if (vote == null) { + saveConfig = true; + vote = VoteObj.make(); + } if (lyricReplace == null) { saveConfig = true; lyricReplace = new HashMap<>(); @@ -162,11 +154,7 @@ public boolean check() { } public void init() { - maxPlayerList = 0; fixSongTime = 0; - maxPlayList = 10; - minVote = 3; - voteTime = 30; adminList = new HashSet<>(); adminList.add("color_yr"); playListSwitch = true; @@ -184,6 +172,7 @@ public void init() { economy = EconomyObj.make(); funConfig = FunConfigObj.make(); cost = CostObj.make(); + vote = VoteObj.make(); ktvMode = true; ktvLyricDelay = 0; lyricDelay = 0; diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/LimitObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/LimitObj.java index 4fd03745..80fac498 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/LimitObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/LimitObj.java @@ -37,6 +37,14 @@ public class LimitObj { * 长度替换符号 */ public String limitText; + /** + * 最大歌曲数 + */ + public int maxPlayList; + /** + * 一个玩家最大可点数量 + */ + public int maxPlayerList; public static LimitObj make() { LimitObj obj = new LimitObj(); @@ -48,6 +56,8 @@ public static LimitObj make() { obj.listLimitSize = 10; obj.messageLimit = false; obj.messageLimitSize = 20; + obj.maxPlayList = 10; + obj.maxPlayerList = 10; obj.limitText = "..."; return obj; } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/VoteObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/VoteObj.java new file mode 100644 index 00000000..6a54bd62 --- /dev/null +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/VoteObj.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.server.core.objs.config; + +public class VoteObj { + /** + * 最小通过投票数 + */ + public int minVote; + /** + * 投票时间 + */ + public int voteTime; + /** + * 投票队列最大大小 + */ + public int voteListSize; + + public static VoteObj make() { + VoteObj obj = new VoteObj(); + obj.minVote = 3; + obj.voteTime = 30; + obj.voteListSize = 10; + return obj; + } +} diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java index 64bf475f..c94afeb9 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java @@ -8,6 +8,7 @@ public class HelpNormalObj { public String cancel; public String vote; public String vote1; + public String agree; public String push; public String push1; public String mute; @@ -98,6 +99,8 @@ public boolean check() { return true; if (hud14 == null) return true; + if (agree == null) + return true; return hud5 == null; } @@ -162,5 +165,7 @@ public void init() { hud5 = "[AllMusic]/music hud pic speed [数值] 设置图片旋转速度"; if (join == null) join = "[AllMusic]/music join 重新播放当前歌曲"; + if (agree == null) + join = "[AllMusic]/music agree 同意当前的投票"; } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java index a9b87f40..fb5ab758 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java @@ -6,19 +6,16 @@ public class PushObj { public String bq; public String bq1; public String bq2; - public String agree; - public String bqAgree; - public String arAgree; - public String timeOut; public String doPush; public String noId; public String noId1; - public String idErr; public String pushErr; public String cancel; + public String cancel1; public String err1; - public String err2; public String err3; + public String err4; + public String err5; public static PushObj make() { PushObj obj = new PushObj(); @@ -38,30 +35,24 @@ public boolean check() { return true; if (bq2 == null) return true; - if (agree == null) - return true; - if (bqAgree == null) - return true; - if (arAgree == null) - return true; - if (timeOut == null) - return true; if (noId == null) return true; if (noId1 == null) return true; - if (idErr == null) - return true; if (pushErr == null) return true; if (cancel == null) return true; if (err1 == null) return true; - if (err2 == null) - return true; if (err3 == null) return true; + if (err4 == null) + return true; + if (err5 == null) + return true; + if (cancel1 == null) + return true; return doPush == null; } @@ -76,31 +67,23 @@ public void init() { bq1 = "[AllMusic]或者点击 "; if (bq2 == null) bq2 = "同意插歌"; - if (agree == null) - agree = "[AllMusic]你同意插歌"; - if (bqAgree == null) - bqAgree = "[AllMusic]" + ARG.player + "同意插歌,共有" + ARG.count + "名玩家同意插歌,需要至少" + ARG.countAll + "名玩家同意才会插歌。"; - if (arAgree == null) - arAgree = "[AllMusic]你已申请插歌"; - if (timeOut == null) - timeOut = "[AllMusic]插歌时间结束"; if (doPush == null) doPush = "[AllMusic]播放顺序已调整"; if (noId == null) noId = "[AllMusic]没有找到你的点歌"; if (noId1 == null) noId1 = "[AllMusic]没有找到序号为" + ARG.index + "的点歌"; - if (idErr == null) - idErr = "[AllMusic]错误的歌曲序号"; if (pushErr == null) pushErr = "[AllMusic]这首歌已经是下一首播放了"; if (err1 == null) err1 = "[AllMusic]你没有发起插歌"; - if (err2 == null) - err2 = "[AllMusic]你发起的插歌已超时"; - if (err3 == null) - err3 = "[AllMusic]发起失败,已经有人发起了插歌投票"; + if (err4 == null) + err4 = "[AllMusic]切歌投票跳过不存在的音乐投票"; + if (err5 == null) + err5 = "[AllMusic]你已经申请过切歌了,不能再继续申请"; if (cancel == null) cancel = "[AllMusic]插歌投票已被发起者取消"; + if (cancel1 == null) + cancel1 = "[AllMusic]已删除你发起的投票"; } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java index b726e03a..34749430 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java @@ -10,11 +10,14 @@ public class VoteObj { public String bqAgree; public String arAgree; public String timeOut; - public String voteDone; + public String next; public String err1; - public String err2; public String err3; + public String err4; + public String err5; public String cancel; + public String cancel1; + public String list; public static VoteObj make() { VoteObj obj = new VoteObj(); @@ -44,13 +47,19 @@ public boolean check() { return true; if (err1 == null) return true; - if (err2 == null) - return true; if (err3 == null) return true; + if (err4 == null) + return true; + if (err5 == null) + return true; if (cancel == null) return true; - return voteDone == null; + if (cancel1 == null) + return true; + if (list == null) + return true; + return next == null; } public void init() { @@ -59,7 +68,7 @@ public void init() { if (doVote == null) doVote = "[AllMusic]已发起切歌投票"; if (bq == null) - bq = "[AllMusic]" + ARG.player + "发起了切歌投票," + ARG.time + "秒后结束,输入/music vote 同意切歌,需要至少" + ARG.countAll + "名玩家同意才会切歌。"; + bq = "[AllMusic]" + ARG.player + "发起了切歌投票," + ARG.time + "秒后结束,输入/music agree 同意切歌,需要至少" + ARG.countAll + "名玩家同意才会切歌。"; if (bq1 == null) bq1 = "[AllMusic]或者点击 "; if (bq2 == null) @@ -67,20 +76,26 @@ public void init() { if (agree == null) agree = "[AllMusic]你同意切歌"; if (bqAgree == null) - bqAgree = "[AllMusic]" + ARG.player + "同意切歌,共有" + ARG.count + "名玩家同意切歌,需要至少" + ARG.countAll + "名玩家同意才会切歌。"; + bqAgree = "[AllMusic]" + ARG.player + "同意投票,共有" + ARG.count + "名玩家同意投票,需要至少" + ARG.countAll + "名玩家同意才会通过。"; if (arAgree == null) arAgree = "[AllMusic]你已申请切歌"; if (timeOut == null) - timeOut = "[AllMusic]切歌时间结束"; + timeOut = "[AllMusic]投票时间结束,共有" + ARG.count + "同意,需要" + ARG.countAll + "才能通过"; if (err1 == null) - err1 = "[AllMusic]你没有发起切歌"; - if (err2 == null) - err2 = "[AllMusic]你发起的切歌已超时"; + err1 = "[AllMusic]你没有发起过切歌投票"; if (err3 == null) - err3 = "[AllMusic]已经有人发起了切歌"; - if (voteDone == null) - voteDone = "[AllMusic]已切歌"; + err3 = "[AllMusic]这首歌已经在投票中了"; + if (err4 == null) + err4 = "[AllMusic]当前没有进行中的投票"; + if (err5 == null) + err5 = "[AllMusic]你已经通过了当前投票"; + if (next == null) + next = "[AllMusic]已切换到播放下一首歌"; if (cancel == null) cancel = "[AllMusic]切歌投票已被发起者取消"; + if (cancel1 == null) + cancel1 = "[AllMusic]歌曲结束,切歌投票终止"; + if (list == null) + list = "[AllMusic]剩余" + ARG.count + "个投票"; } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/SongInfoObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/SongInfoObj.java index 86167354..58dd0d91 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/SongInfoObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/SongInfoObj.java @@ -128,7 +128,7 @@ public void setName(String info) { name = info; } - public String getID() { + public String getId() { return id; } From df304d4d89297d692567fbd814f87b975e24e0ba Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 28 Aug 2026 21:02:31 +0800 Subject: [PATCH 21/46] =?UTF-8?q?=E4=B8=8B=E4=B8=80=E9=A6=96=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E6=97=B6=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allmusic/server/core/music/PlayMusic.java | 9 +++++ .../server/core/music/PlayRuntime.java | 40 ++++++++++++------- .../server/core/objs/message/PushObj.java | 5 +++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java index cf075b39..dda44f9b 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java @@ -582,5 +582,14 @@ public static void removeNowPlayPlayer(String player) { public static void clearNowPlayer() { nowPlayPlayer.clear(); } + + public static SongInfoObj getNextMusic() { + synchronized (playList) { + if (playList.isEmpty()) { + return null; + } + return playList.get(0); + } + } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java index c7fb9c75..12660f15 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java @@ -60,13 +60,7 @@ public static void stop() { private static void clear() { isPlay = false; - VoteItem vote = PlayMusic.getVote(); - if (vote != null && vote.getType() == VoteItem.VoteType.NEXT - && vote.getApi().equalsIgnoreCase(PlayMusic.nowPlayMusic.getApi()) - && vote.getId().equalsIgnoreCase(PlayMusic.nowPlayMusic.getId())) { - AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.cancel1); - PlayMusic.removeVote(); - } + checkVoteNext(); PlayMusic.musicNowTime = 0; PlayMusic.musicAllTime = 0; @@ -80,6 +74,28 @@ private static void clear() { HudUtils.sendHudLyricData(); } + private static void checkVoteNext() { + VoteItem vote = PlayMusic.getVote(); + if (vote != null && vote.getType() == VoteItem.VoteType.NEXT + && vote.getApi().equalsIgnoreCase(PlayMusic.nowPlayMusic.getApi()) + && vote.getId().equalsIgnoreCase(PlayMusic.nowPlayMusic.getId())) { + AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.cancel1); + PlayMusic.removeVote(); + } + } + + private static void checkVotePush() { + VoteItem vote = PlayMusic.getVote(); + SongInfoObj next = PlayMusic.getNextMusic(); + if (vote != null && next != null) { + if (vote.getType() == VoteItem.VoteType.PUSH + && vote.getId().equalsIgnoreCase(next.getId()) + && vote.getApi().equalsIgnoreCase(next.getApi())) { + PlayMusic.removeVote(); + AllMusic.side.broadcastInTask(AllMusic.getMessage().push.cancel2); + } + } + } /** * 歌曲时间定时器 @@ -120,14 +136,6 @@ public static int getMiniVote() { return Math.min(AllMusic.getConfig().vote.minVote, AllMusic.side.getPlayers().size()); } - public static boolean checkMusic(String id, String api) { - if (PlayMusic.nowPlayMusic.getId().equalsIgnoreCase(id) - && PlayMusic.nowPlayMusic.getApi().equalsIgnoreCase(api)) { - return true; - } - return PlayMusic.getMusic(id, api) != null; - } - private static void sendVoteInfo(boolean timeout) { if (timeout) { VoteItem vote = PlayMusic.getVote(); @@ -229,6 +237,8 @@ private static void musicPlayTask() { continue; } + checkVotePush(); + IMusicApi api = AllMusic.MUSIC_APIS.get(PlayMusic.nowPlayMusic.getApi()); PlayMusic.url = PlayMusic.nowPlayMusic.getPlayerUrl() == null ? diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java index fb5ab758..ca2a4689 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java @@ -12,6 +12,7 @@ public class PushObj { public String pushErr; public String cancel; public String cancel1; + public String cancel2; public String err1; public String err3; public String err4; @@ -53,6 +54,8 @@ public boolean check() { return true; if (cancel1 == null) return true; + if (cancel2 == null) + return true; return doPush == null; } @@ -85,5 +88,7 @@ public void init() { cancel = "[AllMusic]插歌投票已被发起者取消"; if (cancel1 == null) cancel1 = "[AllMusic]已删除你发起的投票"; + if (cancel2 == null) + cancel2 = "[AllMusic]下一首歌就是投票歌曲,已取消投票"; } } From ee612df29d481c7ed2069a623070f02e54804186 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 28 Aug 2026 22:15:12 +0800 Subject: [PATCH 22/46] =?UTF-8?q?=E9=9F=B3=E4=B9=90API=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allmusic/server/core/AllMusic.java | 28 +++++---- .../allmusic/server/core/IMusicApi.java | 11 +++- .../server/core/command/CommandEX.java | 16 +++-- .../server/core/command/sub/CommandAgree.java | 10 +-- .../core/command/sub/CommandApiCommand.java | 62 +++++++++++++++++++ .../server/core/command/sub/CommandPush.java | 6 +- .../core/command/sub/CommandReload.java | 1 + .../core/command/sub/CommandSearch.java | 5 +- .../core/command/sub/CommandSearchApi.java | 2 +- .../server/core/command/sub/CommandVote.java | 9 +-- .../server/core/music/MusicSearch.java | 4 +- .../allmusic/server/core/music/PlayMusic.java | 19 +++++- .../server/core/music/PlayRuntime.java | 28 ++++++--- .../allmusic/server/core/music/VoteItem.java | 7 +++ .../server/core/objs/message/VoteObj.java | 5 ++ .../core/objs/music/PlayerAddMusicObj.java | 4 -- .../allmusic/server/core/side/BaseSide.java | 9 ++- .../server/core/utils/MusicApiLoader.java | 21 +++++++ 18 files changed, 198 insertions(+), 49 deletions(-) create mode 100644 server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandApiCommand.java diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java index 5b1108fe..d4f07ac0 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java @@ -40,6 +40,8 @@ public class AllMusic { public static final String SERVER_DIR = "allmusic_server/"; + public static File basePath; + /** * 客户端插件信道名 */ @@ -282,6 +284,10 @@ public static void start() { AllMusic.log.data("[AllMusic]没有注册音乐"); } + for (IMusicApi api : MUSIC_APIS.values()) { + api.reload(basePath); + } + log.data("[AllMusic]已启动-" + version); } @@ -405,11 +411,13 @@ public static void joinPlayNow(String player) { public static void init(File file) { log.data("[AllMusic]正在启动,感谢使用,本插件交流群:571239090"); try { - file.mkdir(); + isRun = true; + basePath = file; + basePath.mkdir(); - configFile = new File(file, "config.json"); - messageFile = new File(file, "message.json"); - cookieFile = new File(file, "cookie.json"); + configFile = new File(basePath, "config.json"); + messageFile = new File(basePath, "message.json"); + cookieFile = new File(basePath, "cookie.json"); if (!configFile.exists()) { configFile.createNewFile(); } @@ -420,16 +428,14 @@ public static void init(File file) { cookieFile.createNewFile(); } - BanSave.init(file); - HudSave.init(file); - MusicListSave.init(file); + BanSave.init(basePath); + HudSave.init(basePath); + MusicListSave.init(basePath); - loadConfig(); - - apis = new File(file, "api"); + apis = new File(basePath, "api"); apis.mkdirs(); - isRun = true; + loadConfig(); } catch (IOException e) { isRun = false; log.data("[AllMusic]启动失败"); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/IMusicApi.java b/server/src/main/java/com/coloryr/allmusic/server/core/IMusicApi.java index 84589812..0f955342 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/IMusicApi.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/IMusicApi.java @@ -4,12 +4,17 @@ import com.coloryr.allmusic.server.core.objs.music.SearchPageObj; import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import java.io.File; +import java.util.List; + public interface IMusicApi { + void reload(File path); + String getId(); SongInfoObj getMusic(String id, String player, boolean isList); - SearchPageObj search(String[] args, boolean isDefault); + SearchPageObj search(String[] args); void setList(String id, Object sender); @@ -22,4 +27,8 @@ public interface IMusicApi { String getMusicId(String arg); boolean checkId(String id); + + void command(Object sender, String name, String[] args); + + List tab(Object sender, String name, String[] args); } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java index dccd8096..b7616e53 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java @@ -73,9 +73,8 @@ public class CommandEX { * @param sender 发送者 * @param name 用户名 * @param args 参数 - * @param isDefault 是否是默认点歌方式 */ - public static void searchMusic(Object sender, String name, String[] args, boolean isDefault) { + public static void searchMusic(Object sender, String name, String[] args) { String apiname = AllMusic.getConfig().defaultApi; @@ -88,7 +87,6 @@ public static void searchMusic(Object sender, String name, String[] args, boolea obj.sender = sender; obj.name = name; obj.args = args; - obj.isDefault = isDefault; obj.api = apiname; if (AllMusic.side.onMusicAdd(sender, obj)) { @@ -105,9 +103,8 @@ public static void searchMusic(Object sender, String name, String[] args, boolea * @param sender 发送者 * @param name 用户名 * @param args 参数 - * @param isDefault 是否是默认点歌方式 */ - public static void searchMusicApi(Object sender, String name, String[] args, boolean isDefault) { + public static void searchMusicApi(Object sender, String name, String[] args) { if (args == null || args.length < 2) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().musicPlay.error2); return; @@ -119,12 +116,14 @@ public static void searchMusicApi(Object sender, String name, String[] args, boo AllMusic.side.sendMessage(sender, AllMusic.getMessage().musicPlay.error2); return; } + + String[] newArgs = new String[args.length - 1]; + System.arraycopy(args, 1, newArgs, 0, newArgs.length); PlayerAddMusicObj obj = new PlayerAddMusicObj(); obj.sender = sender; obj.name = name; - obj.args = args; - obj.isDefault = isDefault; + obj.args = newArgs; obj.api = apiname; if (AllMusic.side.onMusicAdd(sender, obj)) { @@ -222,7 +221,6 @@ public static void addMusic(Object sender, String name, String api, String arg) obj.sender = sender; obj.id = musicID; obj.name = name; - obj.isDefault = false; obj.api = api; if (AllMusic.side.onMusicAdd(sender, obj)) { @@ -280,7 +278,7 @@ public static void execute(Object sender, String name, String[] args) { else { switch (AllMusic.getConfig().defaultAddMusic) { case 1: - searchMusic(sender, name, args, true); + searchMusic(sender, name, args); break; case 0: default: diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java index cf8a8ff8..39ff05a0 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java @@ -8,6 +8,8 @@ import com.coloryr.allmusic.server.core.music.VoteItem; import com.coloryr.allmusic.server.core.objs.message.ARG; +import java.util.Locale; + public class CommandAgree extends ACommand { @Override public void execute(Object sender, String name, String[] args) { @@ -20,15 +22,15 @@ public void execute(Object sender, String name, String[] args) { VoteItem vote = PlayMusic.getVote(); if (vote == null) { - AllMusic.side.sendMessage(name, AllMusic.getMessage().vote.err4); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err4); return; } - if (vote.votePlayer.contains(name)) { - AllMusic.side.sendMessage(name, AllMusic.getMessage().vote.err5); + if (vote.votePlayer.contains(name.toLowerCase(Locale.ROOT))) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err5); return; } - AllMusic.side.sendMessage(name, AllMusic.getMessage().vote.agree); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.agree); String data = AllMusic.getMessage().vote.bqAgree; data = data.replace(ARG.player, name) .replace(ARG.count, String.valueOf(vote.votePlayer.size())) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandApiCommand.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandApiCommand.java new file mode 100644 index 00000000..695e7516 --- /dev/null +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandApiCommand.java @@ -0,0 +1,62 @@ +package com.coloryr.allmusic.server.core.command.sub; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.IMusicApi; +import com.coloryr.allmusic.server.core.command.ACommand; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class CommandApiCommand extends ACommand { + @Override + public void execute(Object sender, String name, String[] args) { + if (args.length < 2) { + AllMusic.side.sendMessage(sender, "[AllMusic]没有指定API"); + return; + } + + String apiname = args[1]; + + IMusicApi api = AllMusic.MUSIC_APIS.get(apiname); + + if (api == null) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().musicPlay.error2); + return; + } + + if (args.length == 2) { + api.command(sender, name, new String[0]); + } else { + String[] newArgs = new String[args.length - 2]; + System.arraycopy(args, 2, newArgs, 0, newArgs.length); + + api.command(sender, name, newArgs); + } + } + + @Override + public List tab(Object player, String name, String[] args, int index) { + if (args.length == 1) { + return new ArrayList<>(AllMusic.MUSIC_APIS.keySet()); + } + + String apiname = args[1]; + + IMusicApi api = AllMusic.MUSIC_APIS.get(apiname); + + if (api == null) { + AllMusic.side.sendMessage(player, AllMusic.getMessage().musicPlay.error2); + return Collections.emptyList(); + } + + if (args.length == 2) { + return api.tab(player, name, new String[0]); + } else { + String[] newArgs = new String[args.length - 2]; + System.arraycopy(args, 2, newArgs, 0, newArgs.length); + + return api.tab(player, name, newArgs); + } + } +} diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java index b62f40e8..23604c53 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java @@ -23,8 +23,12 @@ public void execute(Object sender, String name, String[] args) { } if (PlayMusic.getListSize() == 0 && PlayMusic.getIdleListSize() == 0) { AllMusic.side.sendMessage(sender, AllMusic.side.miniMessage(AllMusic.getMessage().musicPlay.emptyPlay)); + return; + } else if (PlayMusic.fullVoteList()) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err6); + return; } - SongInfoObj music = null; + SongInfoObj music; if (args.length == 1) { music = PlayMusic.findPlayerMusic(name); if (music == null) { diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandReload.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandReload.java index 66a459cd..3aa78247 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandReload.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandReload.java @@ -1,6 +1,7 @@ package com.coloryr.allmusic.server.core.command.sub; import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.IMusicApi; import com.coloryr.allmusic.server.core.command.ACommand; public class CommandReload extends ACommand { diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearch.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearch.java index 70efe362..b0e90206 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearch.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearch.java @@ -26,7 +26,10 @@ public void execute(Object sender, String name, String[] args) { return; } + String[] newArgs = new String[args.length - 1]; + System.arraycopy(args, 1, newArgs, 0, newArgs.length); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().search.startSearch); - CommandEX.searchMusic(sender, name, args, false); + CommandEX.searchMusic(sender, name, newArgs); } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearchApi.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearchApi.java index ce8dadff..ab05227b 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearchApi.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandSearchApi.java @@ -40,7 +40,7 @@ public void execute(Object sender, String name, String[] args) { System.arraycopy(args, 1, newArgs, 0, newArgs.length); AllMusic.side.sendMessage(sender, AllMusic.getMessage().search.startSearch); - CommandEX.searchMusicApi(sender, name, newArgs, false); + CommandEX.searchMusicApi(sender, name, newArgs); } @Override diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java index eef944d1..96479404 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandVote.java @@ -16,6 +16,8 @@ public void execute(Object sender, String name, String[] args) { !AllMusic.side.checkPermission(sender, PermissionList.PERMISSION_VOTE)) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.noPermission); return; + } else if (PlayMusic.fullVoteList()) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err6); } else if (PlayMusic.getListSize() == 0 && PlayMusic.getIdleListSize() == 0) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().musicPlay.emptyPlay); } else if (PlayMusic.nowPlayMusic == null) { @@ -32,7 +34,7 @@ public void execute(Object sender, String name, String[] args) { return; } PlayMusic.removeVote(name, VoteItem.VoteType.NEXT); - AllMusic.side.sendMessage(name, AllMusic.getMessage().push.cancel1); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.cancel1); } else { AllMusic.side.sendMessage(sender, AllMusic.getMessage().command.error); } @@ -41,9 +43,8 @@ public void execute(Object sender, String name, String[] args) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().command.error); return; } else { - String player = name.toLowerCase(Locale.ROOT); - VoteItem item = new VoteItem(PlayMusic.nowPlayMusic.getApi(), PlayMusic.nowPlayMusic.getId(), player, VoteItem.VoteType.NEXT); - item.votePlayer.add(player); + VoteItem item = new VoteItem(PlayMusic.nowPlayMusic.getApi(), PlayMusic.nowPlayMusic.getId(), name, VoteItem.VoteType.NEXT); + item.votePlayer.add(name.toLowerCase(Locale.ROOT)); if (PlayMusic.startVote(item)) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.doVote); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/MusicSearch.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/MusicSearch.java index 0ff085ed..7b563b17 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/MusicSearch.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/MusicSearch.java @@ -33,10 +33,10 @@ private static void task() { AllMusic.side.sendMessageTask(obj.sender, AllMusic.getMessage().musicPlay.error2); continue; } - SearchPageObj search = api.search(obj.args, obj.isDefault); + SearchPageObj search = api.search(obj.args); if (search == null) AllMusic.side.sendMessageTask(obj.sender, AllMusic.getMessage().search - .cantSearch.replace(ARG.name, obj.isDefault ? obj.args[0] : obj.args[1])); + .cantSearch.replace(ARG.name, obj.args[0])); else { AllMusic.side.sendMessageTask(obj.sender, AllMusic.getMessage().search.res); addSearch(obj.name, search); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java index dda44f9b..5156a7d3 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java @@ -111,6 +111,13 @@ public static void addVote(String player) { public static boolean startVote(VoteItem vote) { String id = vote.getId(); String api = vote.getApi(); + + if (PlayMusic.vote != null && PlayMusic.vote.getType() == vote.getType() + && PlayMusic.vote.getApi().equalsIgnoreCase(vote.getApi()) + && PlayMusic.vote.getId().equalsIgnoreCase(vote.getId())) { + return false; + } + for (VoteItem item : voteList) { if (item.getId().equalsIgnoreCase(id) && item.getApi().equalsIgnoreCase(api)) { return false; @@ -158,6 +165,16 @@ public static void doVote() { vote = null; } + public static List cloneVoteList() { + return new ArrayList<>(voteList); + } + + public static void removeVote(VoteItem vote) { + voteList.remove(vote); + } + + public static boolean fullVoteList() { return voteList.size() >= AllMusic.getConfig().vote.voteListSize; } + public static void voteTick() { voteTime--; } @@ -231,7 +248,7 @@ private static void task() { if (obj != null) { IMusicApi api = AllMusic.MUSIC_APIS.get(obj.api); if (api != null) { - addMusic(obj.sender, obj.id, api, obj.name, obj.isDefault); + addMusic(obj.sender, obj.id, api, obj.name, false); } } Thread.sleep(10); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java index 12660f15..4f9e606e 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayRuntime.java @@ -76,23 +76,33 @@ private static void clear() { private static void checkVoteNext() { VoteItem vote = PlayMusic.getVote(); - if (vote != null && vote.getType() == VoteItem.VoteType.NEXT - && vote.getApi().equalsIgnoreCase(PlayMusic.nowPlayMusic.getApi()) - && vote.getId().equalsIgnoreCase(PlayMusic.nowPlayMusic.getId())) { + if (vote != null && vote.getType() == VoteItem.VoteType.NEXT && vote.like(PlayMusic.nowPlayMusic)) { AllMusic.side.broadcastInTask(AllMusic.getMessage().vote.cancel1); PlayMusic.removeVote(); } + + for (VoteItem item : PlayMusic.cloneVoteList()) { + if (item.getType() == VoteItem.VoteType.NEXT && item.like(PlayMusic.nowPlayMusic)) { + PlayMusic.removeVote(item); + } + } } private static void checkVotePush() { VoteItem vote = PlayMusic.getVote(); SongInfoObj next = PlayMusic.getNextMusic(); - if (vote != null && next != null) { - if (vote.getType() == VoteItem.VoteType.PUSH - && vote.getId().equalsIgnoreCase(next.getId()) - && vote.getApi().equalsIgnoreCase(next.getApi())) { - PlayMusic.removeVote(); - AllMusic.side.broadcastInTask(AllMusic.getMessage().push.cancel2); + if (next != null) { + if (vote != null) { + if (vote.getType() == VoteItem.VoteType.PUSH && vote.like(next)) { + PlayMusic.removeVote(); + AllMusic.side.broadcastInTask(AllMusic.getMessage().push.cancel2); + } + } + + for (VoteItem item : PlayMusic.cloneVoteList()) { + if (item.getType() == VoteItem.VoteType.PUSH && item.like(next)) { + PlayMusic.removeVote(item); + } } } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java index 7cc2d7d5..e4fe1ffa 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java @@ -1,5 +1,7 @@ package com.coloryr.allmusic.server.core.music; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; + import java.util.HashSet; import java.util.Set; @@ -37,4 +39,9 @@ public VoteItem(String api, String id, String voteSender, VoteType type) { this.type = type; this.voteSender = voteSender; } + + public boolean like(SongInfoObj info) { + return api.equalsIgnoreCase(info.getApi()) + && id.equalsIgnoreCase(info.getId()); + } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java index 34749430..67fce7e2 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java @@ -15,6 +15,7 @@ public class VoteObj { public String err3; public String err4; public String err5; + public String err6; public String cancel; public String cancel1; public String list; @@ -53,6 +54,8 @@ public boolean check() { return true; if (err5 == null) return true; + if (err6 == null) + return true; if (cancel == null) return true; if (cancel1 == null) @@ -89,6 +92,8 @@ public void init() { err4 = "[AllMusic]当前没有进行中的投票"; if (err5 == null) err5 = "[AllMusic]你已经通过了当前投票"; + if (err6 == null) + err6 = "[AllMusic]投票队列已满,无法发起投票"; if (next == null) next = "[AllMusic]已切换到播放下一首歌"; if (cancel == null) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/PlayerAddMusicObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/PlayerAddMusicObj.java index 0f187f35..defb660e 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/PlayerAddMusicObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/music/PlayerAddMusicObj.java @@ -21,10 +21,6 @@ public class PlayerAddMusicObj { * 参数 */ public String[] args; - /** - * 是否是默认点歌方式 - */ - public boolean isDefault; /** * 歌曲Url */ diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/side/BaseSide.java b/server/src/main/java/com/coloryr/allmusic/server/core/side/BaseSide.java index c1978ccc..84d01773 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/side/BaseSide.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/side/BaseSide.java @@ -4,6 +4,7 @@ import com.coloryr.allmusic.codec.HudPosObj; import com.coloryr.allmusic.codec.MusicPack; import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.IMusicApi; import com.coloryr.allmusic.server.core.music.PlayMusic; import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; @@ -154,7 +155,13 @@ public abstract class BaseSide { * 重载 */ public final void reload() { - AllMusic.init(getFolder()); + File basePath = getFolder(); + + AllMusic.init(basePath); + + for (IMusicApi api : AllMusic.MUSIC_APIS.values()) { + api.reload(basePath); + } } /** diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/utils/MusicApiLoader.java b/server/src/main/java/com/coloryr/allmusic/server/core/utils/MusicApiLoader.java index cf02e823..0bf6e2ea 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/utils/MusicApiLoader.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/utils/MusicApiLoader.java @@ -5,6 +5,7 @@ import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -12,8 +13,11 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.zip.ZipEntry; public class MusicApiLoader { + private static final byte VERSION = '2'; + public static List loadFromDirectory(File dir) { List instances = new ArrayList<>(); if (!dir.exists() || !dir.isDirectory()) { @@ -34,6 +38,23 @@ private static List loadFromJar(File jarFile) { List instances = new ArrayList<>(); try (JarFile jar = new JarFile(jarFile)) { + ZipEntry entry1 = jar.getEntry("version"); + + if (entry1 == null) { + AllMusic.log.data("[AllMusic]旧版API,跳过加载:" + jarFile.getName()); + return instances; + } + + InputStream stream = jar.getInputStream(entry1); + int size = stream.available(); + byte[] temp = new byte[size]; + stream.read(temp); + + if (temp[0] != VERSION) { + AllMusic.log.data("[AllMusic]旧版API,跳过加载:" + jarFile.getName()); + return instances; + } + Enumeration entries = jar.entries(); List classNames = new ArrayList<>(); From e9c0efa6f273976d68091729df5d2126039c8b31 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 28 Aug 2026 22:33:17 +0800 Subject: [PATCH 23/46] =?UTF-8?q?=E7=BC=BA=E5=A4=B1=E7=9A=84=E8=AF=AD?= =?UTF-8?q?=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allmusic/server/core/command/sub/CommandPush.java | 4 ++-- .../coloryr/allmusic/server/core/objs/message/PushObj.java | 5 +---- .../coloryr/allmusic/server/core/objs/message/VoteObj.java | 7 +------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java index 23604c53..b3a139a7 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandPush.java @@ -45,7 +45,7 @@ public void execute(Object sender, String name, String[] args) { if (PlayMusic.startVote(item)) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); } else { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err3); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err5); } AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); @@ -75,7 +75,7 @@ public void execute(Object sender, String name, String[] args) { if (PlayMusic.startVote(item)) { AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); } else { - AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err3); + AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.err5); } AllMusic.side.sendMessage(sender, AllMusic.getMessage().push.doVote); diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java index ca2a4689..38d9c1fc 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/PushObj.java @@ -14,7 +14,6 @@ public class PushObj { public String cancel1; public String cancel2; public String err1; - public String err3; public String err4; public String err5; @@ -46,8 +45,6 @@ public boolean check() { return true; if (err1 == null) return true; - if (err3 == null) - return true; if (err4 == null) return true; if (err5 == null) @@ -83,7 +80,7 @@ public void init() { if (err4 == null) err4 = "[AllMusic]切歌投票跳过不存在的音乐投票"; if (err5 == null) - err5 = "[AllMusic]你已经申请过切歌了,不能再继续申请"; + err5 = "[AllMusic]你已经申请过插歌了,不能再继续申请"; if (cancel == null) cancel = "[AllMusic]插歌投票已被发起者取消"; if (cancel1 == null) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java index 67fce7e2..48d9cd65 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/VoteObj.java @@ -8,7 +8,6 @@ public class VoteObj { public String bq2; public String agree; public String bqAgree; - public String arAgree; public String timeOut; public String next; public String err1; @@ -42,8 +41,6 @@ public boolean check() { return true; if (bqAgree == null) return true; - if (arAgree == null) - return true; if (timeOut == null) return true; if (err1 == null) @@ -80,14 +77,12 @@ public void init() { agree = "[AllMusic]你同意切歌"; if (bqAgree == null) bqAgree = "[AllMusic]" + ARG.player + "同意投票,共有" + ARG.count + "名玩家同意投票,需要至少" + ARG.countAll + "名玩家同意才会通过。"; - if (arAgree == null) - arAgree = "[AllMusic]你已申请切歌"; if (timeOut == null) timeOut = "[AllMusic]投票时间结束,共有" + ARG.count + "同意,需要" + ARG.countAll + "才能通过"; if (err1 == null) err1 = "[AllMusic]你没有发起过切歌投票"; if (err3 == null) - err3 = "[AllMusic]这首歌已经在投票中了"; + err3 = "[AllMusic]切歌投票已经在进行中了"; if (err4 == null) err4 = "[AllMusic]当前没有进行中的投票"; if (err5 == null) From 9eb0c4873b0954c90e7d8b7262b708fdb51c9b04 Mon Sep 17 00:00:00 2001 From: MinaRelayer Date: Sun, 6 Sep 2026 10:28:09 +0800 Subject: [PATCH 24/46] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allmusic/server/core/objs/message/HelpNormalObj.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java index c94afeb9..ddb6afae 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/message/HelpNormalObj.java @@ -166,6 +166,6 @@ public void init() { if (join == null) join = "[AllMusic]/music join 重新播放当前歌曲"; if (agree == null) - join = "[AllMusic]/music agree 同意当前的投票"; + agree = "[AllMusic]/music agree 同意当前的投票"; } } From 453ce50c84e066a056252d3aeb023d304306e88c Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sun, 6 Sep 2026 19:01:26 +0800 Subject: [PATCH 25/46] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E8=87=AA=E7=BA=A0=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/core/objs/config/BanObj.java | 25 ++++++++++++++++-- .../server/core/objs/config/ConfigObj.java | 2 +- .../server/core/objs/config/EconomyObj.java | 14 ++-------- .../server/core/objs/config/FunConfigObj.java | 8 ++---- .../server/core/objs/config/MusicListObj.java | 8 +++++- .../allmusic/server/core/saves/BanSave.java | 5 +++- .../server/core/saves/MusicListSave.java | 5 +++- settings.gradle.kts | 26 +++++++++---------- 8 files changed, 56 insertions(+), 37 deletions(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/BanObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/BanObj.java index 7f4ed8b4..04a4f9df 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/BanObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/BanObj.java @@ -21,8 +21,29 @@ public static BanObj make() { } public boolean check() { - return banPlayers == null || banServer == null || banMusics == null || mutePlayers == null - || muteListPlayers == null; + boolean saveConfig = false; + if (banPlayers == null) { + banPlayers = new HashSet<>(); + saveConfig = true; + } + if (banServer == null) { + banServer = new HashSet<>(); + saveConfig = true; + } + if (mutePlayers == null) { + mutePlayers = new HashSet<>(); + saveConfig = true; + } + if (muteListPlayers == null) { + muteListPlayers = new HashSet<>(); + saveConfig = true; + } + if (banMusics == null) { + banMusics = new HashMap<>(); + saveConfig = true; + } + + return saveConfig; } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java index 11696d9e..69e2d9e8 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/ConfigObj.java @@ -125,7 +125,7 @@ public boolean check() { saveConfig = true; adminList = new HashSet<>(); } - if (economy == null || economy.check()) { + if (economy == null) { saveConfig = true; economy = EconomyObj.make(); } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java index 138acc9b..7e50d13b 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java @@ -6,18 +6,8 @@ public class EconomyObj { public static EconomyObj make() { EconomyObj obj = new EconomyObj(); - obj.init(); - + obj.backend = "server1"; + obj.vault = true; return obj; } - - public boolean check() { - - return false; - } - - public void init() { - backend = "Server1"; - vault = true; - } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/FunConfigObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/FunConfigObj.java index e17ec91b..3131c450 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/FunConfigObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/FunConfigObj.java @@ -6,13 +6,9 @@ public class FunConfigObj { public static FunConfigObj make() { FunConfigObj obj = new FunConfigObj(); - obj.init(); + obj.rain = true; + obj.rainRate = 10; return obj; } - - public void init() { - rain = true; - rainRate = 10; - } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/MusicListObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/MusicListObj.java index d4c6c58d..3bb89522 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/MusicListObj.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/MusicListObj.java @@ -1,6 +1,7 @@ package com.coloryr.allmusic.server.core.objs.config; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; @@ -15,6 +16,11 @@ public static MusicListObj make() { } public boolean check() { - return musics == null; + boolean saveConfig = false; + if (musics == null) { + musics = new HashMap<>(); + saveConfig = true; + } + return saveConfig; } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/saves/BanSave.java b/server/src/main/java/com/coloryr/allmusic/server/core/saves/BanSave.java index 65f44a88..c51adea5 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/saves/BanSave.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/saves/BanSave.java @@ -32,10 +32,13 @@ public static void init(File file) throws IOException { } private static void banCheck() { - if (ban == null || ban.check()) { + if (ban == null) { ban = BanObj.make(); AllMusic.log.data("[AllMusic]配置文件ban.json错误,已覆盖"); saveBan(); + } else if (ban.check()) { + AllMusic.log.data("[AllMusic]配置文件ban.json错误,已覆盖"); + saveBan(); } } diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/saves/MusicListSave.java b/server/src/main/java/com/coloryr/allmusic/server/core/saves/MusicListSave.java index 1de0c515..ecdea0a9 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/saves/MusicListSave.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/saves/MusicListSave.java @@ -39,10 +39,13 @@ public static void saveMusicList() { } public static void musicListCheck() { - if (musicObj == null || musicObj.check()) { + if (musicObj == null) { musicObj = MusicListObj.make(); AllMusic.log.data("[AllMusic]配置文件music.json错误,已覆盖"); saveMusicList(); + } else if (musicObj.check()) { + AllMusic.log.data("[AllMusic]配置文件music.json错误,已覆盖"); + saveMusicList(); } } diff --git a/settings.gradle.kts b/settings.gradle.kts index c1a4fd26..fe6f7c69 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -60,19 +60,19 @@ include(":server:velocity") include(":onejar") -include(":onejar:fabric_1_16_5") -include(":onejar:fabric_1_20_1") -include(":onejar:fabric_1_21") -include(":onejar:fabric_1_21_4") -include(":onejar:fabric_1_21_6") -include(":onejar:fabric_1_21_11") -include(":onejar:fabric_26_1") -include(":onejar:fabric_26_2") -include(":onejar:neoforge_1_21") -include(":onejar:neoforge_1_21_4") -include(":onejar:neoforge_1_21_6") -include(":onejar:neoforge_1_21_11") -include(":onejar:neoforge_26_1") +//include(":onejar:fabric_1_16_5") +//include(":onejar:fabric_1_20_1") +//include(":onejar:fabric_1_21") +//include(":onejar:fabric_1_21_4") +//include(":onejar:fabric_1_21_6") +//include(":onejar:fabric_1_21_11") +//include(":onejar:fabric_26_1") +//include(":onejar:fabric_26_2") +//include(":onejar:neoforge_1_21") +//include(":onejar:neoforge_1_21_4") +//include(":onejar:neoforge_1_21_6") +//include(":onejar:neoforge_1_21_11") +//include(":onejar:neoforge_26_1") pluginManagement { repositories { From 0134c9cd98ea385aaa15904de8e617ef30418b28 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sun, 6 Sep 2026 19:01:46 +0800 Subject: [PATCH 26/46] Update build.gradle.kts --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 720fb28b..7f69f73b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.2.0" + version = "4.2.1" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 From 988a71b0f2fee1ddc6807a34d1d85b5070bc27b2 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 12 Sep 2026 18:29:54 +0800 Subject: [PATCH 27/46] Update CommandEX.java --- .../coloryr/allmusic/server/core/command/CommandEX.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java index b7616e53..1d4bd071 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/CommandEX.java @@ -186,6 +186,11 @@ public static boolean cost(Object sender, String name, int cost, String message) * @param arg 参数 */ public static void addMusic(Object sender, String name, String api, String arg) { + if (PlayMusic.getListSize() >= AllMusic.getConfig().limit.maxPlayList) { + AllMusic.side.sendMessageTask(sender, AllMusic.getMessage().addMusic.listFull); + return; + } + String musicID; IMusicApi api1 = AllMusic.MUSIC_APIS.get(api); @@ -197,9 +202,7 @@ public static void addMusic(Object sender, String name, String api, String arg) musicID = api1.getMusicId(arg); if (api1.checkId(musicID)) { - if (PlayMusic.getListSize() >= AllMusic.getConfig().limit.maxPlayerList) { - AllMusic.side.sendMessageTask(sender, AllMusic.getMessage().addMusic.listFull); - } else if (BanSave.checkBanMusic(musicID, api)) { + if (BanSave.checkBanMusic(musicID, api)) { AllMusic.side.sendMessageTask(sender, AllMusic.getMessage().addMusic.banMusic); } else if (PlayMusic.haveMusic(musicID, api)) { AllMusic.side.sendMessageTask(sender, AllMusic.getMessage().addMusic.existMusic); From af09d7cd94c3ba182382bca53cf36e00dbab4ed6 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 12 Sep 2026 18:32:20 +0800 Subject: [PATCH 28/46] Update build.gradle.kts --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7f69f73b..9a3d0dec 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.2.1" + version = "4.2.2" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 From b49ddc8f58cb685e436e213421709381a774c830 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 12 Sep 2026 19:32:23 +0800 Subject: [PATCH 29/46] fix --- onejar/fabric_26_1/build.gradle.kts | 3 +++ onejar/fabric_26_2/build.gradle.kts | 3 +++ onejar/neoforge_26_1/build.gradle.kts | 11 +++++++++++ server/fabric_26_1/build.gradle.kts | 3 +++ server/fabric_26_2/build.gradle.kts | 3 +++ server/neoforge_26_1/build.gradle.kts | 11 +++++++++++ server/neoforge_26_2/build.gradle.kts | 11 +++++++++++ 7 files changed, 45 insertions(+) diff --git a/onejar/fabric_26_1/build.gradle.kts b/onejar/fabric_26_1/build.gradle.kts index 620b16fb..a775583a 100644 --- a/onejar/fabric_26_1/build.gradle.kts +++ b/onejar/fabric_26_1/build.gradle.kts @@ -52,6 +52,9 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + shadowJar { archiveFileName.set("[fabric-26.1]AllMusic-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) diff --git a/onejar/fabric_26_2/build.gradle.kts b/onejar/fabric_26_2/build.gradle.kts index 11aee5d1..20e60957 100644 --- a/onejar/fabric_26_2/build.gradle.kts +++ b/onejar/fabric_26_2/build.gradle.kts @@ -51,6 +51,9 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + shadowJar { archiveFileName.set("[fabric-26.2]AllMusic-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) diff --git a/onejar/neoforge_26_1/build.gradle.kts b/onejar/neoforge_26_1/build.gradle.kts index 2f63fcc4..e3f00421 100644 --- a/onejar/neoforge_26_1/build.gradle.kts +++ b/onejar/neoforge_26_1/build.gradle.kts @@ -1,3 +1,5 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + plugins { id("dev.architectury.loom-no-remap") version Versions.architecturyLoom // id("architectury-plugin") version "3.5-SNAPSHOT" @@ -54,6 +56,15 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + shadowJar { archiveFileName.set("[neoforge-26.1]AllMusic-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) diff --git a/server/fabric_26_1/build.gradle.kts b/server/fabric_26_1/build.gradle.kts index b4025f2f..e844f31a 100644 --- a/server/fabric_26_1/build.gradle.kts +++ b/server/fabric_26_1/build.gradle.kts @@ -23,6 +23,9 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + shadowJar { archiveFileName.set("[fabric-26.1]AllMusic_Server-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) diff --git a/server/fabric_26_2/build.gradle.kts b/server/fabric_26_2/build.gradle.kts index 2e44cb77..e6a7073d 100644 --- a/server/fabric_26_2/build.gradle.kts +++ b/server/fabric_26_2/build.gradle.kts @@ -23,6 +23,9 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + shadowJar { archiveFileName.set("[fabric-26.2]AllMusic_Server-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) diff --git a/server/neoforge_26_1/build.gradle.kts b/server/neoforge_26_1/build.gradle.kts index 35daa673..094c3313 100644 --- a/server/neoforge_26_1/build.gradle.kts +++ b/server/neoforge_26_1/build.gradle.kts @@ -1,3 +1,5 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + plugins { id("dev.architectury.loom-no-remap") version Versions.architecturyLoom // id("architectury-plugin") version "3.5-SNAPSHOT" @@ -29,6 +31,15 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + shadowJar { archiveFileName.set("[neoforge-26.1]AllMusic_Server-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) diff --git a/server/neoforge_26_2/build.gradle.kts b/server/neoforge_26_2/build.gradle.kts index 899876a4..827736c3 100644 --- a/server/neoforge_26_2/build.gradle.kts +++ b/server/neoforge_26_2/build.gradle.kts @@ -1,3 +1,5 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + plugins { id("dev.architectury.loom-no-remap") version Versions.architecturyLoom // id("architectury-plugin") version "3.5-SNAPSHOT" @@ -29,6 +31,15 @@ tasks { } } + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + shadowJar { archiveFileName.set("[neoforge-26.2]AllMusic_Server-${project.version}.jar") destinationDirectory.set(file("${parent!!.projectDir}/../build")) From 93cf694530c2c19ecf04619013566e437831a0e5 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 12 Sep 2026 19:34:47 +0800 Subject: [PATCH 30/46] Update settings.gradle.kts --- settings.gradle.kts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index fe6f7c69..c1a4fd26 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -60,19 +60,19 @@ include(":server:velocity") include(":onejar") -//include(":onejar:fabric_1_16_5") -//include(":onejar:fabric_1_20_1") -//include(":onejar:fabric_1_21") -//include(":onejar:fabric_1_21_4") -//include(":onejar:fabric_1_21_6") -//include(":onejar:fabric_1_21_11") -//include(":onejar:fabric_26_1") -//include(":onejar:fabric_26_2") -//include(":onejar:neoforge_1_21") -//include(":onejar:neoforge_1_21_4") -//include(":onejar:neoforge_1_21_6") -//include(":onejar:neoforge_1_21_11") -//include(":onejar:neoforge_26_1") +include(":onejar:fabric_1_16_5") +include(":onejar:fabric_1_20_1") +include(":onejar:fabric_1_21") +include(":onejar:fabric_1_21_4") +include(":onejar:fabric_1_21_6") +include(":onejar:fabric_1_21_11") +include(":onejar:fabric_26_1") +include(":onejar:fabric_26_2") +include(":onejar:neoforge_1_21") +include(":onejar:neoforge_1_21_4") +include(":onejar:neoforge_1_21_6") +include(":onejar:neoforge_1_21_11") +include(":onejar:neoforge_26_1") pluginManagement { repositories { From 910c569ba7b2f738331e5dec143c46f088d6f8a0 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 19 Sep 2026 13:54:26 +0800 Subject: [PATCH 31/46] 26.3 --- build.gradle.kts | 2 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 2 +- .../allmusic/client/AllMusicClient.java | 2 +- .../allmusic/client/AllMusicClient.java | 2 +- .../allmusic/client/AllMusicClient.java | 2 +- .../src/main/resources/fabric.mod.json | 1 - client/fabric_26_3/build.gradle.kts | 34 + .../allmusic/client/AllMusicClient.java | 135 ++++ .../allmusic/client/CoreRenderTarget.java | 136 ++++ .../allmusic/client/FloatRenderState.java | 30 + .../coloryr/allmusic/client/MiniMessage.java | 645 ++++++++++++++++++ .../coloryr/allmusic/client/PicRender.java | 72 ++ .../coloryr/allmusic/client/TexRender.java | 109 +++ .../allmusic/client/mixins/GuiShow.java | 18 + .../allmusic/client/mixins/QuitServer.java | 16 + .../allmusic/client/mixins/Register.java | 16 + .../allmusic/client/mixins/SoundEvent.java | 38 ++ .../coloryr/allmusic/client/mixins/Tick.java | 16 + .../allmusic/client/others/VoiceChat.java | 32 + .../coloryr/allmusic/comm/AllMusicInit.java | 11 + .../com/coloryr/allmusic/comm/MusicCodec.java | 23 + .../resources/allmusic_client.mixins.json | 18 + .../resources/assets/allmusic_client/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 42 ++ .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 3 +- .../com/coloryr/allmusic/client/AllMusic.java | 5 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 2 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 3 +- .../allmusic/client/AllMusicClient.java | 2 +- .../allmusic/client/AllMusicClient.java | 2 +- client/neoforge_26_2/build.gradle.kts | 1 - .../allmusic/client/AllMusicClient.java | 4 +- client/neoforge_26_3/build.gradle.kts | 40 ++ client/neoforge_26_3/gradle.properties | 1 + .../allmusic/client/AllMusicClient.java | 193 ++++++ .../allmusic/client/CoreRenderTarget.java | 133 ++++ .../allmusic/client/FloatRenderState.java | 30 + .../coloryr/allmusic/client/MiniMessage.java | 645 ++++++++++++++++++ .../coloryr/allmusic/client/PicRender.java | 71 ++ .../coloryr/allmusic/client/TexRender.java | 109 +++ .../allmusic/client/others/ModernUI.java | 0 .../allmusic/client/others/VoiceChat.java | 34 + .../coloryr/allmusic/comm/AllMusicInit.java | 33 + .../com/coloryr/allmusic/comm/MusicCodec.java | 24 + .../resources/META-INF/neoforge.mods.toml | 42 ++ .../neoforge_26_3/src/main/resources/icon.png | Bin 0 -> 32781 bytes onejar/fabric_26_3/build.gradle.kts | 68 ++ .../main/resources/assets/allmusic/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 49 ++ onejar/neoforge_26_2/build.gradle.kts | 75 ++ onejar/neoforge_26_2/gradle.properties | 1 + .../resources/META-INF/neoforge.mods.toml | 71 ++ .../neoforge_26_2/src/main/resources/icon.png | Bin 0 -> 32781 bytes onejar/neoforge_26_3/build.gradle.kts | 75 ++ onejar/neoforge_26_3/gradle.properties | 1 + .../resources/META-INF/neoforge.mods.toml | 65 ++ .../neoforge_26_3/src/main/resources/icon.png | Bin 0 -> 32781 bytes server/fabric_26_3/build.gradle.kts | 37 + .../coloryr/allmusic/comm/AllMusicInit.java | 11 + .../com/coloryr/allmusic/comm/MusicCodec.java | 23 + .../allmusic/server/AllMusicServer.java | 33 + .../allmusic/server/CommandFabric.java | 69 ++ .../coloryr/allmusic/server/LogFabric.java | 12 + .../coloryr/allmusic/server/SideFabric.java | 157 +++++ .../com/coloryr/allmusic/server/TaskItem.java | 6 + .../com/coloryr/allmusic/server/Tasks.java | 27 + .../allmusic/server/event/MusicAddEvent.java | 24 + .../allmusic/server/event/MusicPlayEvent.java | 29 + .../server/mixin/ServerPlayerMixin.java | 25 + .../resources/allmusic_server.mixins.json | 17 + .../resources/assets/allmusic_server/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 37 + server/neoforge_26_3/build.gradle.kts | 51 ++ server/neoforge_26_3/gradle.properties | 1 + .../coloryr/allmusic/comm/AllMusicInit.java | 33 + .../com/coloryr/allmusic/comm/MusicCodec.java | 24 + .../allmusic/server/AllMusicServer.java | 74 ++ .../allmusic/server/CommandNeoForge.java | 66 ++ .../coloryr/allmusic/server/LogNeoForge.java | 11 + .../coloryr/allmusic/server/SideNeoForge.java | 161 +++++ .../com/coloryr/allmusic/server/TaskItem.java | 6 + .../com/coloryr/allmusic/server/Tasks.java | 27 + .../allmusic/server/event/MusicAddEvent.java | 39 ++ .../allmusic/server/event/MusicPlayEvent.java | 25 + .../resources/META-INF/neoforge.mods.toml | 36 + .../neoforge_26_3/src/main/resources/icon.png | Bin 0 -> 32781 bytes .../allmusic/server/core/music/PlayMusic.java | 2 +- settings.gradle.kts | 8 + 96 files changed, 4254 insertions(+), 25 deletions(-) create mode 100644 client/fabric_26_3/build.gradle.kts create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 client/fabric_26_3/src/main/resources/allmusic_client.mixins.json create mode 100644 client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png create mode 100644 client/fabric_26_3/src/main/resources/fabric.mod.json create mode 100644 client/neoforge_26_3/build.gradle.kts create mode 100644 client/neoforge_26_3/gradle.properties create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java rename client/{neoforge_26_2 => neoforge_26_3}/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java (100%) create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 client/neoforge_26_3/src/main/resources/icon.png create mode 100644 onejar/fabric_26_3/build.gradle.kts create mode 100644 onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png create mode 100644 onejar/fabric_26_3/src/main/resources/fabric.mod.json create mode 100644 onejar/neoforge_26_2/build.gradle.kts create mode 100644 onejar/neoforge_26_2/gradle.properties create mode 100644 onejar/neoforge_26_2/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 onejar/neoforge_26_2/src/main/resources/icon.png create mode 100644 onejar/neoforge_26_3/build.gradle.kts create mode 100644 onejar/neoforge_26_3/gradle.properties create mode 100644 onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 onejar/neoforge_26_3/src/main/resources/icon.png create mode 100644 server/fabric_26_3/build.gradle.kts create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java create mode 100644 server/fabric_26_3/src/main/resources/allmusic_server.mixins.json create mode 100644 server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png create mode 100644 server/fabric_26_3/src/main/resources/fabric.mod.json create mode 100644 server/neoforge_26_3/build.gradle.kts create mode 100644 server/neoforge_26_3/gradle.properties create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 server/neoforge_26_3/src/main/resources/icon.png diff --git a/build.gradle.kts b/build.gradle.kts index 9a3d0dec..546b1714 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.2.2" + version = "4.2.3" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 2d1f477d..3acfb136 100644 --- a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -69,7 +69,8 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index b40fd326..53f2ae8e 100644 --- a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -64,7 +64,8 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 31dcb7d6..a2826149 100644 --- a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -65,7 +65,8 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 8cd4f646..61ca28d8 100644 --- a/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -65,7 +65,8 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 8cd4f646..61ca28d8 100644 --- a/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -65,7 +65,8 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 5e9068cf..0dc948f7 100644 --- a/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -64,7 +64,7 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 5e9068cf..0dc948f7 100644 --- a/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -64,7 +64,7 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index c04442c0..1c9d88a2 100644 --- a/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -66,7 +66,7 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 3b22c550..cc5dd9bc 100644 --- a/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -65,7 +65,7 @@ public void sendMessage(String data) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/fabric_26_2/src/main/resources/fabric.mod.json b/client/fabric_26_2/src/main/resources/fabric.mod.json index 99bd6f13..16495270 100644 --- a/client/fabric_26_2/src/main/resources/fabric.mod.json +++ b/client/fabric_26_2/src/main/resources/fabric.mod.json @@ -37,7 +37,6 @@ "fabric-api": "*" }, "suggests": { - "modernui": "*", "voicechat_api": "*" } } \ No newline at end of file diff --git a/client/fabric_26_3/build.gradle.kts b/client/fabric_26_3/build.gradle.kts new file mode 100644 index 00000000..eb685e4a --- /dev/null +++ b/client/fabric_26_3/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("net.fabricmc.fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +dependencies { + minecraft("com.mojang:minecraft:26.3") + implementation("net.fabricmc:fabric-loader:0.19.5") + + implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") +} + +tasks { + processResources { + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + shadowJar { + archiveFileName.set("[fabric-26.3]AllMusic_Client-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java new file mode 100644 index 00000000..97073e42 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -0,0 +1,135 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicBridge; +import com.coloryr.allmusic.client.core.AllMusicCore; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.comm.MusicCodec; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.sounds.SoundSource; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class AllMusicClient implements ClientModInitializer, AllMusicBridge { + public static final String MODID = "allmusic_client"; + public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); + public static GuiGraphicsExtractor context; + + public static void update(GuiGraphicsExtractor draw) { + context = draw; + AllMusicCore.hudUpdate(); + } + + public int getScreenWidth() { + return Minecraft.getInstance().getWindow().getGuiScaledWidth(); + } + + public int getScreenHeight() { + return Minecraft.getInstance().getWindow().getGuiScaledHeight(); + } + + public int getTextWidth(String item) { + return Minecraft.getInstance().font.width(item); + } + + public int getFontHeight() { + return Minecraft.getInstance().font.lineHeight; + } + + public void sendMessage(String data) { + data = "[AllMusic Client]" + data; + LOGGER.warn(data); + String finalData = data; + Minecraft.getInstance().execute(() -> { + if (Minecraft.getInstance().player == null) + return; + Minecraft.getInstance().player.sendSystemMessage(Component.literal(finalData)); + }); + } + + public float getVolume() { + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); + } + + @Override + public void stopPlayMusic() { + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.MUSIC); + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.RECORDS); + } + + @Override + public TextFrameBuffer makeTextRender(String name) { + return new CoreRenderTarget(name); + } + + @Override + public TextureRender makeTextureRender(String file) { + return new TexRender(file); + } + + @Override + public PictureFrameBuffer makePictureRender(int size) { + return new PicRender(size); + } + + @Override + public String readText(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + try (InputStream inputStream = resource.open()) { + byte[] bytes = inputStream.readAllBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public InputStream readFile(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + return resource.open(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void kick() { + Minecraft client = Minecraft.getInstance(); + + ClientPacketListener packetListener = client.getConnection(); + if (packetListener != null) { + Connection connection = packetListener.getConnection(); + connection.disconnect(Component.nullToEmpty("Old AllMusic server")); + } + } + + @Override + public void onInitializeClient() { + ClientPlayNetworking.registerGlobalReceiver(MusicCodec.ID, (pack, handler) -> AllMusicCore.packDo(pack.pack())); + + AllMusicCore.init(FabricLoader.getInstance().getConfigDir(), this); + + ClientLifecycleEvents.CLIENT_STOPPING.register((a) -> AllMusicCore.stop()); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java new file mode 100644 index 00000000..9124fb0d --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -0,0 +1,136 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.network.chat.Component; + +import java.util.ArrayList; +import java.util.List; + +public class CoreRenderTarget extends TextFrameBuffer { + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); + } + + @Override + public void putText(String text, int y, int color, boolean shadow) { + color = (color & 0x00FFFFFF) | 0xFF000000; + Component component = MiniMessage.parse(text); + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { + return; + } + + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; + } + texts.add(new TextItem<>(width, height, y, component, shadow, color)); + } + + @Override + public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) getOffset(entry, maxWidth); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } + } + } + + @Override + public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } + if (line >= texts.size()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextItem entry = texts.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } + } + + @Override + public Point2f getLine(int line) { + if (line >= texts.size()) { + return new Point2f(0, 0); + } + TextItem entry = texts.get(line); + return new Point2f(entry.width, entry.height); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); + } +} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java new file mode 100644 index 00000000..d143b08b --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java @@ -0,0 +1,30 @@ +package com.coloryr.allmusic.client; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.renderpearl.api.pipeline.RenderPipeline; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.state.gui.GuiElementRenderState; +import org.joml.Matrix3x2f; + +public record FloatRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x0, + float y0, float x1, float y1, float u0, float u1, float v0, float v1, int color, + ScreenRectangle scissorArea, + ScreenRectangle bounds) implements GuiElementRenderState { + public FloatRenderState(RenderPipeline renderPipeline, TextureSetup textureSetup, Matrix3x2f matrix3x2f, + float i, float j, float k, float l, float f, float g, float h, float m, int n, ScreenRectangle screenRectangle) { + this(renderPipeline, textureSetup, matrix3x2f, i, j, k, l, f, g, h, m, n, screenRectangle, getBounds(i, j, k, l, matrix3x2f, screenRectangle)); + } + + private static ScreenRectangle getBounds(float i, float j, float k, float l, Matrix3x2f matrix3x2f, ScreenRectangle screenRectangle) { + ScreenRectangle screenRectangle2 = (new ScreenRectangle((int) Math.ceil(i + 1), (int) Math.ceil(j + 1), (int) Math.ceil(k - i + 1), (int) Math.ceil(l - j + 1))).transformMaxBounds(matrix3x2f); + return screenRectangle != null ? screenRectangle.intersection(screenRectangle2) : screenRectangle2; + } + + public void buildVertices(VertexConsumer vertexConsumer) { + vertexConsumer.addVertexWith2DPose(pose(), x0(), y0()).setUv(u0(), v0()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x0(), y1()).setUv(u0(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y1()).setUv(u1(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y0()).setUv(u1(), v0()).setColor(color()); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java new file mode 100644 index 00000000..88129399 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java @@ -0,0 +1,645 @@ +package com.coloryr.allmusic.client; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.*; +import net.minecraft.resources.Identifier; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MiniMessage { + // 正则表达式匹配标签 + private static final Pattern TAG_PATTERN = Pattern.compile( + "<(/?)(\\w+)(?:\\(([^)]+)\\))?(?::([^>]+))?>" + ); + + // 颜色映射表 + private static final Map COLOR_MAP = new HashMap<>(); + // 装饰器映射 + private static final Map DECORATION_MAP = new HashMap<>(); + + static { + COLOR_MAP.put("black", ChatFormatting.BLACK); + COLOR_MAP.put("dark_blue", ChatFormatting.DARK_BLUE); + COLOR_MAP.put("dark_green", ChatFormatting.DARK_GREEN); + COLOR_MAP.put("dark_aqua", ChatFormatting.DARK_AQUA); + COLOR_MAP.put("dark_red", ChatFormatting.DARK_RED); + COLOR_MAP.put("dark_purple", ChatFormatting.DARK_PURPLE); + COLOR_MAP.put("gold", ChatFormatting.GOLD); + COLOR_MAP.put("gray", ChatFormatting.GRAY); + COLOR_MAP.put("dark_gray", ChatFormatting.DARK_GRAY); + COLOR_MAP.put("blue", ChatFormatting.BLUE); + COLOR_MAP.put("green", ChatFormatting.GREEN); + COLOR_MAP.put("aqua", ChatFormatting.AQUA); + COLOR_MAP.put("red", ChatFormatting.RED); + COLOR_MAP.put("light_purple", ChatFormatting.LIGHT_PURPLE); + COLOR_MAP.put("yellow", ChatFormatting.YELLOW); + COLOR_MAP.put("white", ChatFormatting.WHITE); + } + + static { + DECORATION_MAP.put("bold", ChatFormatting.BOLD); + DECORATION_MAP.put("italic", ChatFormatting.ITALIC); + DECORATION_MAP.put("underlined", ChatFormatting.UNDERLINE); + DECORATION_MAP.put("strikethrough", ChatFormatting.STRIKETHROUGH); + DECORATION_MAP.put("obfuscated", ChatFormatting.OBFUSCATED); + } + + /** + * 解析 MiniMessage 字符串为 Component + */ + public static Component parse(String input) { + MutableComponent root = Component.literal(""); + Stack styleStack = new Stack<>(); + StyleContext currentStyle = new StyleContext(); + styleStack.push(currentStyle); + + StringBuilder textBuffer = new StringBuilder(); + int pos = 0; + int length = input.length(); + + while (pos < length) { + char c = input.charAt(pos); + + if (c == '<') { + // 先处理已积累的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + textBuffer.setLength(0); + } + + // 查找标签结束位置 + int endTag = findClosingTag(input, pos); + if (endTag == -1) { + // 没有找到闭合标签,当作普通文本处理 + textBuffer.append(c); + pos++; + continue; + } + + String tagContent = input.substring(pos, endTag + 1); + Matcher matcher = TAG_PATTERN.matcher(tagContent); + + if (matcher.matches()) { + boolean isClosing = !matcher.group(1).isEmpty(); + String tagName = matcher.group(2).toLowerCase(Locale.ROOT); + String params = matcher.group(3); + String value = matcher.group(4); + + if (isClosing) { + // 关闭标签 - 只关闭对应的开始标签 + String expectedOpenTag = matcher.group(2).toLowerCase(Locale.ROOT); + + // 查找对应的开始标签并关闭 + if (styleStack.size() > 1) { + // 从栈顶向下查找匹配的开始标签 + int index = styleStack.size() - 1; + boolean found = false; + + while (index >= 0) { + StyleContext ctx = styleStack.get(index); + if (ctx.tagName != null && ctx.tagName.equals(expectedOpenTag)) { + // 找到匹配的开始标签,关闭到这一层 + while (styleStack.size() > index + 1) { + styleStack.pop(); + } + currentStyle = styleStack.peek(); + found = true; + break; + } + index--; + } + + if (!found) { + // 没找到匹配的标签,忽略此关闭标签 + } + } + } else { + // 处理各种标签 + StyleContext newStyle = currentStyle.copy(); + + switch (tagName) { + case "color": + handleColorTag(newStyle, params, value); + break; + case "shadow": + handleShadowTag(newStyle, params, value); + break; + case "decoration": + handleDecorationTag(newStyle, params, value); + break; + case "reset": + newStyle = new StyleContext(); + break; + case "rainbow": + handleRainbowTag(newStyle, params, value); + break; + case "gradient": + handleGradientTag(newStyle, params, value); + break; + case "transition": + handleTransitionTag(newStyle, params, value); + break; + case "font": + handleFontTag(newStyle, params, value); + break; + default: + // 未知标签,忽略 + break; + } + + // 记录标签名称以便关闭时匹配 + newStyle.tagName = tagName; + styleStack.push(newStyle); + currentStyle = newStyle; + } + } else { + // 无效标签,当作普通文本 + textBuffer.append(tagContent); + } + + pos = endTag + 1; + } else { + textBuffer.append(c); + pos++; + } + } + + // 处理剩余的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + } + + return root; + } + + /** + * 处理颜色标签 + * 支持格式: , , + */ + private static void handleColorTag(StyleContext style, String params, String value) { + if (value != null) { + // 格式: 或 + TextColor textColor = parseColor(value); + if (textColor != null) { + style.color = textColor; + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } + } else if (params != null) { + // 格式: + if (params.equalsIgnoreCase("rgb")) { + String[] parts = value.split(","); + if (parts.length == 3) { + try { + int r = Integer.parseInt(parts[0].trim()); + int g = Integer.parseInt(parts[1].trim()); + int b = Integer.parseInt(parts[2].trim()); + style.color = TextColor.fromRgb((r << 16) | (g << 8) | b); + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } catch (NumberFormatException e) { + // 忽略解析错误 + } + } + } + } + } + + /** + * 处理阴影标签 + * 支持格式: , , , + */ + private static void handleShadowTag(StyleContext style, String params, String value) { + if (value == null) { + style.shadow = true; + } else if (value.equalsIgnoreCase("true")) { + style.shadow = true; + } else if (value.equalsIgnoreCase("false")) { + style.shadow = false; + } else { + // 自定义阴影颜色 + TextColor shadowColor = parseColor(value); + if (shadowColor != null) { + style.shadow = true; + style.shadowColor = shadowColor; + } + } + } + + /** + * 处理装饰标签 + * 支持格式: , , + */ + private static void handleDecorationTag(StyleContext style, String params, String value) { + if (params == null) return; + + String[] parts = params.split(":"); + String decorationName = parts[0].toLowerCase(Locale.ROOT); + boolean enabled = true; + + if (parts.length > 1) { + enabled = Boolean.parseBoolean(parts[1]); + } else if (value != null) { + enabled = Boolean.parseBoolean(value); + } + + ChatFormatting formatting = DECORATION_MAP.get(decorationName); + if (formatting != null) { + style.decorations.put(formatting, enabled); + } + } + + /** + * 处理彩虹色标签 + * 支持格式: , , , + */ + private static void handleRainbowTag(StyleContext style, String params, String value) { + float saturation = 1.0f; + float brightness = 1.0f; + + if (params != null) { + if (params.equalsIgnoreCase("saturation") && value != null) { + try { + saturation = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } else if (params.equalsIgnoreCase("brightness") && value != null) { + try { + brightness = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } + } + + style.rainbow = true; + style.gradient = false; + style.transition = false; + style.rainbowSaturation = saturation; + style.rainbowBrightness = brightness; + } + + /** + * 处理渐变色标签 + * 支持格式: , + * 渐变是逐字符过渡 + */ + private static void handleGradientTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = start; + style.gradientEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.gradientEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理过渡色标签 + * 支持格式: , + * 过渡是整个文本块的颜色过渡(类似整体渐变效果) + */ + private static void handleTransitionTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = start; + style.transitionEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.transitionEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理字体标签 + * 支持格式: , + */ + private static void handleFontTag(StyleContext style, String params, String value) { + String fontName = value != null ? value : params; + if (fontName != null) { + style.font = fontName; + } + } + + /** + * 解析颜色字符串 + * 支持: 颜色名称, #RRGGBB, #RGB, 十六进制数值 + */ + private static TextColor parseColor(String colorStr) { + if (colorStr == null) return null; + + // 检查是否为预定义颜色名称 + ChatFormatting formatting = COLOR_MAP.get(colorStr.toLowerCase(Locale.ROOT)); + if (formatting != null) { + return TextColor.fromLegacyFormat(formatting); + } + + // 检查十六进制格式 + if (colorStr.startsWith("#")) { + try { + if (colorStr.length() == 7) { + return TextColor.parseColor(colorStr).getOrThrow(); + } else if (colorStr.length() == 4) { + // 扩展 #RGB 到 #RRGGBB + String expanded = "#" + + colorStr.charAt(1) + colorStr.charAt(1) + + colorStr.charAt(2) + colorStr.charAt(2) + + colorStr.charAt(3) + colorStr.charAt(3); + return TextColor.parseColor(expanded).getOrThrow(); + } + } catch (Exception e) { + // 忽略解析错误 + } + } + + return null; + } + + /** + * 查找标签闭合位置 + */ + private static int findClosingTag(String input, int start) { + int pos = start + 1; + while (pos < input.length()) { + char c = input.charAt(pos); + if (c == '>') { + return pos; + } + pos++; + } + return -1; + } + + /** + * 添加文本到组件 + */ + private static void appendText(MutableComponent root, String text, StyleContext styleContext) { + if (text.isEmpty()) return; + + // 如果有渐变效果,需要逐字符处理 + if (styleContext.gradient && styleContext.gradientStart != null && styleContext.gradientEnd != null) { + appendGradientText(root, text, styleContext); + } + // 如果有过渡效果,需要逐字符处理 + else if (styleContext.transition && styleContext.transitionStart != null && styleContext.transitionEnd != null) { + appendTransitionText(root, text, styleContext); + } + // 如果有彩虹效果,需要逐字符处理 + else if (styleContext.rainbow) { + appendRainbowText(root, text, styleContext); + } + // 普通文本 + else { + MutableComponent component = Component.literal(text); + component.setStyle(styleContext.buildStyle()); + root.append(component); + } + } + + /** + * 带渐变效果的文本追加(逐字符颜色变化) + */ + private static void appendGradientText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float ratio = (float) i / (length - 1); + TextColor gradientColor = interpolateColor(styleContext.gradientStart, styleContext.gradientEnd, ratio); + + Style charStyle = baseStyle.withColor(gradientColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 带过渡效果的文本追加(整体颜色过渡) + */ + private static void appendTransitionText(MutableComponent root, String text, StyleContext styleContext) { + Style baseStyle = styleContext.buildBaseStyle(); + + // 过渡效果:为整个文本块创建一个颜色过渡 + MutableComponent transitionComponent = Component.literal(text); + + // 应用过渡样式(这里使用颜色插值,但也可以根据需求调整) + if (styleContext.transitionStart != null && styleContext.transitionEnd != null) { + // 可以使用整体渐变或者固定起始色 + transitionComponent.setStyle(baseStyle.withColor(styleContext.transitionStart)); + } else { + transitionComponent.setStyle(baseStyle); + } + + root.append(transitionComponent); + } + + /** + * 带彩虹效果的文本追加 + */ + private static void appendRainbowText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float hue = (float) i / length; + int rgb = java.awt.Color.HSBtoRGB(hue, styleContext.rainbowSaturation, styleContext.rainbowBrightness); + TextColor rainbowColor = TextColor.fromRgb(rgb & 0xFFFFFF); + + Style charStyle = baseStyle.withColor(rainbowColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 颜色插值 + */ + private static TextColor interpolateColor(TextColor start, TextColor end, float ratio) { + int startRgb = start.getValue(); + int endRgb = end.getValue(); + + int r1 = (startRgb >> 16) & 0xFF; + int g1 = (startRgb >> 8) & 0xFF; + int b1 = startRgb & 0xFF; + + int r2 = (endRgb >> 16) & 0xFF; + int g2 = (endRgb >> 8) & 0xFF; + int b2 = endRgb & 0xFF; + + int r = (int) (r1 + (r2 - r1) * ratio); + int g = (int) (g1 + (g2 - g1) * ratio); + int b = (int) (b1 + (b2 - b1) * ratio); + + return TextColor.fromRgb((r << 16) | (g << 8) | b); + } + + /** + * 样式上下文类,用于追踪嵌套样式 + */ + private static class StyleContext { + String tagName = null; // 记录标签名称,用于关闭匹配 + TextColor color = null; + Boolean shadow = null; + TextColor shadowColor = null; + Map decorations = new HashMap<>(); + boolean rainbow = false; + float rainbowSaturation = 1.0f; + float rainbowBrightness = 1.0f; + boolean gradient = false; + TextColor gradientStart = null; + TextColor gradientEnd = null; + boolean transition = false; + TextColor transitionStart = null; + TextColor transitionEnd = null; + String font = null; + + StyleContext copy() { + StyleContext copy = new StyleContext(); + copy.tagName = this.tagName; + copy.color = this.color; + copy.shadow = this.shadow; + copy.shadowColor = this.shadowColor; + copy.decorations.putAll(this.decorations); + copy.rainbow = this.rainbow; + copy.rainbowSaturation = this.rainbowSaturation; + copy.rainbowBrightness = this.rainbowBrightness; + copy.gradient = this.gradient; + copy.gradientStart = this.gradientStart; + copy.gradientEnd = this.gradientEnd; + copy.transition = this.transition; + copy.transitionStart = this.transitionStart; + copy.transitionEnd = this.transitionEnd; + copy.font = this.font; + return copy; + } + + /** + * 构建基础样式(不含颜色) + */ + Style buildBaseStyle() { + Style style = Style.EMPTY; + + // 应用装饰器 + for (Map.Entry entry : decorations.entrySet()) { + if (entry.getValue()) { + style = style.applyFormat(entry.getKey()); + } + } + + if (font != null) { + try { + style = style.withFont(new FontDescription.Resource(Identifier.parse(font))); + } catch (Exception e) { + // 忽略字体解析错误 + } + } + + return style; + } + + /** + * 构建完整样式(含颜色) + */ + Style buildStyle() { + Style style = buildBaseStyle(); + + // 只有在没有渐变、过渡和彩虹效果时才应用普通颜色 + if (!gradient && !transition && !rainbow && color != null) { + style = style.withColor(color); + } + + if (shadow != null && shadow) { + if (shadowColor != null) { + style = style.withShadowColor(shadowColor.getValue() | 0xFF000000); + } else { + style = style.withShadowColor(0xFF000000); + } + } + + return style; + } + } +} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java new file mode 100644 index 00000000..fca17cc4 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java @@ -0,0 +1,72 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.DynamicTexture; +import org.joml.Matrix3x2fStack; + +import java.io.ByteArrayInputStream; + +public class PicRender extends PictureFrameBuffer { + private final DynamicTexture sourceTexture; + private final DynamicTexture rotateTexture; + + public PicRender(int size) { + sourceTexture = new DynamicTexture("allmusic source", size, size, false); + rotateTexture = new DynamicTexture("allmusic rotate", size, size, false); + } + + @Override + public void update(byte[] source, byte[] rotate) { + try { + ByteArrayInputStream stream = new ByteArrayInputStream(source); + var image1 = NativeImage.read(stream); + sourceTexture.setPixels(image1); + sourceTexture.upload(); + + stream = new ByteArrayInputStream(rotate); + var image2 = NativeImage.read(stream); + rotateTexture.setPixels(image2); + rotateTexture.upload(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(size, size, x, y, dir); + + Matrix3x2fStack matrix = new Matrix3x2fStack(2); + + float a = (float) size / 2; + + if (rotate) { + matrix.translation(point.x + a, point.y + a); + matrix.pushMatrix().rotate((float) Math.toRadians(ang)); + } else { + matrix.translation(point.x + a, point.y + a); + } + + float x0 = -a; + float x1 = a; + float y0 = -a; + float y1 = a; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView(), rotate ? rotateTexture.getSampler() : sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java new file mode 100644 index 00000000..5ee542f5 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java @@ -0,0 +1,109 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.client.renderer.texture.TextureContents; +import net.minecraft.resources.Identifier; +import org.joml.Matrix3x2f; + +public class TexRender extends TextureRender { + private final SimpleTexture sourceTexture; + + public TexRender(String texture) { + super(texture); + Identifier location = Identifier.fromNamespaceAndPath(AllMusicClient.MODID, texture); + sourceTexture = new SimpleTexture(location); + + try { + TextureContents contents = sourceTexture.loadContents(Minecraft.getInstance().getResourceManager()); + + width = contents.image().getWidth(); + height = contents.image().getHeight(); + + sourceTexture.apply(contents); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void drawPic(float x, float y, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = (float) width / 2; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } + + @Override + public void drawPic(float x, float y, float width, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = ((float) this.width / 2) * width; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = width; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } + + @Override + public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(width, height, x, y, dir); + + float w1 = width / 2; + float h1 = height / 2; + + Matrix3x2f matrix = new Matrix3x2f().translation(point.x + w1, point.y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java new file mode 100644 index 00000000..7c1dcb83 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java @@ -0,0 +1,18 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.AllMusicClient; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.Hud; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Hud.class) +public class GuiShow { + @Inject(method = "extractRenderState", at = @At(value = "HEAD")) + public void guiShow(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) { + AllMusicClient.update(graphics); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java new file mode 100644 index 00000000..f77e15c4 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java @@ -0,0 +1,16 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class QuitServer { + @Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;ZZ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;resetData()V")) + public void quit(CallbackInfo info) { + AllMusicCore.onServerQuit(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java new file mode 100644 index 00000000..0d9d49ea --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java @@ -0,0 +1,16 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class Register { + @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketProcessor;(Ljava/lang/Thread;)V")) + public void register(CallbackInfo info) { + AllMusicCore.renderInit(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java new file mode 100644 index 00000000..5bd2ce83 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -0,0 +1,38 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.client.sounds.SoundEngine; +import net.minecraft.client.sounds.SoundManager; +import net.minecraft.sounds.SoundSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(SoundManager.class) +public class SoundEvent { + @Inject(method = "play", at = @At("HEAD"), cancellable = true) + public void play(SoundInstance sound, CallbackInfoReturnable cir) { + if (AllMusicCore.isPlay()) { + SoundSource data = sound.getSource(); + switch (data) { + case RECORDS, MUSIC: { + cir.cancel(); + cir.setReturnValue(SoundEngine.PlayResult.NOT_STARTED); + } + } + } + } + + @Inject(method = "playDelayed", at = @At("HEAD"), cancellable = true) + public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { + if (AllMusicCore.isPlay()) { + SoundSource data = soundInstance.getSource(); + switch (data) { + case RECORDS, MUSIC -> info.cancel(); + } + } + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java new file mode 100644 index 00000000..ff2cc10c --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java @@ -0,0 +1,16 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class Tick { + @Inject(method = "tick", at = @At("HEAD")) + public void tick(CallbackInfo info) { + AllMusicCore.tick(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java new file mode 100644 index 00000000..62eb1259 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java @@ -0,0 +1,32 @@ +package com.coloryr.allmusic.client.others; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import de.maxhenkel.voicechat.api.VoicechatPlugin; +import de.maxhenkel.voicechat.api.events.ClientReceiveSoundEvent; +import de.maxhenkel.voicechat.api.events.EventRegistration; + +public class VoiceChat implements VoicechatPlugin { + @Override + public String getPluginId() { + return "allmusic"; + } + + @Override + public void registerEvents(EventRegistration registration) { + registration.registerEvent(ClientReceiveSoundEvent.EntitySound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.LocationalSound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.StaticSound.class, this::play); + } + + public void play(ClientReceiveSoundEvent.EntitySound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.LocationalSound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.StaticSound event) { + AllMusicCore.chat(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..8d0e96d4 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.comm; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; + +public class AllMusicInit implements ModInitializer { + @Override + public void onInitialize() { + PayloadTypeRegistry.clientboundPlay().register(MusicCodec.ID, MusicCodec.CODEC); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..6b0cee70 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,23 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier ChannelID = Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type ID = new Type<>(ChannelID); + public static final StreamCodec CODEC = + StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + buf -> new MusicCodec(MusicPacketCodec.decode(buf))); + + @Override + public @NonNull Type type() { + return ID; + } +} diff --git a/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json b/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json new file mode 100644 index 00000000..2d2425a6 --- /dev/null +++ b/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "package": "com.coloryr.allmusic.client.mixins", + "compatibilityLevel": "JAVA_25", + "client": [ + "GuiShow", + "QuitServer", + "Register", + "SoundEvent", + "Tick" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png b/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/client/fabric_26_3/src/main/resources/fabric.mod.json b/client/fabric_26_3/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..06b7c0a8 --- /dev/null +++ b/client/fabric_26_3/src/main/resources/fabric.mod.json @@ -0,0 +1,42 @@ +{ + "schemaVersion": 1, + "id": "allmusic_client", + "version": "${version}", + "name": "AllMusic Client", + "description": "AllMusic Client Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic_client/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit" + ], + "client": [ + "com.coloryr.allmusic.client.AllMusicClient" + ], + "voicechat": [ + "com.coloryr.allmusic.client.others.VoiceChat" + ] + }, + "mixins": [ + { + "config": "allmusic_client.mixins.json", + "environment": "client" + } + ], + "depends": { + "fabricloader": ">=0.19.5", + "minecraft": ">=26.3", + "fabric-api": "*" + }, + "suggests": { + "voicechat_api": "*" + } +} \ No newline at end of file diff --git a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index cc00281f..6b029639 100644 --- a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -127,7 +127,8 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.RECORDS); + return Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.RECORDS) + * Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.MASTER); } @Override diff --git a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 100500c7..b9388a1d 100644 --- a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -118,7 +118,8 @@ public void onServerQuit(final ClientPlayerNetworkEvent.LoggingOut e) { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @SubscribeEvent diff --git a/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java b/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java index 273a568a..2b76b382 100644 --- a/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java +++ b/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java @@ -145,7 +145,7 @@ public void stopPlayMusic() { } @Override - public TextFrameBuffer makeTextRender(String name) { + public TextFrameBuffer makeTextRender(String name) { return new CoreRenderTarget(name); } @@ -218,7 +218,8 @@ public void onTick(final TickEvent.ClientTickEvent event) { } public float getVolume() { - return Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.RECORDS); + return Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.RECORDS) + * Minecraft.getMinecraft().gameSettings.getSoundLevel(SoundCategory.MASTER); } @Override diff --git a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index bff92c27..ed56ac5c 100644 --- a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -93,7 +93,8 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 0f0b373c..6dac142f 100644 --- a/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -134,7 +134,7 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index e9e710f7..986c2888 100644 --- a/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -92,7 +92,8 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index e9e710f7..986c2888 100644 --- a/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -92,7 +92,8 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS) + * Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.MASTER); } @Override diff --git a/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index d1d79080..311fc18c 100644 --- a/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -128,7 +128,7 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index c2eed088..e8f1067d 100644 --- a/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -135,7 +135,7 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override diff --git a/client/neoforge_26_2/build.gradle.kts b/client/neoforge_26_2/build.gradle.kts index d9e38334..af44dd08 100644 --- a/client/neoforge_26_2/build.gradle.kts +++ b/client/neoforge_26_2/build.gradle.kts @@ -19,7 +19,6 @@ dependencies { minecraft("com.mojang:minecraft:26.2") neoForge("net.neoforged:neoforge:26.2.0.1-beta") -// compileOnly("icyllis.modernui:ModernUI-NeoForge:26.1.2-3.13.0.4") compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") } diff --git a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index c2eed088..f864acbd 100644 --- a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -135,11 +135,11 @@ public int getFontHeight() { } public float getVolume() { - return Minecraft.getInstance().options.getSoundSourceVolume(SoundSource.RECORDS); + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); } @Override - public TextFrameBuffer makeTextRender(String name) { + public TextFrameBuffer makeTextRender(String name) { return new CoreRenderTarget(name); } diff --git a/client/neoforge_26_3/build.gradle.kts b/client/neoforge_26_3/build.gradle.kts new file mode 100644 index 00000000..aa89b82c --- /dev/null +++ b/client/neoforge_26_3/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + neoForge("net.neoforged:neoforge:26.3.0.6-beta") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") +} + +tasks { + processResources { + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + shadowJar { + archiveFileName.set("[neoforge-26.3]AllMusic_Client-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/client/neoforge_26_3/gradle.properties b/client/neoforge_26_3/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/client/neoforge_26_3/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java new file mode 100644 index 00000000..e8f1067d --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -0,0 +1,193 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicBridge; +import com.coloryr.allmusic.client.core.AllMusicCore; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.comm.AllMusicInit; +import com.coloryr.allmusic.comm.MusicCodec; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.sounds.SoundSource; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModList; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; +import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.neoforge.client.event.RenderGuiLayerEvent; +import net.neoforged.neoforge.client.event.sound.PlaySoundSourceEvent; +import net.neoforged.neoforge.client.event.sound.PlayStreamingSourceEvent; +import net.neoforged.neoforge.client.event.sound.SoundEngineLoadEvent; +import net.neoforged.neoforge.client.gui.VanillaGuiLayers; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +@EventBusSubscriber(modid = AllMusicClient.MODID, value = Dist.CLIENT) +public class AllMusicClient implements IPayloadHandler, AllMusicBridge { + public static final String MODID = "allmusic_client"; + public static boolean modui; + public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Client"); + public static GuiGraphicsExtractor context; + + @SubscribeEvent + public static void setup(final FMLClientSetupEvent event) { + AllMusicClient client = new AllMusicClient(); + AllMusicInit.handler = client; + AllMusicCore.init(FMLPaths.CONFIGDIR.get(), client); + + Minecraft.getInstance().execute(AllMusicCore::renderInit); + + if (ModList.get().isLoaded("modernui")) { + modui = true; + } + } + + @SubscribeEvent + public static void onSound(final PlaySoundSourceEvent e) { + if (!AllMusicCore.isPlay()) return; + SoundSource data = e.getSound().getSource(); + switch (data) { + case MUSIC, RECORDS -> e.getChannel().stop(); + } + } + + @SubscribeEvent + public static void onSound(final PlayStreamingSourceEvent e) { + if (!AllMusicCore.isPlay()) return; + SoundSource data = e.getSound().getSource(); + switch (data) { + case MUSIC, RECORDS -> e.getChannel().stop(); + } + } + + @SubscribeEvent + public static void onServerQuit(final ClientPlayerNetworkEvent.LoggingOut e) { + AllMusicCore.onServerQuit(); + } + + @SubscribeEvent + public static void onRenderOverlay(RenderGuiLayerEvent.Post e) { + if (e.getName().equals(VanillaGuiLayers.CAMERA_OVERLAYS)) { + context = e.getGuiGraphics(); + AllMusicCore.hudUpdate(); + } + } + + @SubscribeEvent + public static void onTick(ClientTickEvent.Post event) { + AllMusicCore.tick(); + } + + public void sendMessage(String data) { + data = "[AllMusic Client]" + data; + LOGGER.warn(data); + String finalData = data; + Minecraft.getInstance().execute(() -> { + if (Minecraft.getInstance().player == null) + return; + Minecraft.getInstance().player.sendSystemMessage(Component.literal(finalData)); + }); + } + + @Override + public void handle(MusicCodec pack, IPayloadContext iPayloadContext) { + try { + AllMusicCore.packDo(pack.pack()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void stopPlayMusic() { + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.MUSIC); + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.RECORDS); + } + + public int getScreenWidth() { + return Minecraft.getInstance().getWindow().getGuiScaledWidth(); + } + + public int getScreenHeight() { + return Minecraft.getInstance().getWindow().getGuiScaledHeight(); + } + + public int getTextWidth(String item) { + return Minecraft.getInstance().font.width(item); + } + + public int getFontHeight() { + return Minecraft.getInstance().font.lineHeight; + } + + public float getVolume() { + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); + } + + @Override + public TextFrameBuffer makeTextRender(String name) { + return new CoreRenderTarget(name); + } + + @Override + public TextureRender makeTextureRender(String file) { + return new TexRender(file); + } + + @Override + public PictureFrameBuffer makePictureRender(int size) { + return new PicRender(size); + } + + @Override + public String readText(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + try (InputStream inputStream = resource.open()) { + byte[] bytes = inputStream.readAllBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public InputStream readFile(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + return resource.open(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void kick() { + Minecraft client = Minecraft.getInstance(); + + ClientPacketListener packetListener = client.getConnection(); + if (packetListener != null) { + Connection connection = packetListener.getConnection(); + connection.disconnect(Component.nullToEmpty("Old AllMusic server")); + } + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java new file mode 100644 index 00000000..fe90072e --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -0,0 +1,133 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.network.chat.Component; + +public class CoreRenderTarget extends TextFrameBuffer { + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); + } + + @Override + public void putText(String text, int y, int color, boolean shadow) { + color = (color & 0x00FFFFFF) | 0xFF000000; + Component component = MiniMessage.parse(text); + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { + return; + } + + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; + } + texts.add(new TextItem<>(width, height, y, component, shadow, color)); + } + + @Override + public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) getOffset(entry, maxWidth); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } + } + } + + @Override + public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } + if (line >= texts.size()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextItem entry = texts.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } + } + + @Override + public Point2f getLine(int line) { + if (line >= texts.size()) { + return new Point2f(0, 0); + } + TextItem entry = texts.get(line); + return new Point2f(entry.width, entry.height); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); + } +} \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java new file mode 100644 index 00000000..d143b08b --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java @@ -0,0 +1,30 @@ +package com.coloryr.allmusic.client; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.renderpearl.api.pipeline.RenderPipeline; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.state.gui.GuiElementRenderState; +import org.joml.Matrix3x2f; + +public record FloatRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x0, + float y0, float x1, float y1, float u0, float u1, float v0, float v1, int color, + ScreenRectangle scissorArea, + ScreenRectangle bounds) implements GuiElementRenderState { + public FloatRenderState(RenderPipeline renderPipeline, TextureSetup textureSetup, Matrix3x2f matrix3x2f, + float i, float j, float k, float l, float f, float g, float h, float m, int n, ScreenRectangle screenRectangle) { + this(renderPipeline, textureSetup, matrix3x2f, i, j, k, l, f, g, h, m, n, screenRectangle, getBounds(i, j, k, l, matrix3x2f, screenRectangle)); + } + + private static ScreenRectangle getBounds(float i, float j, float k, float l, Matrix3x2f matrix3x2f, ScreenRectangle screenRectangle) { + ScreenRectangle screenRectangle2 = (new ScreenRectangle((int) Math.ceil(i + 1), (int) Math.ceil(j + 1), (int) Math.ceil(k - i + 1), (int) Math.ceil(l - j + 1))).transformMaxBounds(matrix3x2f); + return screenRectangle != null ? screenRectangle.intersection(screenRectangle2) : screenRectangle2; + } + + public void buildVertices(VertexConsumer vertexConsumer) { + vertexConsumer.addVertexWith2DPose(pose(), x0(), y0()).setUv(u0(), v0()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x0(), y1()).setUv(u0(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y1()).setUv(u1(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y0()).setUv(u1(), v0()).setColor(color()); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java new file mode 100644 index 00000000..88129399 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java @@ -0,0 +1,645 @@ +package com.coloryr.allmusic.client; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.*; +import net.minecraft.resources.Identifier; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MiniMessage { + // 正则表达式匹配标签 + private static final Pattern TAG_PATTERN = Pattern.compile( + "<(/?)(\\w+)(?:\\(([^)]+)\\))?(?::([^>]+))?>" + ); + + // 颜色映射表 + private static final Map COLOR_MAP = new HashMap<>(); + // 装饰器映射 + private static final Map DECORATION_MAP = new HashMap<>(); + + static { + COLOR_MAP.put("black", ChatFormatting.BLACK); + COLOR_MAP.put("dark_blue", ChatFormatting.DARK_BLUE); + COLOR_MAP.put("dark_green", ChatFormatting.DARK_GREEN); + COLOR_MAP.put("dark_aqua", ChatFormatting.DARK_AQUA); + COLOR_MAP.put("dark_red", ChatFormatting.DARK_RED); + COLOR_MAP.put("dark_purple", ChatFormatting.DARK_PURPLE); + COLOR_MAP.put("gold", ChatFormatting.GOLD); + COLOR_MAP.put("gray", ChatFormatting.GRAY); + COLOR_MAP.put("dark_gray", ChatFormatting.DARK_GRAY); + COLOR_MAP.put("blue", ChatFormatting.BLUE); + COLOR_MAP.put("green", ChatFormatting.GREEN); + COLOR_MAP.put("aqua", ChatFormatting.AQUA); + COLOR_MAP.put("red", ChatFormatting.RED); + COLOR_MAP.put("light_purple", ChatFormatting.LIGHT_PURPLE); + COLOR_MAP.put("yellow", ChatFormatting.YELLOW); + COLOR_MAP.put("white", ChatFormatting.WHITE); + } + + static { + DECORATION_MAP.put("bold", ChatFormatting.BOLD); + DECORATION_MAP.put("italic", ChatFormatting.ITALIC); + DECORATION_MAP.put("underlined", ChatFormatting.UNDERLINE); + DECORATION_MAP.put("strikethrough", ChatFormatting.STRIKETHROUGH); + DECORATION_MAP.put("obfuscated", ChatFormatting.OBFUSCATED); + } + + /** + * 解析 MiniMessage 字符串为 Component + */ + public static Component parse(String input) { + MutableComponent root = Component.literal(""); + Stack styleStack = new Stack<>(); + StyleContext currentStyle = new StyleContext(); + styleStack.push(currentStyle); + + StringBuilder textBuffer = new StringBuilder(); + int pos = 0; + int length = input.length(); + + while (pos < length) { + char c = input.charAt(pos); + + if (c == '<') { + // 先处理已积累的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + textBuffer.setLength(0); + } + + // 查找标签结束位置 + int endTag = findClosingTag(input, pos); + if (endTag == -1) { + // 没有找到闭合标签,当作普通文本处理 + textBuffer.append(c); + pos++; + continue; + } + + String tagContent = input.substring(pos, endTag + 1); + Matcher matcher = TAG_PATTERN.matcher(tagContent); + + if (matcher.matches()) { + boolean isClosing = !matcher.group(1).isEmpty(); + String tagName = matcher.group(2).toLowerCase(Locale.ROOT); + String params = matcher.group(3); + String value = matcher.group(4); + + if (isClosing) { + // 关闭标签 - 只关闭对应的开始标签 + String expectedOpenTag = matcher.group(2).toLowerCase(Locale.ROOT); + + // 查找对应的开始标签并关闭 + if (styleStack.size() > 1) { + // 从栈顶向下查找匹配的开始标签 + int index = styleStack.size() - 1; + boolean found = false; + + while (index >= 0) { + StyleContext ctx = styleStack.get(index); + if (ctx.tagName != null && ctx.tagName.equals(expectedOpenTag)) { + // 找到匹配的开始标签,关闭到这一层 + while (styleStack.size() > index + 1) { + styleStack.pop(); + } + currentStyle = styleStack.peek(); + found = true; + break; + } + index--; + } + + if (!found) { + // 没找到匹配的标签,忽略此关闭标签 + } + } + } else { + // 处理各种标签 + StyleContext newStyle = currentStyle.copy(); + + switch (tagName) { + case "color": + handleColorTag(newStyle, params, value); + break; + case "shadow": + handleShadowTag(newStyle, params, value); + break; + case "decoration": + handleDecorationTag(newStyle, params, value); + break; + case "reset": + newStyle = new StyleContext(); + break; + case "rainbow": + handleRainbowTag(newStyle, params, value); + break; + case "gradient": + handleGradientTag(newStyle, params, value); + break; + case "transition": + handleTransitionTag(newStyle, params, value); + break; + case "font": + handleFontTag(newStyle, params, value); + break; + default: + // 未知标签,忽略 + break; + } + + // 记录标签名称以便关闭时匹配 + newStyle.tagName = tagName; + styleStack.push(newStyle); + currentStyle = newStyle; + } + } else { + // 无效标签,当作普通文本 + textBuffer.append(tagContent); + } + + pos = endTag + 1; + } else { + textBuffer.append(c); + pos++; + } + } + + // 处理剩余的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + } + + return root; + } + + /** + * 处理颜色标签 + * 支持格式: , , + */ + private static void handleColorTag(StyleContext style, String params, String value) { + if (value != null) { + // 格式: 或 + TextColor textColor = parseColor(value); + if (textColor != null) { + style.color = textColor; + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } + } else if (params != null) { + // 格式: + if (params.equalsIgnoreCase("rgb")) { + String[] parts = value.split(","); + if (parts.length == 3) { + try { + int r = Integer.parseInt(parts[0].trim()); + int g = Integer.parseInt(parts[1].trim()); + int b = Integer.parseInt(parts[2].trim()); + style.color = TextColor.fromRgb((r << 16) | (g << 8) | b); + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } catch (NumberFormatException e) { + // 忽略解析错误 + } + } + } + } + } + + /** + * 处理阴影标签 + * 支持格式: , , , + */ + private static void handleShadowTag(StyleContext style, String params, String value) { + if (value == null) { + style.shadow = true; + } else if (value.equalsIgnoreCase("true")) { + style.shadow = true; + } else if (value.equalsIgnoreCase("false")) { + style.shadow = false; + } else { + // 自定义阴影颜色 + TextColor shadowColor = parseColor(value); + if (shadowColor != null) { + style.shadow = true; + style.shadowColor = shadowColor; + } + } + } + + /** + * 处理装饰标签 + * 支持格式: , , + */ + private static void handleDecorationTag(StyleContext style, String params, String value) { + if (params == null) return; + + String[] parts = params.split(":"); + String decorationName = parts[0].toLowerCase(Locale.ROOT); + boolean enabled = true; + + if (parts.length > 1) { + enabled = Boolean.parseBoolean(parts[1]); + } else if (value != null) { + enabled = Boolean.parseBoolean(value); + } + + ChatFormatting formatting = DECORATION_MAP.get(decorationName); + if (formatting != null) { + style.decorations.put(formatting, enabled); + } + } + + /** + * 处理彩虹色标签 + * 支持格式: , , , + */ + private static void handleRainbowTag(StyleContext style, String params, String value) { + float saturation = 1.0f; + float brightness = 1.0f; + + if (params != null) { + if (params.equalsIgnoreCase("saturation") && value != null) { + try { + saturation = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } else if (params.equalsIgnoreCase("brightness") && value != null) { + try { + brightness = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } + } + + style.rainbow = true; + style.gradient = false; + style.transition = false; + style.rainbowSaturation = saturation; + style.rainbowBrightness = brightness; + } + + /** + * 处理渐变色标签 + * 支持格式: , + * 渐变是逐字符过渡 + */ + private static void handleGradientTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = start; + style.gradientEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.gradientEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理过渡色标签 + * 支持格式: , + * 过渡是整个文本块的颜色过渡(类似整体渐变效果) + */ + private static void handleTransitionTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = start; + style.transitionEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.transitionEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理字体标签 + * 支持格式: , + */ + private static void handleFontTag(StyleContext style, String params, String value) { + String fontName = value != null ? value : params; + if (fontName != null) { + style.font = fontName; + } + } + + /** + * 解析颜色字符串 + * 支持: 颜色名称, #RRGGBB, #RGB, 十六进制数值 + */ + private static TextColor parseColor(String colorStr) { + if (colorStr == null) return null; + + // 检查是否为预定义颜色名称 + ChatFormatting formatting = COLOR_MAP.get(colorStr.toLowerCase(Locale.ROOT)); + if (formatting != null) { + return TextColor.fromLegacyFormat(formatting); + } + + // 检查十六进制格式 + if (colorStr.startsWith("#")) { + try { + if (colorStr.length() == 7) { + return TextColor.parseColor(colorStr).getOrThrow(); + } else if (colorStr.length() == 4) { + // 扩展 #RGB 到 #RRGGBB + String expanded = "#" + + colorStr.charAt(1) + colorStr.charAt(1) + + colorStr.charAt(2) + colorStr.charAt(2) + + colorStr.charAt(3) + colorStr.charAt(3); + return TextColor.parseColor(expanded).getOrThrow(); + } + } catch (Exception e) { + // 忽略解析错误 + } + } + + return null; + } + + /** + * 查找标签闭合位置 + */ + private static int findClosingTag(String input, int start) { + int pos = start + 1; + while (pos < input.length()) { + char c = input.charAt(pos); + if (c == '>') { + return pos; + } + pos++; + } + return -1; + } + + /** + * 添加文本到组件 + */ + private static void appendText(MutableComponent root, String text, StyleContext styleContext) { + if (text.isEmpty()) return; + + // 如果有渐变效果,需要逐字符处理 + if (styleContext.gradient && styleContext.gradientStart != null && styleContext.gradientEnd != null) { + appendGradientText(root, text, styleContext); + } + // 如果有过渡效果,需要逐字符处理 + else if (styleContext.transition && styleContext.transitionStart != null && styleContext.transitionEnd != null) { + appendTransitionText(root, text, styleContext); + } + // 如果有彩虹效果,需要逐字符处理 + else if (styleContext.rainbow) { + appendRainbowText(root, text, styleContext); + } + // 普通文本 + else { + MutableComponent component = Component.literal(text); + component.setStyle(styleContext.buildStyle()); + root.append(component); + } + } + + /** + * 带渐变效果的文本追加(逐字符颜色变化) + */ + private static void appendGradientText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float ratio = (float) i / (length - 1); + TextColor gradientColor = interpolateColor(styleContext.gradientStart, styleContext.gradientEnd, ratio); + + Style charStyle = baseStyle.withColor(gradientColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 带过渡效果的文本追加(整体颜色过渡) + */ + private static void appendTransitionText(MutableComponent root, String text, StyleContext styleContext) { + Style baseStyle = styleContext.buildBaseStyle(); + + // 过渡效果:为整个文本块创建一个颜色过渡 + MutableComponent transitionComponent = Component.literal(text); + + // 应用过渡样式(这里使用颜色插值,但也可以根据需求调整) + if (styleContext.transitionStart != null && styleContext.transitionEnd != null) { + // 可以使用整体渐变或者固定起始色 + transitionComponent.setStyle(baseStyle.withColor(styleContext.transitionStart)); + } else { + transitionComponent.setStyle(baseStyle); + } + + root.append(transitionComponent); + } + + /** + * 带彩虹效果的文本追加 + */ + private static void appendRainbowText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float hue = (float) i / length; + int rgb = java.awt.Color.HSBtoRGB(hue, styleContext.rainbowSaturation, styleContext.rainbowBrightness); + TextColor rainbowColor = TextColor.fromRgb(rgb & 0xFFFFFF); + + Style charStyle = baseStyle.withColor(rainbowColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 颜色插值 + */ + private static TextColor interpolateColor(TextColor start, TextColor end, float ratio) { + int startRgb = start.getValue(); + int endRgb = end.getValue(); + + int r1 = (startRgb >> 16) & 0xFF; + int g1 = (startRgb >> 8) & 0xFF; + int b1 = startRgb & 0xFF; + + int r2 = (endRgb >> 16) & 0xFF; + int g2 = (endRgb >> 8) & 0xFF; + int b2 = endRgb & 0xFF; + + int r = (int) (r1 + (r2 - r1) * ratio); + int g = (int) (g1 + (g2 - g1) * ratio); + int b = (int) (b1 + (b2 - b1) * ratio); + + return TextColor.fromRgb((r << 16) | (g << 8) | b); + } + + /** + * 样式上下文类,用于追踪嵌套样式 + */ + private static class StyleContext { + String tagName = null; // 记录标签名称,用于关闭匹配 + TextColor color = null; + Boolean shadow = null; + TextColor shadowColor = null; + Map decorations = new HashMap<>(); + boolean rainbow = false; + float rainbowSaturation = 1.0f; + float rainbowBrightness = 1.0f; + boolean gradient = false; + TextColor gradientStart = null; + TextColor gradientEnd = null; + boolean transition = false; + TextColor transitionStart = null; + TextColor transitionEnd = null; + String font = null; + + StyleContext copy() { + StyleContext copy = new StyleContext(); + copy.tagName = this.tagName; + copy.color = this.color; + copy.shadow = this.shadow; + copy.shadowColor = this.shadowColor; + copy.decorations.putAll(this.decorations); + copy.rainbow = this.rainbow; + copy.rainbowSaturation = this.rainbowSaturation; + copy.rainbowBrightness = this.rainbowBrightness; + copy.gradient = this.gradient; + copy.gradientStart = this.gradientStart; + copy.gradientEnd = this.gradientEnd; + copy.transition = this.transition; + copy.transitionStart = this.transitionStart; + copy.transitionEnd = this.transitionEnd; + copy.font = this.font; + return copy; + } + + /** + * 构建基础样式(不含颜色) + */ + Style buildBaseStyle() { + Style style = Style.EMPTY; + + // 应用装饰器 + for (Map.Entry entry : decorations.entrySet()) { + if (entry.getValue()) { + style = style.applyFormat(entry.getKey()); + } + } + + if (font != null) { + try { + style = style.withFont(new FontDescription.Resource(Identifier.parse(font))); + } catch (Exception e) { + // 忽略字体解析错误 + } + } + + return style; + } + + /** + * 构建完整样式(含颜色) + */ + Style buildStyle() { + Style style = buildBaseStyle(); + + // 只有在没有渐变、过渡和彩虹效果时才应用普通颜色 + if (!gradient && !transition && !rainbow && color != null) { + style = style.withColor(color); + } + + if (shadow != null && shadow) { + if (shadowColor != null) { + style = style.withShadowColor(shadowColor.getValue() | 0xFF000000); + } else { + style = style.withShadowColor(0xFF000000); + } + } + + return style; + } + } +} \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java new file mode 100644 index 00000000..51fa9e23 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java @@ -0,0 +1,71 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import com.mojang.blaze3d.platform.NativeImage; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.DynamicTexture; +import org.joml.Matrix3x2fStack; + +import java.io.ByteArrayInputStream; + +public class PicRender extends PictureFrameBuffer { + private final DynamicTexture sourceTexture; + private final DynamicTexture rotateTexture; + + public PicRender(int size) { + sourceTexture = new DynamicTexture("allmusic source", size, size, false); + rotateTexture = new DynamicTexture("allmusic rotate", size, size, false); + } + + @Override + public void update(byte[] source, byte[] rotate) { + try { + ByteArrayInputStream stream = new ByteArrayInputStream(source); + var image1 = NativeImage.read(stream); + sourceTexture.setPixels(image1); + sourceTexture.upload(); + + stream = new ByteArrayInputStream(rotate); + var image2 = NativeImage.read(stream); + rotateTexture.setPixels(image2); + rotateTexture.upload(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(size, size, x, y, dir); + + Matrix3x2fStack matrix = new Matrix3x2fStack(2); + + float a = (float) size / 2; + + if (rotate) { + matrix.translation(point.x + a, point.y + a); + matrix.pushMatrix().rotate((float) Math.toRadians(ang)); + } else { + matrix.translation(point.x + a, point.y + a); + } + + float x0 = -a; + float x1 = a; + float y0 = -a; + float y1 = a; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView(), rotate ? rotateTexture.getSampler() : sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java new file mode 100644 index 00000000..90c89199 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java @@ -0,0 +1,109 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.client.renderer.texture.TextureContents; +import net.minecraft.resources.Identifier; +import org.joml.Matrix3x2f; + +public class TexRender extends TextureRender { + private final SimpleTexture sourceTexture; + + public TexRender(String texture) { + super(texture); + Identifier location = Identifier.fromNamespaceAndPath(AllMusicClient.MODID, texture); + sourceTexture = new SimpleTexture(location); + + try { + TextureContents contents = sourceTexture.loadContents(Minecraft.getInstance().getResourceManager()); + + width = contents.image().getWidth(); + height = contents.image().getHeight(); + + sourceTexture.apply(contents); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void drawPic(float x, float y, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = (float) width / 2; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } + + @Override + public void drawPic(float x, float y, float width, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = ((float) this.width / 2) * width; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = width; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } + + @Override + public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(width, height, x, y, dir); + + float w1 = width / 2; + float h1 = height / 2; + + Matrix3x2f matrix = new Matrix3x2f().translation(point.x + w1, point.y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } +} diff --git a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java similarity index 100% rename from client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java rename to client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java new file mode 100644 index 00000000..9caead22 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java @@ -0,0 +1,34 @@ +package com.coloryr.allmusic.client.others; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import de.maxhenkel.voicechat.api.ForgeVoicechatPlugin; +import de.maxhenkel.voicechat.api.VoicechatPlugin; +import de.maxhenkel.voicechat.api.events.ClientReceiveSoundEvent; +import de.maxhenkel.voicechat.api.events.EventRegistration; + +@ForgeVoicechatPlugin +public class VoiceChat implements VoicechatPlugin { + @Override + public String getPluginId() { + return "allmusic"; + } + + @Override + public void registerEvents(EventRegistration registration) { + registration.registerEvent(ClientReceiveSoundEvent.EntitySound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.LocationalSound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.StaticSound.class, this::play); + } + + public void play(ClientReceiveSoundEvent.EntitySound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.LocationalSound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.StaticSound event) { + AllMusicCore.chat(); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..d9282960 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.comm; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +@Mod(AllMusicInit.MODID) +public class AllMusicInit implements IPayloadHandler { + + public static final String MODID = "allmusic"; + + public static IPayloadHandler handler; + + public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { + modEventBus.addListener(this::register); + } + + public void register(final RegisterPayloadHandlersEvent event) { + event.registrar("1.0") + .optional() + .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); + } + + @Override + public void handle(MusicCodec data, IPayloadContext iPayloadContext) { + if (handler != null) { + handler.handle(data, iPayloadContext); + } + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..434fa510 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier channel = + Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type TYPE = new Type<>(channel); + public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + (buf) -> { + return new MusicCodec(MusicPacketCodec.decode(buf)); + }); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..544e45e7 --- /dev/null +++ b/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,42 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_client" +version = "${version}" +displayName = "AllMusic Client" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Client Mod''' + +[[dependencies.allmusic_client]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId="voicechat_api" +type="optional" +ordering="AFTER" +side="CLIENT" \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/resources/icon.png b/client/neoforge_26_3/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/onejar/fabric_26_3/build.gradle.kts b/onejar/fabric_26_3/build.gradle.kts new file mode 100644 index 00000000..c109d33a --- /dev/null +++ b/onejar/fabric_26_3/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + id("net.fabricmc.fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:fabric_26_3").projectDir.resolve("src/main/resources") +val serverResDir = project(":server:fabric_26_3").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + serverResDir + )) + } + } +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + implementation("net.fabricmc:fabric-loader:0.19.5") + + implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") + + implementation(include("net.kyori:adventure-platform-fabric:7.2.0")!!) +} + +tasks { + processResources { + // onejar 自己的 fabric.mod.json 优先(排在 srcDirs 第一位), + // client/server 的 fabric.mod.json 作为重复项被排除 + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + + shadowJar { + archiveFileName.set("[fabric-26.3]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + +// relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") +// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") + } + + build { + dependsOn(shadowJar) + } +} diff --git a/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png b/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/onejar/fabric_26_3/src/main/resources/fabric.mod.json b/onejar/fabric_26_3/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..6f009cfc --- /dev/null +++ b/onejar/fabric_26_3/src/main/resources/fabric.mod.json @@ -0,0 +1,49 @@ +{ + "schemaVersion": 1, + "id": "allmusic", + "version": "${version}", + "name": "AllMusic", + "description": "AllMusic Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit", + "com.coloryr.allmusic.server.AllMusicServer" + ], + "client": [ + "com.coloryr.allmusic.client.AllMusicClient" + ], + "voicechat": [ + "com.coloryr.allmusic.client.others.VoiceChat" + ] + }, + "mixins": [ + { + "config": "allmusic_client.mixins.json", + "environment": "client" + }, + { + "config": "allmusic_server.mixins.json", + "environment": "server" + } + ], + "depends": { + "fabricloader": ">=0.19.3", + "fabric-api": "*", + "minecraft": ">=26.2", + "adventure-platform-fabric": ">=7.0.0" + }, + "suggests": { + "modernui": "*", + "voicechat_api": "*" + } +} diff --git a/onejar/neoforge_26_2/build.gradle.kts b/onejar/neoforge_26_2/build.gradle.kts new file mode 100644 index 00000000..ec10623e --- /dev/null +++ b/onejar/neoforge_26_2/build.gradle.kts @@ -0,0 +1,75 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:neoforge_26_2").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:neoforge_26_2").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:neoforge_26_2").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:neoforge_26_2").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + )) + } + } +} + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.2") + neoForge("net.neoforged:neoforge:26.2.0.1-beta") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") + + implementation(include("net.kyori:adventure-platform-neoforge:7.0.0")!!) +} + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + + shadowJar { + archiveFileName.set("[neoforge-26.2]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/onejar/neoforge_26_2/gradle.properties b/onejar/neoforge_26_2/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/onejar/neoforge_26_2/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/onejar/neoforge_26_2/src/main/resources/META-INF/neoforge.mods.toml b/onejar/neoforge_26_2/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..3aab326f --- /dev/null +++ b/onejar/neoforge_26_2/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,71 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues/" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_client" +version = "${version}" +displayName = "AllMusic Client" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Client Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_client]] +modId = "neoforge" +type = "required" +versionRange = "[26.1.0.19-beta,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId = "minecraft" +type = "required" +versionRange = "[26.1,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId="modernui" +type="optional" +ordering="AFTER" +side="CLIENT" + +[[dependencies.allmusic_client]] +modId="voicechat_api" +type="optional" +ordering="AFTER" +side="CLIENT" + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[26.1.0.19-beta,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[26.1,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/onejar/neoforge_26_2/src/main/resources/icon.png b/onejar/neoforge_26_2/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/onejar/neoforge_26_3/build.gradle.kts b/onejar/neoforge_26_3/build.gradle.kts new file mode 100644 index 00000000..ef978c25 --- /dev/null +++ b/onejar/neoforge_26_3/build.gradle.kts @@ -0,0 +1,75 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:neoforge_26_3").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + )) + } + } +} + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + neoForge("net.neoforged:neoforge:26.3.0.6-beta") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") + + implementation(include("net.kyori:adventure-platform-neoforge:7.2.0")!!) +} + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + + shadowJar { + archiveFileName.set("[neoforge-26.3]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/onejar/neoforge_26_3/gradle.properties b/onejar/neoforge_26_3/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/onejar/neoforge_26_3/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..e2e8e100 --- /dev/null +++ b/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,65 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues/" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_client" +version = "${version}" +displayName = "AllMusic Client" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Client Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_client]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId="voicechat_api" +type="optional" +ordering="AFTER" +side="CLIENT" + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/onejar/neoforge_26_3/src/main/resources/icon.png b/onejar/neoforge_26_3/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 zcmV)gK%~EkP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf04{VzSaefwW^{L9 za%BKlX>Me1cXJ?6Xm50HAVp+pbZ>GXF)%JLE-^4LF)=PNHZwRh5Py*g00007bV*G` z2jl|*3^oimt;)Lq001C#MObu0a%Ew3X>V>+X>DZyGB7eQIxsUjFfkx8Fgi3bIx{#M z_*8iS001F$MObu0a%Ew3X>V>IRB3Hx05UK#Fgh?ZIxsOHF)%tbF*-9ilXo}2004gc zNklDYhj-7MQOi#|U8M_NCHUI=bkN`-ESt2QuqD0BEL|d{giLzW} z+ESIQl3h}+jmoxt^aoe$$Nx&r+O16swL2CHcg#BEaGU%d z`|}@Nv}Ednb@(1K?itUx>Zr`SNa1j}d)1ey+NPgmvty-H-rA+-?03HLw=LJ2v1n+` zp1trjJ2UioOE+&>Eta=txMGo3jNT(uZaHi~z%}37Ve5oK7K(De83V3_6>BfrFaPVy z7HJplXMS+QQUkNriZ;#aqZXm9R)}%7Q?yWbaC(VW7-xt6nig+HxzVAlK04g5S-2Cj zMmS|N#UZ=!^-tK1>>HM!U7?ktHOH&8-KI@o3jqsP^kPwPT8>660=ThO)vn~q_Vk}z zx6NkJmdDa||A{lUIxqx$NvqILn;dPod~`}X`0L_K?F-WVDbjcQ-Owg%v2AN~N_OV_ z9A9e?9-yOYzC}loZbt<>_i&nWTeK>otMQ?aei#^qpAPs^63N2!DJE)y*HS8GcRE!oghMvG(X`1weACAN@D*!* z`4x+A6%YWyw>!T6MAL_6@5%dpceFull9o!%*suN4J!@5;T@n~(tyUei z<&^?>i33=4PXq5ZJ7`_Nb~5(_wB$I_Oi^ythSLiM9YJuFhjX@8IA^cjdD>on`I43M zM*%ELIZ0PAK=H5-THk4_)4FR)8@c@*hyh5!Z#Bb~6dBTZ{XTI7g@TTQFPAa2;p$OMuD%@{jTtHleJM01D+m1K#&BakYJC_v(N(w=zh1J-_Z z)NVZYS?f%dtko%NNF*^E7s~X~iZr~vFmQ%CEgNXn?edqd*=8bacQ2l>#x~kxZX70y zTBjDWS~N;uFp-#B&Fx8oI)GeB%-kyz;P}BB0hxB|^it?A{h8-^#Bi{QCM1^1)~FS^ ztXU#9VKKNq6dkf~On{3_*iP&#yhe3#=!1BoE1LLey{vDO0y?{%G)GAV!hS-nL)(Rgh?3LpK8>V+hb6 zw)s2nwZHe*SMg(xK%fQ;kd&nX2sI>I9i$xuLTIV#PBd@n8jJ+&4eHJ0j@o$P0x-0# zl&IL)CVs`6348U?Ke5*76{=!JRo|tS4xG2Bi{^+mBY+HHfj5OO@%R_7+x@M)t$xpQ z=!_Q3-nO`W8DJ{K%@VDa!>_a-&uaakq;B<0#ruWxFgk@}NL{#tUF>9Fg0Q8AQZ_Vt z$_9q#ESVXyNCMRy!SM=JXc&Tp(%#r%nLpl+KGmiFR^8q;z*X6j z>17MWqLy2kvqmdzoi-*RTCjRdNQ|I?K(I#H?W@~7%!SmHlQcI7(ooT;<6S+KvP66k z`z&dRfusy|1R2atTLJ7+iM=+t#+Jt!Gd*&P@gpFaV%A=`|D*O}*M8Q%=hdIID}Ve! z%iKC?+17}4w&K*O(U&xb#;TqLr~bAr#=V%l2$-tL;~H8iK9DghqcO^8V6=kz>R=*} z1^f?~)WPZEwU=nJ4a|pSFF+RYdv$knFvkAOFex6%^Q8_WP=JcZTgVc4koCVP;*Fj2=$i? zZQEEnVvFTTtA|c{nMSm34FTNBq7iV~fWz$`b)}V{J;pMBjHu093GPg=e5 zh;8N`vFq2Kw*T?xUc|P}QiFdGN)dpZTyM)W?RdLw^N=OWS^CE}+72`JA;zPcw2EfyxHM~w`X07A8YGK@+M%tgC9FIchc&@K zr2;;H1&!7MKP~!=axEW47*-NNJ@XGN9-1M_4srW)x0UYaZi+m9>D146>ycG}X1oHn zSuqPKkP_YRa#^>$o_bMTdyd*rsbm+wamzC4xD_*a-5^4BMEFn#MB(j=+1j#+$b_f| zn{cn`(k+s#6`r!CjR`w@WZed$Z$NmYingO2+4fzzRtl~h5`37D6}HFh-~ZmWedc$! z?Ds#}w9kC1XrKM}1&cK<&^~>`>>_EH@GpWtApf?)#_c-7JbBEIaP>|H6IwXM>}Y-Q^O%Qr4s zJu+>D<`cHGFlnO`Yc`g738@cMbr?0=v5aWr@a%W_4fh2k3{5m+dpvB_W*s+j)3y>THlMg^ zHwIp|%ws%HZPUVoc+&ht1gZXkmsjkC*Y4QKn+uk{wqWtiiZ$vjTSnt`<|f=J6<(zJ zWeRo$Z+DD7&TcxKCc*vv_4;!#PT+dt(utq<(r=U0V1H$$2d?=CV*!fwvG(&}TPU9kWR>fSrVh{|IGhElq0+ehwMWp3UoM^>zQV#&hi3RX*O zP)icKi>F=|1Dsmlv=6?rXzgmja!pJNrYu>n$t$q=WDM;wqrfpRKa`h=l<}ZJ#@K@^ zNN-2r{j@qP_fq8Xi^t!Q0hD?iv%is@Ycld}`0Rnj0Ya>>5*&F{6X&>5)Bps3pkC!CQ)tau}4XQpmhEVKYpAW2)^c*UhN zu&rIU)ZiL*&`fgdAbJd~7T zlsn<3n=gfgF+4NH=5-jN9^bUOJEmo;BnmKD1ycYKF>e&2y*?auKL<-WtK6JNZv^ef zFd>RPG&}ev+97r7IPi$@J`YBF8f9P(_9ut=0D36*o-6MN$J-=eeXx;(axbY#IU>T8 zI`A&k5m~@T@B}L(gSJlOaA!DUw|Ts&LfNp5Ow8ED&G*_PH(#)+t+Q|k9xH{zH~C5CW+0dp6_jQBi5$6}f?hLsJo62?UJ2otoU zt693A=M;&O(WJ_IscE zpD8TXRge@Ge@Zr%QoUIOM`^^{JL#c z%a%Gfhcu!#k#Kl}*g9#U+s7@mg~PKEwUOkCU7TCDiK(Iu53SkU@KrlK`E`5v)EDjS z)aN0<2Bw9u2nLXfwlHB7lF}Q*4$S_Q=cwrz*0d2pf}_Xm)%#~H5zShsQX(WBvdsda zY&~o<6Zb8Xd>y6%D43=nF+mbC`3^Cl1K;o!B%u(~7sPlb)4*RI>Yg6SgzVz5pPBSr zlU0Re%JO3)wmmvz&2$QLf*!)xP{5^j)r=N&r0qm}x_cl6&Y`aKQ2q;Ly+v6!#A@(&U$}+U?~f z8~UO5W7LE^ad*vTigPymAHKt?wYH6fM{T>kX1AkPZE5_DO+FzHzF-z@S`A5Wf{t*g z1nL=NlTe91Jj@aybnS!e2U?zbu;SLigmgle>^EQhVLLi~+J0o~MO(htu*s=ctbO@S z3nkZVGoG<#r%EkF`g;a{K-=#w9ag7|6@Z ze{$oDt)y1$diIXpf}P8T|k@YFHCBQoI3j zcBM?;2<>2(2;o{NiaN$QTUdXu6>?Ggk@nxSlN-NcGu!{(BDY_$`ofYeZf;m=a)huH zh648e=`cs@@T^*DXKxn5_xc9<(z^kZ%Z`#%zdr}{4@N>-l5OFl{Ft5 zlYOgOf=k6cr<7A!2%jYQ$mW*MesHrJ2AULJ55z9E~c5Dhpho9_mY?cAKh0G zqpdhHoiT*YMuw8Mv^{34W0!0rwrnqrL~Ma^tl+o|&dkte)@%3L>(Bj3$b9?UUq;uv z+3WX7v+Q$_)enM%843u#x8B?5{=OfCznAvk@8w?NN?#t#LTG<>=Zq{g`>Ynoje$f> zhYr=-_P)1r_Vin;cI8&iMqj*XwYP4YP+MEd$?ykGRJ3N>=`AadZd!aTY2h-y0#c5F zroE;!7%mm|TWb$EdkU*?!W0ur>$tsh>FG~kgCzWRaKaQOYWlbt(R6gpvI8sBkJxhc zGGW_*g+KU=RX*~VB|r9IGdX3N3FM!UsI(uky}Vb_56ZwG{J_MTaN3^<5d>v7E2+OA zYw}8AZBV~ItKEa{EAQ>UKlNZ1q4n8+ua|bp^kCxao__(SV7t4y*Rd8ev>x6@k~N`| z#&aDh)NFNq-d?eiz0oN7{C0w}2?pL?-$e4u-sWI6VNo2y^2oA{*|gPGvN(-|g3-YF z(Nut<)rY# z+RvD+5x-B2p$&?@Y9Esm3j#G>A+jeS`}2N;ADRPn?#Y5pMDF1-fLGzCvaUgE*GBAZ zZx`=-bAOp2_mZ09j>D|fgT0sf%lp`Ri0nP+FK}3r8Gr+D0wYKe;ZQnZFOLn_9Xi%} zm}dD!tPKcRAILCNh}_9qy+zDS+s#PP!YNerauk)Vvf6SLXc{?-!NLr;U2-Iy8bq)v z$N2dSROZe-m4jJg?Ezce{vrF#mww!S`%nIcefpb!)!temXWV%wYX0a^rclseLCKpCxZLE>(GDk!{6bpy%W)Ufax!=yB{Fr zX(>0^tlJddL-T7^-Q2QbyJmGbzLFWR%!$)BTexCR-Fw~!Y{YI=R;&`=vJTS!QtCfi z^dt9Rm}=0dh}!K#DS#*#T)@%&Pq=n-5Y!99dfUy9+OK{7N3B8l=kxpG#CV;DAv+to zV}GamkL+sfH?8p<@8wa`Vw!ITe#Q4eU%SbX*@u%rNFKbuHwnH6pY@0vJk}rG6w`Ju z)hJ12CVnfV1EiYYH#k9lXoth!?!;1 zquHxkJZ=kdv(-~mR(;|Tt37+!B2PVL=||2n&X`?Uf7-6xf5GB6W0qZtHMEl=I;&+!FTne)PH|(K3F{m#3#GhN!n>olUlCHAXqOe z9PQro+?D2cn*-wS9fLj{4*v&y-7MkJry$ys_7MAktm^Kp0brfbLF3XaUB}{h_#rET zB<*aG=4ohO0Lb6UZQ7TrFWVcoY+KC3?*R=$@uvW10qrtD_nqgV9C`Id?TYyKpsQg(CIpayp8Z z4{zB0&b-|pziP3wCA77cdj({#mmuvBxIc4We*+0o84X*1&>qnxo0~1zShi$?ooze5 z`HFp{^O`;1_?&&B{!2F8_(NNuY~fheI@uwgF%;r~&|5ty^~V10liGRv98%1^FL^(~ zf5(HghBM!{)4ht*PJiy+b|%-iUUNQG%xCDY|LBJwgof1P{QW7|+ST+GE0H?pT%{E( zF@wBxF#;&%Q&F6D)uwAH8!8{Q$ojN(>P0K1H;L{_R+~mkgmY*V%4&W=a|E6Qn);Vy z^g!-U0c?&RucGht&W%_*;ak6=?P1H@dD8}7xo*QXMH)q0KpSn$ov`%7XRL|jHwAl? z!t}qtn{E$*b~qda3DKU%U7YVrDkNvDFFD?NHX2>U=%amLk4{k27p zF0#CSuk8aFpr<|#O1j@d1IQH+BkLs+5x-83s}mtjIzToFSi*#H3InYZ%zJB_R0Ibt zStA;VRe4rLYbZ2w;}QVr-n5Xf4)(&~u!V8#wEE`j{|Xt!C@tVh0+&|pwCiBh260A* z%Vi7KJne2)lLuJ!KBdc_k+@5^a;=Umvvi3koYEc{~!H}%XaMSx2&|a<_x1ZZaU2Y3Jkyl zafqjwS<^E#h*nVDewdVHg4&%y7zZLmappR82FLf%3FueF7l~M;i5Woq#2ea!*tTdx z>-LgrX(wgc?d4wE`q$Toh+K^~A#%%K59@@IR>NFKV?@v&(r6xDh@6Q?qh$#afL)h*&0XBK#Fsv8^x;q1OC;4_^TMZl*c1 zuMRqh;_3mbR4>?tT7{Hlr;9rC-jq~HPl|IG*9+f{eItf z&y?9AUN`ri@n^T(zTBVs87lJhmD3&t?BOy8XP5ZOY9q+{9yF@3BACK_r;Ib$tlOFM zC7V7vZARUYYTllnB@y_3zV1-nG6r%{2zDuvy+8GFL`)Ya)R>N%e(U-eYx+s zXE9FqI)MJ2Q;%_+0fb!LPzpNHXD{#TT)l|XyWhnG`V6|?&At2nOw!ScTXuNSt~?&K zR{M?}nXB3-e(0n<_xzyE9z#QHS8RI=l^aS>TmO(lRrR)dl0nmI9ZG*r zc6;qfTPj?%H}B2Z_|TLMLHAgQKamymg;X`DanRK0ZuWovNfH5dt!HSFL_K1UEq<4^ z-Wr4Da2SUnIB{+~9QU$9wzP%X(E=G4{13kCvR|kIJiLA0>ANrO|E{!GSi5Dqx!11efQ6J?FB~VcE9(yOb%JNiCT|u*fZaOegGDO=D-~otzxi^j=)1N;1C+5>0zxq1k*~nOUWz6b}32SYKZLPX) z@d0TT7~8=EPcWlQfmiINy=}j{+)KMme{h+D?teSEM>l8`5NSVG>1&W>_jj|B%J-7$ z2N@zk6u*u@aB{H75|oV+@~z$14uO>2zcp*W_glku{jF#17yrpI`&65`_w znqEFtJ&>yq&tplld_y7>5j9&|pSBy#Vm$q~HmM$U=?IdmyRcLcdqOC?8wd7t9B>#C0pOj z*~Sw7N)(QZDHO*4f%x(zwAsbI3gMxPlf%+}oF0^w`tjVKdPMBygX-_k`_oSS|MJg$ zr$6VKg8iRCnDAf{XQEOMNOa}W<09$bRF*5$zg{h?cD`x<;Gd7#=wQtL>W{o>wZ=(H z4dQNv2S9!X0>d<@beR~B0w6Y!kd=Z#s0T5Cg-g~%#m8GIh|siqr600S{n2;X3m<4% zW&MWFNt`(S3WR#m#%fE1foL276EqTh@Kr)9L((GFYr_m-Yw|rHrq()+-+s>W*W1>3 za?R@XqD4olR!!in>ZMi63PvBNv=t4CL3`~B!M)T^g#P<)mkukppP=uyz5djzLu4c* zcfD#pcfap)*#4~N`?G-3GOJ({c)*6T&;KjCeD0syK=|L; zNNbr^6(*`JHK4y;=A_Y0w2fHF8b{Zxlv=lX zr^J}Rkuzw}reIYczL3?`0R1@Y-P?cv?Q(w-etWsUt#7Bj{@i0C!OIO@228N~y*IG^ z>0p(k@~S3j4BnE3FsN9pG#UV@Y8APavGql%#$|ixDF|6BP**`+l{&$DdbB`OT@B!{ z>;fGCMt`woFRR(TmgW!e%oEWqOAj|}es$K`gl(Zt(I<;qXrGdHngk#^to>~&WR&Jf z;#ef5sU*d;Ei=+cZCE9{Y9k?SB!M~GH5fz(J;*~*{{iEByN7DbAob(wPm}v$-kcJR6TU4%Ng;eKHHfs#t~V_HCvCU+vC5vx?zZFqdeW>4I-(a9CN z_2w2O!}iE~j##y}OQ+0vmU8&5Ya zw|Ul@opDTnLNru*xPnB>hoHSKoVze%P9UPDd^Me2i5IPoq(>TY8!paTc5{~JgBDMu zt+S|IP-!#|@b#SF9h~vq*^4 zdiVBL)OY1xhr#y<&Xc??WQ6)@E7z{u_x@Pee&%lt+p{0Z+lRkzz%D-Au~=f!V#y)< zwP7{p1~TIC_Ty_B%C zN4IP^cGGGxy>+D5x*%S+E`Ml~1~o8mi;qssYINJixfsch*y9Tyv1hLTs145_xBN=n zqWO#!TZ`7XBrTwqKLnFt79#}h_VBf8Y%uUcy0 zEzE&b{j!}tAA@+B)DeNiI7lsR>O`$bhe!g+#C4=?!HBci7-nezDVXNWJq)O5{6l-b3LWpyb^HLy<-emWBuQYzUX@C9x z-2d!P(~s)(5~K|X4Td0eUv@@N<3W~WJJO`GozMZoY89R#`C5?+?73Q$GZ?nPjGQ|F z^MBq2b;#22q_ZbytkTwYu!JS*dcQ7e4f<|i*V{N^I>If2UB+t!Mi?Tzg}1JqK5@Ig z{FHt1t>>Ku`t=Wd@{lNzQK#?4-w@pX6&=_?!QvCx@eA^{L|MhMb(!ZUo zbWl6*ln-iae^%;UN8b0y>79un5ldNRdXqgt`QEeOuWsQo5ejYE3m@&+I$>9H8&$4J z9Mri?cq9_FmDMdvrW#243~fOS!aWPkay?<|_owVP{^_PA+m|3Lv46Bg+x$cI%Q0(3 zWk?9dkY2rprh}DQ(Wq^g-*1Wdm|eT|l-=JxYL>y_Y@{^NK!!P6F$mwVhfcl>l*_ag zhOjV^Zxh!HUd%#VYZNU=9I2G79ZOrCKC;lZnl4&xqGmOUlv+!+nYd@G*_$@}BrGzK zvwAdGzE+s1vG#CxQ0^gHsdrE5-Ky!gONTw*U%&TxXrF)MCl1&Tz(0QvjspFI2ogL` zn-RjL?fiB7pMP=6M#szcFaAN!Uin7c28O3ms}($ONp#xsrK@)QgxUE^5j^s+wyhM97QBG_=B#_QkLMuvNlI zyLkDSeeQF0+p-C?!dO=$Z3EY)J@?2BdvfkqFc&#OMlA%Pk;GiuQXZDM*|1h4Z-4)v zJY&E8>z}pHe<5k7MiaWtz$}xIG1{#UrYpEq#5~lSQ6fZ z;9ly#{!dB0c6)T$-|n~DPvq%`_HGH=Ni^jD^tmT%;5_ymvGm}EoqzZ>d+LLuFip%} z`T7=NSPREan>O&)lLJ;P#_ir!^6g<;UrAauf5MVzgGei7AHVZc7F`&y3f^>M0GsXS z73mZ&*<~%eYVt1d)Z0k-R_#f9@!I=rHNR;ecs60jhOgU1>P?#&{+3-n{;IwA@^zb= z{(VBaWqbe;9ClV{DwRz;LY#IUl?r)#{Q3L#!pCbiG6dXT-0-dIvQEBJfs0yv+o}=b z|Hw9AU?OCaXw%yg0v-^Zy{TX7-Yz{TWZzzTH~k#e#~u@~Crgj>xi`@Wwlk#I9;9gM zx82Iydq0To?A*3<4~Ol2&kfkrbQn*+29g`rs7F9J85JEPhoiJ@Xa_l#0CjZfoK*njYTQ@2D`yLB>_h{Ubl(K0dU#tDq00d%}uB|L3!-o0S8;xSviQ?RMwj-5Yx-_9PH zw~67eS~mU_i*@oqsx<;cn!4IJ5`_u;Jp&lH(`i@;vvK@P$>vVK#i!XLj|^D3wPv+) z(JI?5-~EM!p{PtpJMG%Q_aevyfSvB=7(qV)?sE%^veGkI` z3}??jwI>$$i@xOdgisxqoj4z{$Df(Cf$XS#^F^FC zy?^IX2KJiIR=D5vGG;J_>$67FHD+ZjcVc0;nq=Gg`x>=Gl8zJ0l z!I-Imf<5vS9oV`JPOREvPY>A#K6cgyhbHZtU*9Hh(B4P-)tZD%B1|ABR7~V2hwdO! z?56KB8G(vcJ}8Mmz306$-)`u(Klc;;pyx`5^$~gBLuWAoDzuw}i5Ok)=O*q+9ZRJW zi-$rfE7q6n{iy#W()rD=Mr~s11$+6cX20?aIs3vNY*{)r3(~Nw)Y0LLT5wStG9)7C zSaIB%s}Z}Adc*R~g5|K`Hd3~cb8Slu)=<2XVr>r5{y{8oIBTyjoUn7_1C~kNwRXL1 z?>V_-&pz>*4Uc>UiN%>@up*F5i9CP7?dBj-rf)HZv>2KtLcI@V?A4cAHa_x@&EJaK zum9q@{m0)gSt>E$%%ezIM!}D?)5H=&;77^fuT)|(7BDh7*$i1JMyN^%d$0`drv1b? zEFC7z`%`~ghvkEkGvnX<3EcR;)a&%HNMwZ;**cuLgDQ?hkJ_(&@n;?BAOGMV;3$?bMDv)8J66$FFzqo^a%~liMSpd`b4z;( zrBZm*X`=-~H|?MN%4z%T@7}Rb{$k0Ji8)Jz$1I!_(-8I%Jp^0d1A$P;rxifG^^|A= z4`wXRa8M>NoYoc8Q@8)ONr&ThH}y2dLFwJpf2Y)If9Ku6URF9>C3QPTx&kK%+Z-LV zJ2$oHvuw|QbPD1lrKwTFgroXZM`}?og-Y^Ksf2hhQcj?WaMH4hPj${?n%Z_IquGBT|?9vYfLn-gZ(@QBoiY zuAyoZnDaO^9kr!fnCZed>OjYd-Qc2A0 z_b?$Lt3byx_8$#YLA#VAR@kW6@Yt4(jFyQ4blNv|-r?8Wf5IC08a3cw$JZ%0hy}vH z3OD(2@i0p@*|7}TKv9Yqqd)cFc&98F56Xw7gW7mEd4IcpuY0|dbQ9JHn4m8Q9HRoL zI5cFba*f#emPL}U+1>e9co4SfxsHvEz$8t1$RYsskb0@dFbpFpR7j_Tkm@lUz$9uo zmbd)ChQ+HB*51N41Ccv%p#f{Q(zL2cmndHR6yE(&;*X>arUYr$7B{sfpz{WSt&yP* z`6Yea-5sodC<166v1(<_EmJ^EC|))xsutZqk6pt7hd8n)0#rFPv`i*>ub zx@p%J*KEDgv^)%uuXb!1esU0!bj2MB55zq-Z_6e>Dy^{f9|Dz zCWt)qNZ(=*jVq~ut{!NB+-@~(c=DG0Pyh6kedGt5_R;SjviCfP+K=8tomB1aTNAdt zn6NNjxQ|58LvDNw|TUa_{w-*L{cCcA(+ody0IGi_uEsE`yw$aWW+GMnT zMll*h`}l*cU#s>re`^S~dYST=gti<_Lep@~^0i6(2 zd(=TwM6@NWHDLE{XnWdeit#;?p^z4=F58#CLCvP=`vCj0h~CTl+t|$kYP`Cq;C|%&kDl#Y3<^@v z(B4jW?%|Z(pI@~1Jo_-nRg5mK*3Zn$QSrR}AHVRHRkltc@KS|A46YS9ZI;x#e63>I zHm)IE+|!(YOb!jEh7cerc*a{0OfiujFiXiQ=fjt?#ySoKWX3mM#et-2gCNy z{^`2?&c9i+r2e6dEXncRzs2pBx#s<42|~PFvdrF$l0H(rA#DozeOR z(Zm}m#@J}v1~9j0#%=iQDT0$Y<_QfVq6>#Gf|i~|c)+ThsU(m0C5PHVqHZr_s=TG0Yf%Z4iF55<6VLBs7Q_Sj9>buma z*mkX9g$gRZUGk;tda-SVtxc;{aXce~Hak3RN2Z2ta7d9J;i zXbYI;8CtP~ZRB!x>;5uYqGmsmZ&?Co@yiz(6Zi>FPuS4Rj1$^lP0PeA)g*EPG*I4^ zdj1D7r8~yDzc+~%!JI-*_;!RO@Uwvut+1u*qaZbf`OQ$aV&P~Bj*H{$ZA0)f z_3*G!+wPLnPCHt+RwP9i#On$_{m`jAxXbwzaR^neU*M==W~${DPFoul-LO)4g5lExM=Nm-0gR7%)PgE|j& z7Q>dqT%3IEx*ff}YM(qmZH3_(g)tyQKmHWA>5&1ONZ~8^3$ZW*ZA2h?7`Q@c50??` zwK$MgVu0YmUV+p->U5drT@jjo4@#lG^^+fa=h~!RC(a1kM4>hDm^O7vg=@+ciz}zV zZ7xv&#kC42uGdno)Uamn^qT?oXqgzg4#ElJC=tNg2f(VG+KL^q>j!@+Y5K z?bRWYT8YyJ5&f!}4w0^PYF5BrZ=wQW#d#%FYCWA8LsZzI7aPCMQ)t@^pG5XA{5k z75nw$Q+97?$PzF|D2^7w6tr4(OEajEu`!z*AGIV*6T@#x^QkH2kbI~ntq-R0-I(d zg=9V>@faBb%UVUbMtVc!uuc$#z%hs!3#85tiXwz!{{ra3 zZb0f?dhF8WZIbw9f01sxdo;k?sgiy!3ram$))~URPZA%&FoG|Vetp$KlMtySr>O38 z_uBm{~Qqj20VjhME1}x3} z1aOCmnKh>p(+^!b3c(PF3$UcW&JMx1x54?lNjn-q4IN}lzI2=tv z-w#m#GY6Wlz2|x&S_b2gT9uB7f~2~YvFXKX%SyEp_$Cgoj#wDAS}#9kE6u0v?8G0T z-sf#{P}{w_Z~epzIHfR3C2VWCbv*wfCIPKMAMte7k}%2;_BoTyI>GfWUj)e&i~^}l z*$wI?68JCDN+4VZRYzdH4jQByLn4bMo9ZO(N3LD9BX{R*n|hl4(5c8Z2y-U_qtVz1 z%(2Lmt27gT?4->;{3w!dmMawP_Wk>|T_*g2S!0Rd=j-%Sw^7DFF*1qLd=LWy7^ZbO4v8y+5}{1B=)X=&^*idPmF{K+h6S(j_s zVFxjS?~r#55!Ektx=jX|AUoQG6qKvrHw@FxxzQ<``P3iS%*t}05rAJa78oPDO%GpA z;X{Oy*2%{0WrmPp;VnTf28p>mT0n%PtTTKUISMa9ZV)z4M+&c*R9YT{d9)PYxR z`2jFbxm>ZRP7{FvG>xp2&>NT#If;rO2IK>vQl<-4OMK%s%PcKgzC|cJG=QH%G_+K- zZ$PZuqlA;KsEtky+d~(QTULEQJUNtF4ej6r%Fij6>vn%_&F*h)0t)Ism37Bccmw~s8M3<0;Vzr5F$37$J)k=waID-&9jY=NCDVhS= znG{Y_6lV+qD8$TwfTe22KKI7ov6bzoypv9+Z1H5?#s}AIdh``LG5ZaRg|B%C7=%_G ziz_4pWl^bn+R%)o7@@6K#{?q?m&d<>hu#1tpMU4Rs{4;prV=(WJ_Z4BAR%I#(62#k zJ4A?Yf;8`>dcf~nmZCmF7<+br4eB=frt;X zg#awAZrIAEHlIc<2@Z5{lpNuFr6qV5*E0x478F;@quKE$HI^b^Gx5F0dL|Y)Ljkn(l58}X;?2S8D z?X`tP%)^sb4qdd-c-|T%6ni^rW0`q7eryp5x^1y2Mg`ZX{*Wu>^33dEae{mRNoW7<<29fZF z6~K=su|yArq_X#0_z&CplKVNrt$t%j!FZT!m6MK>#?xV2+RCn_6sYA6wQrN5U%oz& z_Go||sppPVV6TQ>_9eIC+|z`hgw20mmb$I5JohFNUUToeOh{FeW5)LyD!+v{-sCea zm6}c2I3BW`G7V8GQt#bK3weBwWR#G|n+rMH-Y8nVUbf8CkR`Hz&0c){6ZXOP-a{g< zTbR&qbnqo4ZVf~TD40`B2~yNn6IH5V=vT>v9HA)&g5t6PsA6q^(UG?ImzMYr(eMGH z5c$ZF8TXodj$%v=xKYp-0|xyR)$(T1?k%snSxq~cY9 z+5KsXT+eq^kcCCq8k|*D&BL4b0FaU^w@NpcQv5^tW#1&V= z*APX&2QCLCLLGq*x%|+{mCcIXy1&98iVPuPGh@>>I6g|-AF<#5+z;Dh7v8i}XMYR0 z=0SAHVx0-ok1`8TsdzDf`x7nDKzqJ334cIq3oQuQ0=7y?VV}_6vH}jM>dN_=nVxo7 zO4X9j6^JQ+bk)m{X_$;6X3J)E%=YE(ZW6~-U9dVkq&-l0YPia5+TIy#0{ zlivb!!;})5Ogafp2QABI9Fvfxej1Gu)t*hptlvjIhyi-${$LG2(qMhj0`Br%!YU;W z14(rTQp1jyu<1qYm+ZQjpbC=oc8@r{Nz9u~#B7{FP2k8V-fr-)QikLZUItK8rpa{3 zRensnfVij0-aD&nww_0o>oBHJ%Tn0k28*lLNO2*$I&`{`n1<6mtY3QD^2ZcnCHqq zEi9&L$L;LeeZs+c%WviEK7D9iORThxoPL%u&rgiu^Hl8NGso=0Dp+YFaPry~LoG^u zr&1|fm|q~wD^jKb^K|ToKk*4m$jo#S_Ur%l-w~6?Z5Zt&Nf)tuCPbf(k1l?E%XtB^ zKTW_QYI9p#cH%3qS&|;L!vO!@iqW>+Zzt@f!MNQc44geWVNb$TNgO%7dwA>q5w<^O^!0?=Zm6{q*o7sJ zj(6Tuts)DAUR_@CO%z(id-ky_giKNFT!%0xXC($8K;$dPp^1 zh5iKUJcag36GEcnVI%o2A=|(-U0+zSJYi`JCJoCk!3SueS*7+hB|R}QYLf^~3aua* z^ToVxWLX2>$A(gN@yH~+0v;qu-C0+`zCRtv01Dk!az*s`MRr>Mq^g|> zxF=RXkzgik7LwzO4X8rgcDv@Yc^A%{vLyIulj{hG95tnASbG-GYBI69p+EGg{R=B- z%Jtl)6*UD6Lq%}#WOAC4WMG+@nt<>VNc{knw8xQ%YID=J(Ykdx*)Wzo^Ff5&KVE5J$d&J3|n%f=w%IZ9Dfq zbp8Y$dD=qw1hR)7W5)?X_-r#M(2mqGUfPj5RScUz)2zYqUwiW^^b5Rlg)xHzX+K3| zee>_ywR_*A-+!Q@Y#A8C*@{wsYUqo0{N(>;k)|A_Jbp)tkWFfF5atN$ZXYt#0>`SMrob}ksqL0*oBRCqfg+98yoA z>+tZPO;1hX0FE;9AX?bU=P6&bQW1@(D2Mx^na^?H3FB?q`yagoo{Ap6BV&w36nZd4 zo_*~61G|EilBu-axOLkB+@?dR>;!gFZ`8;@JEABj1WPq(!9_=>4Gbb}s5ISId@Juu za|BYWQKX=UR0jxTMhV3>u+2+~!{yzIiG_3|A~P_;fX^WnCy{zMz(p|N@^-6W+3dGy z`;VaRmJfbvV$w!NiKR1H2t!8-vzj$4tgYC4PtVy2m|2lW2!dQ+&Doc)-tdVSm2*k& z5?M(I_4g3OKmv-8P7K1Az=>llb$p=B^<`^R@-{|TcKO_C8}nJfx=$L(^VhGG$9(LB zpujER-bO%fudLhk`wO3?4yc>gAF|0=F=ghpDIv!6U64ws+k!(Q7t6`$e0W`6C-2oyUP1x1o^%1Tui# ze+U9(gz=E`EBEc8)5q-L6Vujd1gn7UNYXy@;y0|Mg?)-f8G};K;Zu^`*H7W7KpPQ~ z)s~8F;?Oma>|%b)>PY=4rtG0Jr|sPFqn5&f(`ugP7-Z(eXhw7*C%R0(OQ`Gl)pc9S zZ6TvXKLf+S9K)g3zKJmEeaz0BK4~XtYZUy6!8X>{Yzd;TQm+EhUGsp>;H@jnr*KPK z`Ugh27lBqqJ9%UV1_(lIq2n+H*fS5%j{tGjorP8Vjok2X%g)Rlu@gs+z`@i*B3y#IAqke&|LRTwiTL`PZ=pr-;b8(X z1#k;@HZndzpIHz;Y^{0{rXcps)bQG+J?Ncqr_(^Xbj*u(^8gPd^;yLGx31ayA3ksI zxpLk@wQbs@PGZ1bxixQ}`SQyaP17dD!Py?>7lxf*VRqfo{F1bT2*0_pY2il2W~ayP z)UjhWgsBQ?Y~66q*Ab`_ibHgmWgW+CX>HX*<|vF1m&3=n8^9-rei&1A^w<=*l)ykd z@K?k)*@9W*fNSQ@;gWRIzMD@6QGcbiginzl7zH;pY?rp|nTIbK)(-q&N<>!w<2CoB zJwCuL0=VXG2i5eC|K%rzPwzmF1%PIqq%qQ%i77&mXi8gYu*+IY*Q-@{pH;%X&D^># zUR*kV+Kx|+(gFZBaSnwnRH_D{bNt&-dKH`Y+O-?plFvYgV7-I7P7noT(nB^iHH4;N z94+-NXDb6!sIM}(Y2v7Ai8qGYB~mFBt+=*qkDfhY&prN-|4a>bg8mX#4JGaW{=Gl6 zvgR0Y&wUpYnq%jsQj*3dO`%b1GOLP)YNI`d@a~V`wy1|CeIzxx-z_W`!>p+lG` zi2tQe{8`$Myt?tXZVTk1iOj^~?5gr+v#2h3n8PktZ$~&(5 zK@uT|-iGnCDO3(=VR^wGJ$Kx`^Zid-tGwkKF*U~+OAgx~ef@R&3cgJ%nYG|I14O(4 zxpGQ1<*Ny;1_am9GFg~=W^%%&iL*1nq;+avX%s%may0cKN*Tv;o=|u(R{%1!Wdge% z2Y&D)Y1cIE1X^Qiddkv7Kl1LiwR{^%-@=Qp%8N%cXnUM+=O$S~Pq`*ORs$xfX?9ib zTwr2ljug6%jEuM$h7DW;G#G2RQnHhiBX;%}(HP~$0KWck01v`wA`O?2 z%;Bsyj$-?OP6P0>0wJdK{UaNxwIT`HlJrWWv_LRO6=5NGCKA!I^L&`z>E*cxcF`rzdS} zY}DItBEf|sD!%KWy2+A(m-|EY*p%3Bg)W$mLtUenhBg=Tm~lzZ~-1* z;Q>t`;UmY>n8J3-*ITBCQug>+LTBLc*}$;;FR{tqgb6roDi|qeK=&4N_N$-%9QI2) zbT!*1E(-`AI3T)4nz3&kLNM8}X+oiFW`G9~R5&(W+=3raMZ$|)d3Pu$#|G`#Ywu%{9#qP?n)Z&ScKny6SMvFB_y*OXf$Q^9+$?+lk$shfG zOQRh^L;xy`*0pFdX`lMsm+b2|?zwi+9z1O!%YeK=;_AVHtV_F0PDliiz~Eo_5Gm^+ zt%w5?t`_jfmsf4OK$yk26X~oYK@MW8QT8d@W5hIpdT0 zk&p<+0H2z$YIsa9a}lT92n;lYqz}^O05EFC^N(MA$?~Ag<9h^6S}Y2F$EL1LWu4-~EfbPT*b!(4hEK9|2+^|GxoP6sGy|mzoHo6eukf!{CVT=JjYiex#>ed3iIcwm-$4>(Q~f;y zvTxgp#(m~2n0l^ZBP%#Z72iuB#?o4ZRvK$KS#x6p_7{KX6Tniz?pJ(jIF444I$p#c z|M5$&T54nhz6|*~MN~cm_Fq~+MAA4Kw54(Rv?+rVfi;D-nJ?Kgc6|da)hfi+asEwzX zoN1hF;X8qr6A67ypBvlNhP`&2lV)H_e33XOEdptkW1s+b0y7J3|sIZaN&Kw3f6WRP^UP6d&j^-zTR8i?SM zGulL5EU&Cuk#H=Dr3zK(y?GP7|1uK2)e53{@akY@g zKf{NBIpGA0r7|!jG^1QCziA(S<}siB^i3lYs=g#zDILrJ+zB`awVX`b@Bh))?BzFZ zJCUUYG$pJZtZ6)9sVZ@|t8RHl-dfD{{s`#9 z#|WYZnb%-%LNIjjFPqrC|x8D*9Mi~l{QKMoNva6626ifz`nA+Wj7ZVeKk^h8Y1Zd zoGP7s8pbATk-BL|Va`kVEk|KcdEiyF!0P(CEi5hC+6IoGBp${BLKr5{T(a5PcPQ0f zgb`~He*mUBPSkXEZq}vTF5^CQcurmmm1=^u4HLJsvqH%!dOB`ow5{E@zd|U4UNK1&rL9-5GRw!4( zW@~@o(IYdCEULm`RfneuVHfW&`i$434?SeZk4{?^b*LRdVij%vNTa#5=|TrQtb-h> zZG~C*`oN}N4VGQc!yH?-OSOtNw>n*+39H;83tAeStmv>VteJqD{l8l zLK;LGjd7}dy`U-|K!{i9CLd)0_@~DwY>Fsp5Q1w;TDn1!BHw_b^exQ=`v9x7QXNEU zJc%SW9z5&Es-%fp)b-Gpx}s4Cv-GvAw`{%Kv>{(8LVFZ)_K>E(Cx>y4DX)n-aZ@+- zXdQX}iSq{>6e+?1F`u$riS(c^)z;JyZDdn8PU92F3&Y6ak;KD*i)(AD$KOBvW!aJz!{9#(BKe`TLe|RZk4irg*0sA zNRVEzsKN2>EQ*1OWh633^pLYcrD1-?KgiSPR;gz9me=eiKEZmPm>p)w5SnWK-It8f zA_>gF=~E}{%;^(0F+T3!>!_UNwKZGS1W#_uH?{!wH-j z9<(cGPTS?vCw+IX?7O0=7-5-w4=_U;0rSXb5y5@RnTSAml%uH`Pc44=&SDh@nIfj< zLL?AkiZvpp5?U_FIK+?&(Y0hf1p|wk!mFI|9*uhm2DtFR`2b2DW~l;HjG%^>u%XLq zIgQsnXP-asa+H7xj&vbOLjaHi!a!Q+R}Dl?kS@fvVh-U>*V{NTlKQyjed$YPMx*py zFnGan?3|?W{_?VIfcUuH1e%$!vC(lG!BehdCnaGGkW(Tu(B1{Thu4t0M>3=>%EVlE zR&#cHam5dNsv>cESw!2g0TNYSMvW?bowD=i&->1rq{36+Q%JUr6S%y(LgbS7Svsk< zFq%tJ50V_Q4g8g2Vaqp#W^sbgA3tV~o;{5OPFThXCSpT0sd$Dck&(uestkmUPnF}J zOonhT112pYZJ}An)5ninp#%*04>E5enc9*@(@tycqLR4~BZ5>UziKCrO-mY`vG&tg z$u&UtdN&1h^$MyY?v|~7{o0*CRnxepgS}w44Dh;d;ImR7SPo7CheS?|wl>$0e*F`~ z`jqKVY%j040a7)6s?{v5oQYS|sCMVZ6G`_EqA*AiRXu-y$+jrl2KglhT}CpCwU$+A zU*Vb5d>C(Cvw}<5^=o(T+49B~nxO6ut$Ye?uF!0sBz_DgI(PProj88N1_uTK4=I*o z3)9ILSzTR&@JKx7L`>-~lK`h+71LFV1*;IvXv+0C&fG&MPuiKIvo?a6)3kI4jUw|S zX41$-cHzgFM~VJqZiUOhRU^}uv|0i!aqP&XUAb`1P98hv3q*xV!Tlr%;cbWkXwIq# zmHe2v_7I4fXt7l<+v&NZ?tcVbsh`~>!96q#ltUtrB;@jB%k_@^vtRj?AKl`!QrI!y zx-C1V@*+Bv6Q?;h!vOBMkYoy~h^K>O#6EjKmmG)UEa(Xd+2O_ifQU z7o%vTB~)vXs6jDyZ$RosUa@x8$W6@<4d|KBolMb=JmZj5bMSJAmaLB+IclTBs7J;r z1ZoneT9c!yBVvla1K1~LXwm%wb+3XXm84;5L#-NPQVc&lG;DLnj@pr#Ni>G0fos;OHGQ+pD)7B|djZ6yEQ5B` z8`N@W*UN4D*3~KR)`z9JXF_<>}kd|1ICJsUurHhnS`Jnl^0h1l63nrf0n=K|@>eG2B-G4*oh5 z$Yal-H8O;QQWgr)&b3o$GtmfgDQSYlbc3Z^n^0-w|3o#1e#36bu ztb%_~Lh1zn6dL2POBd|Iu_L~LQ!m75-d%4x>phB21d$PExn0-D6@hmru%trNs2B@8 z=^Y7$tyAN}_QXRM?9#;xHZd~d)5P*6q=^c^*s6m!8Wp~^{-6$4Fi8z?7uSRvpw=Z& z&__8cXa4kaZl}q)IUAv|80Qt&* zHCrV!Fo1U}Imv>QI0%&5YWX_03Q7v;OvvFw6F;Jzd;?i*ib`-qT8dH^K*6=EWSPl! zz2cKSvWtm0lHm>{sC5+^XdCG&HQpc+D3`W~7P8p&({=_=UW+h7k`9fKGg-iYSX^9o zyRL29+PC1db22p=GJi`ra`kEvCal@C4t*g0f8@*wn;XsAU{p>lrbHxWELwwb!fJ3D z8@DAga?<>_mGH!M+`Zn-(@PyK7#d|{7fv6s_doTRFN`G_Xti9lB^+I!E5O{gs7q~d zKx^PsV^&X^VO?Ba@wEgUTA`cLWSWTEE>x^RB%%2k@tcHk6i0e24O0WB`z{(Hsb`8p z|9zhc*tsADToO2Z?(T72XoT$-{;%Ic&HIos19+Txq~jylTp-cpg3iv7b5cT`7EzbL z7I1yHXIFbjw{Cd&1i+f{s=?_}>^!Ep2L#C*iFHqXWHT|l`d_to?F9jz*I<;IsOBCnrYju~XAN^Qeh7e;_<}2@-j(FA5M+blV?_0W>5LstZJlc=?sr-tdE$B&CT2 z9y^4R{nDDc-e~RZP*E6|N@~|U^2uOa8k87>dhvJUXkOY@Zw*CIGcNr?EI_(hAj`J< ztQ{;aY0zeml3wG6`?cmUg~NF9;(0rM;QKK2U&8=+^oO5Z{E9d@`bc|68 zQo$Yod;la}%dIK;IyyOG7YX0a9-YC9r+s`JO}I!!iP3}?(cI560wxKFw3h7o&eXP# zj`8XMt!9PrEol#(KVwfke8J9~oU=6IzrMa^H*emu)s_$THTw}@C$-&iw%TUc7R)lHqy1+XB|H(T&r%;5FKsqjFHKVqB| zPLk&TMO0r?r@S)>g+&DIY*9VnK{Y}k8kTvHpXc}aBPi5X)9s<8Fl=ks?)Sa#y=a6n zm<|6!^8lrSEiEp2NU3-It9bprrmdV+iK(YC7nkt3&mW($*`Wc{F%;G> znL$Jyap`E63Kn6-w7y3HxWxo8ln4({VsqkdLbniJd@627rbg^Nk6poMIFE)LaBXyd zanWwxzGv$j+te4;bj}hEFQJgsyjJ!q&4|5q>%M*NOE1|1CP9Rs!H$c`Tsr7GUo86! zr}iaCc%-ETQyrwOZWV&L07f89tbLHDj!bv!G8WNGZFN)RgO6YMd3}8#Lj-9zP!G~X z0={!CX?*R*0-Pr`rRV_UK*S83?O_sJ>1zNGS?X6wo_&&X!0I85Lnn@RDU!^B06w+Z zK};pCcETg|G9l6uf=Zk&6|R9I0EBYW#Jvd!U9r1&@7M}VQO9x93zae>uDO6keY9|z zq&>Da)-98W*ii_ijhv^j?^(tY(F>K5cwlk9ha(laXf8sM(>tGs#}0?CH696ve1#Nd z>gddvJ#yisT|9rv=FlPu+TFx4yLIcfEh0FTDs9QHk}8&;0Inod8ldJhG;eVm|Kydo zZrDn0)1wv%#uQBMc3rr_{73_@ZE9UbIK{Ds&=NQSwy?HoF@@BEnacG--Y2D`vW4gF zD0Y(cRWO?VtRyT55#1a5t7Y4xN8u)t@6Pc@!E+UrN)ivaFCeuB5Zc40{eePK8lPc& zY{X(>8c7V#bu^5mOl|r0acLU{lLnz#Bq@n~6(4CD*_l{iQBUdK!jen2!XPUElD835nT`E4#_4shop15+xKJ@;_?3pJn+0?`cebj($-rl%+&FvG+2WU z&_vUe^89PHCbkyO8f0qzerza<`2bL)Rc1upYKxOr9$_6H zEcy%05+>FT_*81H=F;O`S}wN^jQAipLn6NK9pJvEX*Inp_&r*nMS0e8K(lBJy&o9T z3Lk!fIF_P3CyU9XHT>M;D*f>oVzs398J@IHv)-^VoY40?dcmH1-$VBN)0gbrNwf%3 zx4D+L8>sIa*Y9CsN)TF6QH7wX^aw=b!+dBrmG%sz9e!{D^M6Oi%KA zH11`z`|cO_10-F&PgR4cE~$K_2#5}$-X{2h!~lSNW5w{Ub^(UcZqL?o72Cogku*wG zDFo3ki)uGeg-}N`RTE>w{svG8&7xDjRaV+VeQDRQ7)PBZP}w^7Pp?loSlq@+_z|`0 zS4oRGVm>)w^1MB)13z*(wLzqUN31t>MzHgeYB{w!`$uo=w2^Mj8MJ8MH4SiTcQQb` zV=IY|!)V8kj@k#_|A>9ynMdvP+@y_W({84gmveUY#%;U5yk@nk=HqdG8MFKZc`Vwj z(Tb*QdAnkt|H?P)-~Rq*?CPC)-=I=woLaoPI}hXByh|8Za?f5fquPca(>?_eRrGO+ z$e+>S7xKBZZ;+5%+5r5@r_vmYw5~L;Xs7v&az$$gVu66J{TYq&;7$G0f2E+?Ac1#B z+>x1Ch$B7;95XS9R80`R3BU+;R%*L#>o_H)3Y;hj<-Sj5K-emtcaxZS0Btf!c%;px zG66wHeA2Ce_5&zsI|(9OU5TnZQxVZ#r+dkRmKF$(V3E3`j0#Y{jo5AHA%3%p=3 zVOX37^e;>u`66_|wU8b7F?A%SEoQ?nI5IVAPd;+dKJ@$(_SB>2Y-Sv(4uQ}{)$%#Q zxTOu8&I(?8(xVdXGgRMT1HO;@V$=pqFKK`6=DgiqTD5d`#CH}Yh^n+CEw-R8(u5)<#Qbp5@}8Bz9xaxi_oFza+wD0JeJd~H?>43(N1Bb&T3G4 zfX?JS4A7tCoyz&!lew=QvS&}uIx~n0qVuMyHz7zhAM zkHM+3QSyv+ZlB`hk&$7-owR617U;Xo0Ev8nBB1|pb056R%X+0uQ^>*8Z!hV&q(Eo? zX>vusNqFh(DH|q67NZGHO_VgW8>A)mDT%Malm#2iL~!Je+J|0v3{M_^VQzwZs9mJJ zQPdLkhTU0O^G&N&OrIaXL|fV_N_Os6V4I3!W<_~ope7+ z8c^#7oA@P+*iD}pNQ5037zp+(N}wpWRWAANW=9E3Anliyqvu*vHv`{GJfb&;edUqj z?xZJE2tWaRYkXat_N85n^(AkV{}Ral?DrtJW|zQ+w*Ayub|)w%hYacCi>8r; zW4&6QFII_Ri+W1WvS|QCRc)s#6$`|#ahn(&BrJm9uFjPhWS0STv$#??eJ+A)!J`#OO|*y5NJT1FXX}zAaqaK!vF#DL_!`Q-9E9=mvv7K>)?kYvq!qp zNPpaOi6%BnhywvCE|m@9D>zMZL5OlJf&^%)f3r~b<=Lhj902p#K@cNk=)9hhp^S|Y zJEtJH;&wShILUPee$VFBsfx#RSodo7mhg!V;##`VGW`&rV%+mO>nj8`{bn)*qhae>|V=~8r zk2=DII93I9tDAdihXF+Ro+;?5*MpKC)teR6;tU+8IcvVtxwumtDW^lo^$8;G%L5Li zO~=I5EDDE9VTdub)w(QcW}spM^{T~{nvRY`jp_6YWeq}u(Ym@;^|ZDj$yFS!1rseG z33)#8K|7k;7qaD)N+Ql2BPQoLsDYs*Df9$9}fZ^u+ioN*FSM86#`gN;g?_=0*90Dt2_jT@2M=u|u zw{G3NZyW3DmS+4~FVT4QuEojYN1WlC^%CQ2;~0-vT05?QS6kDx14;h87JP(br#iSu zAz;!HBDUr#LC;mYU5R~JBXKkFL z)5NNNpALd^`AE|F3@msj9uB9h5U=@s*(dD|@_&?(8K@AN6{~ey-7Ncyp#oU5YZ{DR z#MH({`4T!GPl%Ia1Bu)|N*l-=>1@9;Hc}&!!=#R+3iUP-IPg$2i`M7>vyh=!ywiad za9$J9wmtdSWj{zkKX{$n*s_I%73_7zN<0_bj&=y9uuF~}EM$u~wx9pntM=t@UAMa{ z1vFgTo__jCJAP~$#1pj;^SfpOS`k{ftHbC?>~6W}|HwS)4+EFon)CIHx6IBzAp$6E&|XHKtt3fX#nhIVLVGFX=){QgwTpLKuaDs{$Ul&+Q-50SY-%692BU#dFW8f)L72_Y zFF5h_T_=V~LWCsw1m#-YH%oHI?Wk|j=DC2@t5%A;MR2JyF_9$LB^SHiOl0lm^1A)T zr~kly`?G&+Z{A%pOAOonwJocM;&%1!g3ZGe!CnH!L!Sx8tharWgm7A)PY$uP+ z+Q^`OM*)&hPP2sC!0HE90~6wEL)jDzM+tByqTN|sba=GsGY1Rtfv`OiA^d^)4ci9%++8sYd968ipbC%DI46IqkGj z-|~We4WG8uUM{!c&ou|`FL3fe44?r^W$WMyF`lyEQqramowOljEBVqc!uD}+e{NMgjahqEmRN#1j7>Zg?yr?UemTGkR>Dni#T61X6kJd?$@p&c^=XnzR!Vga?&LA zTCCFPBH9b}S04_~q)5I#%$Q)8o;2l_yw?=9Si;ZyV%IER{3@HJ8T78&(Vv#ENWUT-UFyWPfS zjxquBxc3*UowxD@UxXcC<-CT<+tbjQjwpXl?0I?;2WAE)G(+@jL1u2-4B8 zkf&3E;Zg#~gF zb_5Srk$xQr6mE!fi-b;$0)*lifk+%+d4C`mDdKrW3oq3`V~B`aN{xUl{94Lme<`m0 zg4#z1^ID70(9g)lOAcdCHTUgPz8Xj{5?euv;skn{(NnCf&6x^^z?qeZ*D7@>;~*pr zhafVang!g(+=!vILMJt=aNI(5 zd+5?}n?}pDG(`<89dICRHk@KSE&Wu5>o)$%ArbN$`1*g+m;o-hO>5v+=tUDhYe9pggnX7HzK!NPU5LuHoIaVT-f8HYZ7rue!B@woMlp9+E?uyx@lhL0C5YcAiMu655C!5a68pCa zU-gp8o7ZmPxap)%`6Mv5oLo3X8%45&v^w%di&vWXni(JSDQ>}+OlSQBkk_x>K(jJ# z5aE6lZ3;iawKP==FVfE)6e3BQd+Czag+{N-D?}5Ww6-FKETL5#u`1^c!J<8)D~kw1 znmAF2@`C%a{wYQlhBUu4HZ*8@@ll=lwl9?v&`6h5tK`6^Z@Zi+{*j<5Ue~~atz+C^ z@Z!)0#8T)asg{2bY=@Q=5Sp~E-iJm7I%h^-MeN1{cAOj=lor#szRjU%d z>RcebN~idJJ-3BsK($jZhT7MzWVJstIqn-{f}?5bHqCbz4#p$mF0O6ZYHr(?n6=9{ z9FU zQrZ+J(x}w0h^)E_vt(|9jja%$d)|#RUB@n;J7c5Rb}jIzzx&JU5Q_L)wq6b*?aUoJ zM!f(&{Q!ib4oyHssXslY-y(*A(!p;WiP?oWoqUqp$^%Z^LpN>0RTSfM6w;UhI)r@c zNI2wMiS*o88#E-ernIpHqR8wiy3tBxp%k2*phn!JQ$Zg)|J2G3-kidFv#li{M4F`lW2f(4p zA~C3d)pu!NeKI)z{Q)pdJHW{Ocp_!nMZJGXVIdi2(>SG@9l^@-j5U4eZ>b)TMM#g; z8Km0a7JW7C7@RDnt8HYyorwpU=m1}Ih}!16RBpW?I}{M5Yswk4kWLn%x*=s1Z&my2 zHUCc_==bWVm$1(E18~jxtA81oC=cB{0NcvyhO2V#Q*Cw$qz2@l$WH5-qLoHd@xFG` zglu&~zty7|%3zl+h3FN=f~z&Fwz9fzrE<%vL=w_aTBnd{&?ZyEP$P*GedV#`KTmRu7NsH#4+t2hNc zd%J)n(Q>lX1!IBRgS~=c4`~{0yr_^hgNK0A7lmY-`OcVJQ^*d>hAO=D4I%=9B&HS1 z=C%pr#1yor-vXqj_qD#cMesyFAljRCD1tmRJb?FIwcGQH#L{@@5Px`Zz@0noR??{` z2|wUDcsEfqqD}1j%=82dM7#^JHgGhx=yiuS?#?fxrUNF>22?p~GHqf&oy)A%KVM66 zgbV6mRkVy2()kBk*U&7a02hQogb`e8d0ysCKXfV6Al%7Ekmi=}R4A9|n=z`ih{=ET z5)77sd#I(8&k4%p_2Z}M3HSMUEDMfhEL4=cIBt{W&)t>6P zDP}<09V@310oJ(PSLo8uVHM~5u_=HYET?imcv&)XT$VNAdiLu2GNbD$$+M`ovO za4<#uUb69#A?l^kloY1S!om{u@G(%s3rl$TZ(gHiw3K`f%E%OoAq0=!W$c|E5^R_W z*~r+0d+~blBaa>TI#kh=uFf%VhZ@G#2!b(pEQMq0Q{Rk3U}#+O!+g1(@;$$+OYz;* zlh#9%o=L)?j_;rqBH{EC$7bmO0C=oukV3vfs6pv$K~0e=iFdSrP{f2R`~z^xRIqzO zgufaUFt}ItsZJ*~suVje?v-T|BdKrISE%U@CrR(~>+KntGQp{=UgQUC6(O`s7Vs0+ zskWw>wVyx?sHtWjdbeFUa%t?kUOShR)g{p&O|yhbB7zXAMH!tUqSv_v`?(V*@E7Vn zo*LzK&Y}7hKBBQC(pS(F-L&;p$r}hmt%wTbq9V zA9xT#DdAx)l9(i}*FlZ4yD}^K33weND>bVPE_DW^J{0Xl0s6wZ(>8>L(78Nf*Lftq zQpS%FXSlotD%sCGfVc|NL>zy&0SOYGh6gkD(4`B0Y@A*TQ+TVX@gKaV9Dqa_3d`0>qI?e=+~V^A zYKe-PS<|P6wY-EM01{N*cL;G`pF)TM@YyJdm0#ojStzgx<|ZwjDXt(eM3_ z4GuXoj7qhm4WvP0s9yczsZJe{C$4>r+Jd5A?{XG`N{WW`dY2}g91swvD5QyY7#h&& z;Alz8iI~2tNz>SZ2WHSw@8`~(u@U?Y{|Oxa=~cJ&RXLpa4+w$oCuH8l`7|#9bZMtA z*?w&)lNOWTqnrd*y)P-;%N%qgsdrEOUJ8f@XVIa=PBC>R6|NKviY<_dF{+86^O~h z2{ehqMmbcD8i!dsh5bz*kONM9*%n}6jE=u?t1VSN>!%k(U=qQk`Txn$0fWbbd3yR^ zSjoY_VY_>85v>A}y$%rp?KXf{Cku*(Nf@<>iPTzTaNe=DdqsSjS+i%-etB&DE7x!O zPsh|jgo^`h)fz=>R7-YZYQ#S9*aiEZ7v77rjR|V%gfdKwP(gq6~{e7#6P; zET-|_*>g!}$Viq{r~fiJKuHSJj^NW+v;jn?ZVE5XOwKesgn=~Odvtn=ubPRB`fT0C zR>5J@j-%IKf6MYXXlcF6$Jq4aklGrjw}j4~IZLz?=Nie=91Cp>X!@Ki$cu1$zNY@$ zcJuCnEiEovqXHwM@h;BI+6SI~+|J@)52rCbEeNmI&Jmg>Ospkkt&>Ewtc);4+cJfU zu%Mr$1=uAd&LI5LYvZMo);@$R-3i2EPZIH!_7lE8OSR~eKW+}xoMy<5%uM^j4`cL@ z%$2jskyK8O*VstfRjVQcw^wQlP-GrtQq;SYhD?WA*0mg0{ZyUW@IorZr_70xbdr0+ zl-0O}L=OY$Lms+RypSNm>g}5vuu9!}e^e5wWfHW1zUGrf5)f3X7ic>Jj0^?SwR*U` zp7T{dZD7?dCV{iIx`A0r`uTyHch_4{iYyczoIP_2KVra7?b3$YA$*AfQZEyzP;X;x z!(X5cqw4kBiVhTR6;n>JYGt^nG+(&z2tWHkfc&h16_9!5_s_)8_2|MT!<1Q z^3Y!}<)2RS_UT>*lA!4coxB0TwZf%!a{ZFHyjj-_f{Y{wDj1DOr*KPs@Ll~;NKfSG zNTsS5Uv1>3`qmzN!J`=2rI|89NFp8_3tJ`Hkj3=5hPK`d3>czVkcX_LVNK4!z?cCs z24IHhKl2tXzLZkp}r2p*5*(-b$n2d@Q^VJPQC1L{`^@#08>83Frl6n zf}|OnwW3vWOZMpbOD-6E zDvLN?UkC%Cr%xV}Y2zEVkO8>EMWPbRbf7k_x4h~L_KC;$Ke07*qoM6N<$f}Y2R=Kufz literal 0 HcmV?d00001 diff --git a/server/fabric_26_3/build.gradle.kts b/server/fabric_26_3/build.gradle.kts new file mode 100644 index 00000000..eca95f96 --- /dev/null +++ b/server/fabric_26_3/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + id("net.fabricmc.fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +dependencies { + minecraft("com.mojang:minecraft:26.3") + implementation("net.fabricmc:fabric-loader:0.19.5") + + implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") + + implementation(include("net.kyori:adventure-platform-fabric:7.2.0")!!) +} + +tasks { + processResources { + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + + shadowJar { + archiveFileName.set("[fabric-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..8d0e96d4 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.comm; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; + +public class AllMusicInit implements ModInitializer { + @Override + public void onInitialize() { + PayloadTypeRegistry.clientboundPlay().register(MusicCodec.ID, MusicCodec.CODEC); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..6b0cee70 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,23 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier ChannelID = Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type ID = new Type<>(ChannelID); + public static final StreamCodec CODEC = + StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + buf -> new MusicCodec(MusicPacketCodec.decode(buf))); + + @Override + public @NonNull Type type() { + return ID; + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java new file mode 100644 index 00000000..bc0bf10c --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.minecraft.server.MinecraftServer; + +import java.io.File; + +public class AllMusicServer implements ModInitializer { + public static MinecraftServer server; + + @Override + public void onInitialize() { + AllMusic.log = new LogFabric(); + AllMusic.side = new SideFabric(); + + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) + -> CommandFabric.instance.register(dispatcher)); + + ServerLifecycleEvents.SERVER_STARTED.register((a) -> { + server = a; + AllMusic.init(new File(AllMusic.SERVER_DIR)); + AllMusic.start(); + Tasks.init(); + }); + + ServerLifecycleEvents.SERVER_STOPPING.register((a) -> { + AllMusic.stop(); + }); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java new file mode 100644 index 00000000..5bf02199 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java @@ -0,0 +1,69 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class CommandFabric implements Command, Predicate, SuggestionProvider { + public static CommandFabric instance = new CommandFabric(); + + public void register(CommandDispatcher dispatcher) { + dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) + .then(Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this).executes(this))); + } + + @Override + public int run(CommandContext context) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, arg.length); + + CommandEX.execute(item, context.getSource().getTextName(), arg); + + return 0; + } + + @Override + public boolean test(CommandSourceStack stack) { + return true; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, temp.length - 1); + + builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); + + List results = CommandEX.getTabList(item, item.getTextName(), arg); + + String remaining = builder.getRemaining().trim(); + + for (String s : results) { + if (s.startsWith(remaining)) { + builder.suggest(s); + } + } + + return builder.buildFuture(); + } +} \ No newline at end of file diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java new file mode 100644 index 00000000..6b749f8e --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java @@ -0,0 +1,12 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + + +public class LogFabric implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicServer.server.sendMessage(data); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java new file mode 100644 index 00000000..c6192e39 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java @@ -0,0 +1,157 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.comm.MusicCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.InteractionResult; + +import java.io.File; +import java.util.Collection; + +public class SideFabric extends BaseSide { + @Override + public void runTask(Runnable run) { + AllMusicServer.server.execute(run); + } + + @Override + public void runTask(Runnable run1, int delay) { + Tasks.add(new TaskItem() {{ + tick = delay; + run = run1; + }}); + } + + @Override + public boolean checkPermission(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.permissions() == PermissionSet.ALL_PERMISSIONS; + } + + @Override + public boolean isPlayer(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.isPlayer(); + } + + @Override + public boolean checkPermission(Object player, String permission) { + return checkPermission(player); + } + + @Override + public boolean needPlay(boolean islist) { + for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!com.coloryr.allmusic.server.core.AllMusic.isSkip(player.getName().getString(), null, false, islist)) { + return true; + } + } + return false; + } + + @Override + public Collection getPlayers() { + return AllMusicServer.server.getPlayerList().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ServerPlayer player1) { + return player1.getName().getString(); + } + + return null; + } + + @Override + public String getPlayerServer(Object player) { + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ServerPlayer player1) { + send(player1, pack); + } + } + + @Override + public Object getPlayer(String player) { + return AllMusicServer.server.getPlayerList().getPlayer(player); + } + + @Override + public void sendBar(Object player, net.kyori.adventure.text.Component data) { + if (player instanceof ServerPlayer player1) { + player1.sendActionBar(data); + } + } + + @Override + public File getFolder() { + return new File(AllMusic.SERVER_DIR); + } + + @Override + public void broadcast(net.kyori.adventure.text.Component message) { + for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false)) { + player.sendMessage(message); + } + } + } + + @Override + public void sendMessage(Object obj, net.kyori.adventure.text.Component message) { + if (obj instanceof CommandSourceStack source) { + source.sendMessage(message); + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + return MusicPlayEvent.EVENT.invoker().interact(obj) != InteractionResult.PASS; + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + CommandSourceStack source = (CommandSourceStack) obj; + return MusicAddEvent.EVENT.invoker().interact(source.getPlayer(), music) != InteractionResult.PASS; + } + + @Override + public Component miniMessage(String input) { + MiniMessage mm = MiniMessage.miniMessage(); + return mm.deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.runCommand(command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.suggestCommand(command)); + } + + private void send(ServerPlayer players, MusicPack data) { + if (players == null) + return; + runTask(() -> ServerPlayNetworking.send(players, new MusicCodec(data))); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java new file mode 100644 index 00000000..40f9a5b9 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java @@ -0,0 +1,6 @@ +package com.coloryr.allmusic.server; + +public class TaskItem { + public int tick; + public Runnable run; +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java new file mode 100644 index 00000000..d4e72d39 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java @@ -0,0 +1,27 @@ +package com.coloryr.allmusic.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tasks { + private static final List taskItems = new CopyOnWriteArrayList<>(); + + public static void init() { + AllMusicServer.server.addTickable(Tasks::tick); + } + + public static void tick() { + for (var item : new ArrayList<>(taskItems)) { + item.tick--; + if (item.tick <= 0) { + taskItems.remove(item); + item.run.run(); + } + } + } + + public static void add(TaskItem item) { + taskItems.add(item); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..6ef1835c --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; + +public interface MusicAddEvent { + Event EVENT = EventFactory.createArrayBacked(MusicAddEvent.class, + (listeners) -> (player, music) -> { + for (MusicAddEvent listener : listeners) { + InteractionResult result = listener.interact(player, music); + + if (result != InteractionResult.PASS) { + return result; + } + } + + return InteractionResult.PASS; + }); + + InteractionResult interact(ServerPlayer player, PlayerAddMusicObj music); +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..852895f1 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,29 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.world.InteractionResult; + +public interface MusicPlayEvent { + Event EVENT = EventFactory.createArrayBacked(MusicPlayEvent.class, + (listeners) -> (music) -> { + for (MusicPlayEvent listener : listeners) { + InteractionResult result = listener.interact(music); + + if (result != InteractionResult.PASS) { + return result; + } + } + + return InteractionResult.PASS; + }); + + /** + * 音乐播放事件,返回PASS表示通过 + * + * @param music 歌曲信息 + * @return 是否通过 + */ + InteractionResult interact(SongInfoObj music); +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java new file mode 100644 index 00000000..60e133d4 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java @@ -0,0 +1,25 @@ +package com.coloryr.allmusic.server.mixin; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import net.minecraft.network.Connection; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.CommonListenerCookie; +import net.minecraft.server.players.PlayerList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerList.class) +public class ServerPlayerMixin { + @Inject(at = @At("HEAD"), method = "remove") + private void remove(ServerPlayer entity, CallbackInfo info) { + PlayMusic.removeNowPlayPlayer(entity.getName().getString()); + } + + @Inject(at = @At("TAIL"), method = "placeNewPlayer") + private void add(Connection connection, ServerPlayer player, CommonListenerCookie clientData, CallbackInfo ci) { + AllMusic.joinPlay(player.getName().getString()); + } +} diff --git a/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json b/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json new file mode 100644 index 00000000..6e607dbc --- /dev/null +++ b/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json @@ -0,0 +1,17 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.coloryr.allmusic.server.mixin", + "compatibilityLevel": "JAVA_25", + "server": [ + ], + "mixins": [ + "ServerPlayerMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +} diff --git a/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png b/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/server/fabric_26_3/src/main/resources/fabric.mod.json b/server/fabric_26_3/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..b8127dee --- /dev/null +++ b/server/fabric_26_3/src/main/resources/fabric.mod.json @@ -0,0 +1,37 @@ +{ + "schemaVersion": 1, + "id": "allmusic_server", + "version": "${version}", + "name": "AllMusic Server", + "description": "AllMusic Server Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic_server/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit", + "com.coloryr.allmusic.server.AllMusicServer" + ] + }, + "mixins": [ + { + "config": "allmusic_server.mixins.json", + "environment": "server" + } + ], + "depends": { + "fabricloader": ">=0.19.5", + "minecraft": ">=26.3", + "fabric-api": "*", + "adventure-platform-fabric": ">=7.2.0" + }, + "suggests": { + } +} diff --git a/server/neoforge_26_3/build.gradle.kts b/server/neoforge_26_3/build.gradle.kts new file mode 100644 index 00000000..441c444f --- /dev/null +++ b/server/neoforge_26_3/build.gradle.kts @@ -0,0 +1,51 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + neoForge("net.neoforged:neoforge:26.3.0.6-beta") + + implementation(include("net.kyori:adventure-platform-neoforge:7.2.0")!!) +} + +tasks { + processResources { + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + + shadowJar { + archiveFileName.set("[neoforge-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/server/neoforge_26_3/gradle.properties b/server/neoforge_26_3/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/server/neoforge_26_3/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..d9282960 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.comm; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +@Mod(AllMusicInit.MODID) +public class AllMusicInit implements IPayloadHandler { + + public static final String MODID = "allmusic"; + + public static IPayloadHandler handler; + + public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { + modEventBus.addListener(this::register); + } + + public void register(final RegisterPayloadHandlersEvent event) { + event.registrar("1.0") + .optional() + .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); + } + + @Override + public void handle(MusicCodec data, IPayloadContext iPayloadContext) { + if (handler != null) { + handler.handle(data, iPayloadContext); + } + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..434fa510 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier channel = + Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type TYPE = new Type<>(channel); + public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + (buf) -> { + return new MusicCodec(MusicPacketCodec.decode(buf)); + }); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java new file mode 100644 index 00000000..2e1109f1 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java @@ -0,0 +1,74 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import com.mojang.brigadier.CommandDispatcher; +import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.MinecraftServer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +@EventBusSubscriber(modid = "allmusic_server", value = Dist.DEDICATED_SERVER) +public class AllMusicServer { + public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Server"); + public static MinecraftServer server; + public static MinecraftServerAudiences audiences; + + @SubscribeEvent + public static void commonSetup(final FMLCommonSetupEvent event) { + AllMusic.log = new LogNeoForge(); + AllMusic.side = new SideNeoForge(); + } + + @SubscribeEvent + public static void onRegisterCommands(RegisterCommandsEvent event) { + if (event.getCommandSelection() != Commands.CommandSelection.INTEGRATED) { + CommandDispatcher dispatcher = event.getDispatcher(); + CommandNeoForge.instance.register(dispatcher); + } + } + + @SubscribeEvent + public static void onServerStarting(ServerStartedEvent event) { + if (event.getServer().isDedicatedServer()) { + audiences = MinecraftServerAudiences.of(event.getServer()); + server = event.getServer(); + AllMusic.init(new File(AllMusic.SERVER_DIR)); + AllMusic.start(); + Tasks.init(); + } + } + + @SubscribeEvent + public static void onServerStopping(ServerStoppingEvent event) { + if (event.getServer().isDedicatedServer()) { + AllMusic.stop(); + } + } + + @SubscribeEvent + public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + if (server != null) { + PlayMusic.removeNowPlayPlayer(event.getEntity().getName().getString()); + } + } + + @SubscribeEvent + public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { + if (server != null) { + AllMusic.joinPlay(event.getEntity().getName().getString()); + } + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java new file mode 100644 index 00000000..f6275e4e --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java @@ -0,0 +1,66 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class CommandNeoForge implements Command, Predicate, SuggestionProvider { + public static CommandNeoForge instance = new CommandNeoForge(); + + public void register(CommandDispatcher dispatcher) { + dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) + .then(Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this).executes(this))); + } + + @Override + public int run(CommandContext context) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, arg.length); + + CommandEX.execute(item, context.getSource().getTextName(), arg); + + return 0; + } + + @Override + public boolean test(CommandSourceStack stack) { + return true; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, temp.length - 1); + + if (input.endsWith(" ")) + builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); + + List results = CommandEX.getTabList(item, item.getTextName(), arg); + + for (String s : results) { + builder.suggest(s); + } + + return builder.buildFuture(); + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java new file mode 100644 index 00000000..2b12188a --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + +public class LogNeoForge implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicServer.audiences.console().sendMessage(data); + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java new file mode 100644 index 00000000..7e79134a --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java @@ -0,0 +1,161 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.comm.MusicCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.PermissionSet; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.network.PacketDistributor; + +import java.io.File; +import java.util.Collection; + +public class SideNeoForge extends BaseSide { + @Override + public void runTask(Runnable run) { + AllMusicServer.server.execute(run); + } + + @Override + public void runTask(Runnable run1, int delay) { + Tasks.add(new TaskItem() {{ + tick = delay; + run = run1; + }}); + } + + @Override + public boolean checkPermission(Object player, String permission) { + return checkPermission(player); + } + + @Override + public boolean checkPermission(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.permissions() == PermissionSet.ALL_PERMISSIONS; + } + + @Override + public boolean isPlayer(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.isPlayer(); + } + + @Override + public boolean needPlay(boolean islist) { + for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false, islist)) { + return true; + } + } + return false; + } + + @Override + public Collection getPlayers() { + return AllMusicServer.server.getPlayerList().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ServerPlayer player1) { + return player1.getName().getString(); + } + + return null; + } + + @Override + public String getPlayerServer(Object player) { + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ServerPlayer player1) { + send(player1, pack); + } + } + + @Override + public Object getPlayer(String player) { + return AllMusicServer.server.getPlayerList().getPlayerByName(player); + } + + @Override + public void sendBar(Object player, Component data) { + if (player instanceof ServerPlayer player1) { + AllMusicServer.audiences.audience(player1).sendActionBar(data); + } + } + + @Override + public File getFolder() { + return new File(AllMusic.SERVER_DIR); + } + + @Override + public void broadcast(Component message) { + for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false)) { + AllMusicServer.audiences.audience(player).sendActionBar(message); + } + } + } + + @Override + public void sendMessage(Object obj, Component message) { + if (obj instanceof CommandSourceStack source) { + AllMusicServer.audiences.audience(source).sendActionBar(message); + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + MusicPlayEvent event = new MusicPlayEvent(obj); + NeoForge.EVENT_BUS.post(event); + return event.isCancel(); + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + CommandSourceStack source = (CommandSourceStack) obj; + MusicAddEvent event = new MusicAddEvent(music, source.getPlayer()); + NeoForge.EVENT_BUS.post(event); + return event.isCancel(); + } + + @Override + public Component miniMessage(String input) { + MiniMessage mm = MiniMessage.miniMessage(); + return mm.deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.runCommand(command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.suggestCommand(command)); + } + + private void send(ServerPlayer players, MusicPack data) { + if (players == null) + return; + runTask(() -> PacketDistributor.sendToPlayer(players, new MusicCodec(data))); + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java new file mode 100644 index 00000000..40f9a5b9 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java @@ -0,0 +1,6 @@ +package com.coloryr.allmusic.server; + +public class TaskItem { + public int tick; + public Runnable run; +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java new file mode 100644 index 00000000..b861abcc --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java @@ -0,0 +1,27 @@ +package com.coloryr.allmusic.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tasks { + private static final List taskItems = new CopyOnWriteArrayList<>(); + + public static void init() { + AllMusicServer.server.addTickable(Tasks::tick); + } + + public static void tick() { + for (TaskItem item : new ArrayList<>(taskItems)) { + item.tick--; + if (item.tick <= 0) { + taskItems.remove(item); + item.run.run(); + } + } + } + + public static void add(TaskItem item) { + taskItems.add(item); + } +} \ No newline at end of file diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..7c730137 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,39 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.bus.api.Event; + +public class MusicAddEvent extends Event { + /** + * 添加的音乐 + */ + private final PlayerAddMusicObj music; + /** + * 添加者 + */ + private final ServerPlayer player; + + private boolean cancel; + + public MusicAddEvent(PlayerAddMusicObj id, ServerPlayer player) { + this.music = id; + this.player = player; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public ServerPlayer getPlayer() { + return player; + } + + public PlayerAddMusicObj getMusic() { + return music; + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..b16e0f95 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,25 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.neoforged.bus.api.Event; + +public class MusicPlayEvent extends Event { + private final SongInfoObj music; + private boolean cancel; + + public MusicPlayEvent(SongInfoObj music) { + this.music = music; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public SongInfoObj getMusic() { + return music; + } +} diff --git a/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..177695c3 --- /dev/null +++ b/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,36 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/server/neoforge_26_3/src/main/resources/icon.png b/server/neoforge_26_3/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java index 5156a7d3..c7f211a1 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java @@ -482,7 +482,7 @@ public static int getIdleListSize() { */ private static boolean checkDeep(MusicObj music) { for (MusicObj obj : deep) { - if (Objects.equals(obj.id, music.id) && obj.api == music.api) { + if (Objects.equals(obj.id, music.id) && Objects.equals(obj.api, music.api)) { return true; } } diff --git a/settings.gradle.kts b/settings.gradle.kts index c1a4fd26..235ffed8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ include(":client:fabric_1_21_8") include(":client:fabric_1_21_11") include(":client:fabric_26_1") include(":client:fabric_26_2") +include(":client:fabric_26_3") include(":client:forge_1_7_10") include(":client:forge_1_12_2") @@ -27,6 +28,7 @@ include(":client:neoforge_1_21_6") include(":client:neoforge_1_21_11") include(":client:neoforge_26_1") include(":client:neoforge_26_2") +include(":client:neoforge_26_3") include(":server") @@ -38,6 +40,7 @@ include(":server:fabric_1_21_6") include(":server:fabric_1_21_11") include(":server:fabric_26_1") include(":server:fabric_26_2") +include(":server:fabric_26_3") include(":server:forge_1_7_10") include(":server:forge_1_12_2") @@ -50,6 +53,7 @@ include(":server:neoforge_1_21_6") include(":server:neoforge_1_21_11") include(":server:neoforge_26_1") include(":server:neoforge_26_2") +include(":server:neoforge_26_3") include(":server:spigot") include(":server:paper") @@ -68,11 +72,15 @@ include(":onejar:fabric_1_21_6") include(":onejar:fabric_1_21_11") include(":onejar:fabric_26_1") include(":onejar:fabric_26_2") +include(":onejar:fabric_26_3") + include(":onejar:neoforge_1_21") include(":onejar:neoforge_1_21_4") include(":onejar:neoforge_1_21_6") include(":onejar:neoforge_1_21_11") include(":onejar:neoforge_26_1") +include(":onejar:neoforge_26_2") +include(":onejar:neoforge_26_3") pluginManagement { repositories { From 16e33989c80cf3f6d6c83dffe320402760dd1def Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Sat, 19 Sep 2026 18:36:36 +0800 Subject: [PATCH 32/46] =?UTF-8?q?mp3=E6=8E=A2=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 +- .../coloryr/allmusic/client/core/AllMusicPlayer.java | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 546b1714..53800dd0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.2.3" + version = "4.2.4" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 511b8825..1b2ff0d0 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -197,9 +197,13 @@ private void run() { if (head[0] == 0 && head[1] == 0 && head[2] == 0 && head[3] == 0x1c) { decoder = new M4ADecoder(this); - } else if (head[0] == 'I' && head[1] == 'D' && head[2] == '3') { - decoder = new Mp3Decoder(this); - } else if (head[0] == (byte) 0xFF && head[1] == (byte) 0xFB) { + } else if ((head[0] == 'I' && head[1] == 'D' && head[2] == '3') + || (head[0] == (byte) 0xFF && head[1] == (byte) 0xFB) + || (head[0] == (byte) 0xFF && head[1] == (byte) 0xFA) + || (head[0] == (byte) 0xFF && head[1] == (byte) 0xF3) + || (head[0] == (byte) 0xFF && head[1] == (byte) 0xF2) + || (head[0] == (byte) 0xFF && head[1] == (byte) 0xE3) + || (head[0] == (byte) 0xFF && head[1] == (byte) 0xE2)) { decoder = new Mp3Decoder(this); } else { decoder = new OggDecoder(this); From edab660b0cf8ba4f570175f15f31911fcc176a9a Mon Sep 17 00:00:00 2001 From: coloryr <402067010@qq.com> Date: Thu, 24 Sep 2026 19:12:25 +0800 Subject: [PATCH 33/46] 123 --- .../java/com/coloryr/allmusic/server/core/AllMusic.java | 7 +++++-- .../com/coloryr/allmusic/server/core/music/PlayMusic.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java index d4f07ac0..5b124a55 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java @@ -153,7 +153,7 @@ public static boolean isSkip(String name, String server, boolean checkPlay) { return true; if (!checkPlay) return false; - return PlayMusic.containNowPlay(name); + return !PlayMusic.containNowPlay(name); } catch (NoSuchElementException e) { return true; } @@ -179,7 +179,7 @@ public static boolean isSkip(String name, String server, boolean checkPlay, bool return true; if (!checkPlay) return false; - return PlayMusic.containNowPlay(name); + return !PlayMusic.containNowPlay(name); } catch (NoSuchElementException e) { return true; } @@ -390,6 +390,9 @@ public static void joinPlayNow(String player) { if (BanSave.checkMuteListPlayer(player1)) { return; } + if (PlayMusic.containNowPlay(player1)) { + return; + } AllMusic.side.runTask(() -> { SongInfoObj music = PlayMusic.nowPlayMusic; diff --git a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java index c7f211a1..9cbd671a 100644 --- a/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/PlayMusic.java @@ -570,7 +570,7 @@ public static Set getNowPlayPlayer() { */ public static boolean containNowPlay(String player) { player = player.toLowerCase(); - return !nowPlayPlayer.contains(player); + return nowPlayPlayer.contains(player); } /** From 7aebdb2962c37bb937698f1c98b08220666f6800 Mon Sep 17 00:00:00 2001 From: Color_yr <402067010@qq.com> Date: Fri, 25 Sep 2026 10:16:45 +0800 Subject: [PATCH 34/46] Update build.gradle.kts --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 53800dd0..c6a9b98c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,7 @@ allprojects { apply(plugin = "com.gradleup.shadow") group = "com.coloryr" - version = "4.2.4" + version = "4.2.5" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 From f6efcb7afc4523590fe0ebff64a801199a84b6e9 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 18:55:10 +0800 Subject: [PATCH 35/46] chore: add 26.3 server modules --- README.md | 4 ++ gradle/wrapper/gradle-wrapper.properties | 4 +- server/folia_26_3/build.gradle.kts | 33 +++++++++++++++ .../folia_26_3/src/main/resources/plugin.yml | 12 ++++++ server/paper_26_3/build.gradle.kts | 33 +++++++++++++++ .../paper_26_3/src/main/resources/plugin.yml | 11 +++++ server/spigot_26_3/build.gradle.kts | 41 +++++++++++++++++++ .../spigot_26_3/src/main/resources/plugin.yml | 11 +++++ server/velocity_26_3/build.gradle.kts | 33 +++++++++++++++ .../src/main/resources/velocity-plugin.json | 12 ++++++ settings.gradle.kts | 6 +++ 11 files changed, 198 insertions(+), 2 deletions(-) create mode 100644 server/folia_26_3/build.gradle.kts create mode 100644 server/folia_26_3/src/main/resources/plugin.yml create mode 100644 server/paper_26_3/build.gradle.kts create mode 100644 server/paper_26_3/src/main/resources/plugin.yml create mode 100644 server/spigot_26_3/build.gradle.kts create mode 100644 server/spigot_26_3/src/main/resources/plugin.yml create mode 100644 server/velocity_26_3/build.gradle.kts create mode 100644 server/velocity_26_3/src/main/resources/velocity-plugin.json diff --git a/README.md b/README.md index c35baa1d..7afad397 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ - Folia - Velocity +本分支新增服务端 26.3 构建模块:`:server:paper_26_3`、`:server:folia_26_3`、`:server:spigot_26_3` 与 `:server:velocity_26_3`。Paper/Folia 模块以 Paper API `26.3.build.157-beta`、Java 25 编译;Spigot 模块以 Spigot API `26.3-R0.1-SNAPSHOT` 编译;Velocity 模块只依赖 Velocity API `4.2.1-SNAPSHOT`,不依赖 Paper API。旧服务端模块保留。 + +本次服务端升级不宣称 Fabric、Forge 或 NeoForge 客户端已经支持 26.3;客户端/模组模块需另行迁移和实机验证。 + 模组支持的服务器 - Forge - NeoForge diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b52fb7e7..83c47d09 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip -networkTimeout=10000 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip +networkTimeout=30000 retries=0 retryBackOffMs=500 validateDistributionUrl=true diff --git a/server/folia_26_3/build.gradle.kts b/server/folia_26_3/build.gradle.kts new file mode 100644 index 00000000..0452979d --- /dev/null +++ b/server/folia_26_3/build.gradle.kts @@ -0,0 +1,33 @@ +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") +} + +sourceSets { + named("main") { + java.srcDir("../folia/src/main/java") + } +} + +dependencies { + // Folia 26.3 API is not published separately; compile the Folia-safe code against Paper 26.3. + compileOnly("io.papermc.paper:paper-api:26.3.build.157-beta") + compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") + compileOnly("me.clip:placeholderapi:2.11.6") +} + +tasks { + processResources { + filesMatching("plugin.yml") { + expand("version" to project.version) + } + } + + shadowJar { + archiveFileName.set("[folia-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } +} diff --git a/server/folia_26_3/src/main/resources/plugin.yml b/server/folia_26_3/src/main/resources/plugin.yml new file mode 100644 index 00000000..d1f78c2a --- /dev/null +++ b/server/folia_26_3/src/main/resources/plugin.yml @@ -0,0 +1,12 @@ +name: allmusic +main: com.coloryr.allmusic.server.AllMusicFolia +author: Color_yr +version: ${version} +folia-supported: true +softdepend: [ PlaceholderAPI, Vault, Essentials, CMI ] +api-version: '26.3' + +commands: + music: + description: AllMusic + usage: §4[AllMusic]§6/music help diff --git a/server/paper_26_3/build.gradle.kts b/server/paper_26_3/build.gradle.kts new file mode 100644 index 00000000..a7f1893f --- /dev/null +++ b/server/paper_26_3/build.gradle.kts @@ -0,0 +1,33 @@ +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +repositories { + maven("https://repo.papermc.io/repository/maven-public/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") +} + +sourceSets { + named("main") { + java.srcDir("../paper/src/main/java") + } +} + +dependencies { + compileOnly("io.papermc.paper:paper-api:26.3.build.157-beta") + compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") + compileOnly("me.clip:placeholderapi:2.11.6") + compileOnly("net.kyori:adventure-text-minimessage:${Versions.minimessage}") +} + +tasks { + processResources { + filesMatching("plugin.yml") { + expand("version" to project.version) + } + } + + shadowJar { + archiveFileName.set("[paper-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } +} diff --git a/server/paper_26_3/src/main/resources/plugin.yml b/server/paper_26_3/src/main/resources/plugin.yml new file mode 100644 index 00000000..cd779d32 --- /dev/null +++ b/server/paper_26_3/src/main/resources/plugin.yml @@ -0,0 +1,11 @@ +name: allmusic +main: com.coloryr.allmusic.server.AllMusicPaper +author: Color_yr +version: ${version} +softdepend: [ PlaceholderAPI, Vault, Essentials, CMI ] +api-version: '26.3' + +commands: + music: + description: AllMusic + usage: §4[AllMusic]§6/music help diff --git a/server/spigot_26_3/build.gradle.kts b/server/spigot_26_3/build.gradle.kts new file mode 100644 index 00000000..f2d3776d --- /dev/null +++ b/server/spigot_26_3/build.gradle.kts @@ -0,0 +1,41 @@ +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +repositories { + maven("https://hub.spigotmc.org/nexus/content/groups/public/") + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") +} + +sourceSets { + named("main") { + java.srcDir("../spigot/src/main/java") + } +} + +dependencies { + compileOnly("org.spigotmc:spigot-api:26.3-R0.1-SNAPSHOT") + compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") + compileOnly("me.clip:placeholderapi:2.11.6") + + shadowImplementation("net.kyori:adventure-platform-bukkit:4.4.1") + shadowImplementation("net.kyori:adventure-text-minimessage:4.26.1") + shadowImplementation("net.kyori:adventure-api:4.8.1") + shadowImplementation("net.kyori:adventure-text-serializer-gson:4.8.1") + shadowImplementation("net.kyori:adventure-text-serializer-legacy:4.8.1") + shadowImplementation("net.kyori:adventure-text-serializer-plain:4.8.1") + shadowImplementation("net.kyori:adventure-key:4.8.1") +} + +tasks { + processResources { + filesMatching("plugin.yml") { + expand("version" to project.version) + } + } + + shadowJar { + archiveFileName.set("[spigot-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") + } +} diff --git a/server/spigot_26_3/src/main/resources/plugin.yml b/server/spigot_26_3/src/main/resources/plugin.yml new file mode 100644 index 00000000..198374f6 --- /dev/null +++ b/server/spigot_26_3/src/main/resources/plugin.yml @@ -0,0 +1,11 @@ +name: allmusic +main: com.coloryr.allmusic.server.AllMusicBukkit +author: Color_yr +version: ${version} +softdepend: [ PlaceholderAPI, Vault, Essentials, CMI ] +api-version: '26.3' + +commands: + music: + description: AllMusic + usage: §4[AllMusic]§6/music help diff --git a/server/velocity_26_3/build.gradle.kts b/server/velocity_26_3/build.gradle.kts new file mode 100644 index 00000000..653839d8 --- /dev/null +++ b/server/velocity_26_3/build.gradle.kts @@ -0,0 +1,33 @@ +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +repositories { + maven("https://nexus.velocitypowered.com/repository/maven-public/") + maven("https://repo.papermc.io/repository/maven-public/") +} + +sourceSets { + named("main") { + java.srcDir("../velocity/src/main/java") + } +} + +dependencies { + // Velocity-only module: no Paper API is required. + compileOnly("com.velocitypowered:velocity-api:4.2.1-SNAPSHOT") + annotationProcessor("com.velocitypowered:velocity-api:4.2.1-SNAPSHOT") + compileOnly("net.kyori:adventure-text-minimessage:${Versions.minimessage}") +} + +tasks { + processResources { + filesMatching("velocity-plugin.json") { + expand("version" to project.version) + } + } + + shadowJar { + archiveFileName.set("[velocity-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } +} diff --git a/server/velocity_26_3/src/main/resources/velocity-plugin.json b/server/velocity_26_3/src/main/resources/velocity-plugin.json new file mode 100644 index 00000000..993a173a --- /dev/null +++ b/server/velocity_26_3/src/main/resources/velocity-plugin.json @@ -0,0 +1,12 @@ +{ + "id": "allmusic", + "name": "AllMusic", + "version": "${version}", + "description": "全服点歌插件", + "url": "https://github.com/Coloryr/AllMusic", + "authors": [ + "Color_yr" + ], + "dependencies": [], + "main": "com.coloryr.allmusic.server.AllMusicVelocity" +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 235ffed8..dceaacb3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -62,6 +62,12 @@ include(":server:paper") include(":server:bungeecord") include(":server:velocity") +// Minecraft 26.3 Bukkit-family adapters; legacy server modules above remain unchanged. +include(":server:paper_26_3") +include(":server:folia_26_3") +include(":server:spigot_26_3") +include(":server:velocity_26_3") + include(":onejar") include(":onejar:fabric_1_16_5") From 17b611dcf805cb34b1622cb8d13f30216add5a8e Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 19:20:08 +0800 Subject: [PATCH 36/46] chore: scope 26.3 update to server modules --- client/fabric_26_3/build.gradle.kts | 34 - .../allmusic/client/AllMusicClient.java | 135 ---- .../allmusic/client/CoreRenderTarget.java | 136 ---- .../allmusic/client/FloatRenderState.java | 30 - .../coloryr/allmusic/client/MiniMessage.java | 645 ------------------ .../coloryr/allmusic/client/PicRender.java | 72 -- .../coloryr/allmusic/client/TexRender.java | 109 --- .../allmusic/client/mixins/GuiShow.java | 18 - .../allmusic/client/mixins/QuitServer.java | 16 - .../allmusic/client/mixins/Register.java | 16 - .../allmusic/client/mixins/SoundEvent.java | 38 -- .../coloryr/allmusic/client/mixins/Tick.java | 16 - .../allmusic/client/others/VoiceChat.java | 32 - .../coloryr/allmusic/comm/AllMusicInit.java | 11 - .../com/coloryr/allmusic/comm/MusicCodec.java | 23 - .../resources/allmusic_client.mixins.json | 18 - .../resources/assets/allmusic_client/icon.png | Bin 32781 -> 0 bytes .../src/main/resources/fabric.mod.json | 42 -- client/neoforge_26_3/build.gradle.kts | 40 -- client/neoforge_26_3/gradle.properties | 1 - .../allmusic/client/AllMusicClient.java | 193 ------ .../allmusic/client/CoreRenderTarget.java | 133 ---- .../allmusic/client/FloatRenderState.java | 30 - .../coloryr/allmusic/client/MiniMessage.java | 645 ------------------ .../coloryr/allmusic/client/PicRender.java | 71 -- .../coloryr/allmusic/client/TexRender.java | 109 --- .../allmusic/client/others/ModernUI.java | 15 - .../allmusic/client/others/VoiceChat.java | 34 - .../coloryr/allmusic/comm/AllMusicInit.java | 33 - .../com/coloryr/allmusic/comm/MusicCodec.java | 24 - .../resources/META-INF/neoforge.mods.toml | 42 -- .../neoforge_26_3/src/main/resources/icon.png | Bin 32781 -> 0 bytes onejar/fabric_26_3/build.gradle.kts | 68 -- .../main/resources/assets/allmusic/icon.png | Bin 32781 -> 0 bytes .../src/main/resources/fabric.mod.json | 49 -- onejar/neoforge_26_3/build.gradle.kts | 75 -- onejar/neoforge_26_3/gradle.properties | 1 - .../resources/META-INF/neoforge.mods.toml | 65 -- .../neoforge_26_3/src/main/resources/icon.png | Bin 32781 -> 0 bytes server/fabric_26_3/build.gradle.kts | 37 - .../coloryr/allmusic/comm/AllMusicInit.java | 11 - .../com/coloryr/allmusic/comm/MusicCodec.java | 23 - .../allmusic/server/AllMusicServer.java | 33 - .../allmusic/server/CommandFabric.java | 69 -- .../coloryr/allmusic/server/LogFabric.java | 12 - .../coloryr/allmusic/server/SideFabric.java | 157 ----- .../com/coloryr/allmusic/server/TaskItem.java | 6 - .../com/coloryr/allmusic/server/Tasks.java | 27 - .../allmusic/server/event/MusicAddEvent.java | 24 - .../allmusic/server/event/MusicPlayEvent.java | 29 - .../server/mixin/ServerPlayerMixin.java | 25 - .../resources/allmusic_server.mixins.json | 17 - .../resources/assets/allmusic_server/icon.png | Bin 32781 -> 0 bytes .../src/main/resources/fabric.mod.json | 37 - server/neoforge_26_3/build.gradle.kts | 51 -- server/neoforge_26_3/gradle.properties | 1 - .../coloryr/allmusic/comm/AllMusicInit.java | 33 - .../com/coloryr/allmusic/comm/MusicCodec.java | 24 - .../allmusic/server/AllMusicServer.java | 74 -- .../allmusic/server/CommandNeoForge.java | 66 -- .../coloryr/allmusic/server/LogNeoForge.java | 11 - .../coloryr/allmusic/server/SideNeoForge.java | 161 ----- .../com/coloryr/allmusic/server/TaskItem.java | 6 - .../com/coloryr/allmusic/server/Tasks.java | 27 - .../allmusic/server/event/MusicAddEvent.java | 39 -- .../allmusic/server/event/MusicPlayEvent.java | 25 - .../resources/META-INF/neoforge.mods.toml | 36 - .../neoforge_26_3/src/main/resources/icon.png | Bin 32781 -> 0 bytes settings.gradle.kts | 6 - 69 files changed, 4086 deletions(-) delete mode 100644 client/fabric_26_3/build.gradle.kts delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java delete mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java delete mode 100644 client/fabric_26_3/src/main/resources/allmusic_client.mixins.json delete mode 100644 client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png delete mode 100644 client/fabric_26_3/src/main/resources/fabric.mod.json delete mode 100644 client/neoforge_26_3/build.gradle.kts delete mode 100644 client/neoforge_26_3/gradle.properties delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java delete mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java delete mode 100644 client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml delete mode 100644 client/neoforge_26_3/src/main/resources/icon.png delete mode 100644 onejar/fabric_26_3/build.gradle.kts delete mode 100644 onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png delete mode 100644 onejar/fabric_26_3/src/main/resources/fabric.mod.json delete mode 100644 onejar/neoforge_26_3/build.gradle.kts delete mode 100644 onejar/neoforge_26_3/gradle.properties delete mode 100644 onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml delete mode 100644 onejar/neoforge_26_3/src/main/resources/icon.png delete mode 100644 server/fabric_26_3/build.gradle.kts delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java delete mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java delete mode 100644 server/fabric_26_3/src/main/resources/allmusic_server.mixins.json delete mode 100644 server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png delete mode 100644 server/fabric_26_3/src/main/resources/fabric.mod.json delete mode 100644 server/neoforge_26_3/build.gradle.kts delete mode 100644 server/neoforge_26_3/gradle.properties delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java delete mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java delete mode 100644 server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml delete mode 100644 server/neoforge_26_3/src/main/resources/icon.png diff --git a/client/fabric_26_3/build.gradle.kts b/client/fabric_26_3/build.gradle.kts deleted file mode 100644 index eb685e4a..00000000 --- a/client/fabric_26_3/build.gradle.kts +++ /dev/null @@ -1,34 +0,0 @@ -plugins { - id("net.fabricmc.fabric-loom") version Versions.fabricLoom -} - -java.sourceCompatibility = JavaVersion.VERSION_25 -java.targetCompatibility = JavaVersion.VERSION_25 - -dependencies { - minecraft("com.mojang:minecraft:26.3") - implementation("net.fabricmc:fabric-loader:0.19.5") - - implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") - - compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") -} - -tasks { - processResources { - filesMatching("fabric.mod.json") { - expand( - "version" to project.version - ) - } - } - - shadowJar { - archiveFileName.set("[fabric-26.3]AllMusic_Client-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - } - - build { - dependsOn(shadowJar) - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java deleted file mode 100644 index 97073e42..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicBridge; -import com.coloryr.allmusic.client.core.AllMusicCore; -import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; -import com.coloryr.allmusic.client.core.render.TextFrameBuffer; -import com.coloryr.allmusic.client.core.render.TextureRender; -import com.coloryr.allmusic.comm.MusicCodec; -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.network.Connection; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.sounds.SoundSource; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -public class AllMusicClient implements ClientModInitializer, AllMusicBridge { - public static final String MODID = "allmusic_client"; - public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); - public static GuiGraphicsExtractor context; - - public static void update(GuiGraphicsExtractor draw) { - context = draw; - AllMusicCore.hudUpdate(); - } - - public int getScreenWidth() { - return Minecraft.getInstance().getWindow().getGuiScaledWidth(); - } - - public int getScreenHeight() { - return Minecraft.getInstance().getWindow().getGuiScaledHeight(); - } - - public int getTextWidth(String item) { - return Minecraft.getInstance().font.width(item); - } - - public int getFontHeight() { - return Minecraft.getInstance().font.lineHeight; - } - - public void sendMessage(String data) { - data = "[AllMusic Client]" + data; - LOGGER.warn(data); - String finalData = data; - Minecraft.getInstance().execute(() -> { - if (Minecraft.getInstance().player == null) - return; - Minecraft.getInstance().player.sendSystemMessage(Component.literal(finalData)); - }); - } - - public float getVolume() { - return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); - } - - @Override - public void stopPlayMusic() { - Minecraft.getInstance().getSoundManager().stop(null, SoundSource.MUSIC); - Minecraft.getInstance().getSoundManager().stop(null, SoundSource.RECORDS); - } - - @Override - public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(name); - } - - @Override - public TextureRender makeTextureRender(String file) { - return new TexRender(file); - } - - @Override - public PictureFrameBuffer makePictureRender(int size) { - return new PicRender(size); - } - - @Override - public String readText(String file) { - try { - var man = Minecraft.getInstance().getResourceManager(); - Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); - try (InputStream inputStream = resource.open()) { - byte[] bytes = inputStream.readAllBytes(); - return new String(bytes, StandardCharsets.UTF_8); - } - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - @Override - public InputStream readFile(String file) { - try { - var man = Minecraft.getInstance().getResourceManager(); - Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); - return resource.open(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - @Override - public void kick() { - Minecraft client = Minecraft.getInstance(); - - ClientPacketListener packetListener = client.getConnection(); - if (packetListener != null) { - Connection connection = packetListener.getConnection(); - connection.disconnect(Component.nullToEmpty("Old AllMusic server")); - } - } - - @Override - public void onInitializeClient() { - ClientPlayNetworking.registerGlobalReceiver(MusicCodec.ID, (pack, handler) -> AllMusicCore.packDo(pack.pack())); - - AllMusicCore.init(FabricLoader.getInstance().getConfigDir(), this); - - ClientLifecycleEvents.CLIENT_STOPPING.register((a) -> AllMusicCore.stop()); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java deleted file mode 100644 index 9124fb0d..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicHud; -import com.coloryr.allmusic.client.core.Point2f; -import com.coloryr.allmusic.client.core.render.TextFrameBuffer; -import com.coloryr.allmusic.codec.HudPosType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.network.chat.Component; - -import java.util.ArrayList; -import java.util.List; - -public class CoreRenderTarget extends TextFrameBuffer { - private final boolean isState; - - public CoreRenderTarget(String name) { - isState = name.equals("state"); - } - - @Override - public void putText(String text, int y, int color, boolean shadow) { - color = (color & 0x00FFFFFF) | 0xFF000000; - Component component = MiniMessage.parse(text); - Font font = Minecraft.getInstance().font; - int width = font.width(component); - if (width == 0) { - return; - } - - int height = font.lineHeight + (shadow ? 1 : 0); - if (isState) { - y = 0; - } - texts.add(new TextItem<>(width, height, y, component, shadow, color)); - } - - @Override - public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - if (texts.isEmpty()) { - return; - } - - GuiGraphicsExtractor gui = AllMusicClient.context; - if (gui == null) return; - Font font = Minecraft.getInstance().font; - - for (TextItem entry : texts) { - int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; - Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); - - int drawX = (int) point.x; - int drawY = (int) (point.y + entry.y); - int finalColor = applyAlpha(entry.color, alpha); - - if (maxWidth != -1 && entry.width > maxWidth) { - int scrollOffset = (int) getOffset(entry, maxWidth); - gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); - gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); - if (scrollOffset > 0) { - gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); - } - gui.disableScissor(); - } else { - gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); - } - } - } - - @Override - public void drawLine(float x, float y, float alpha, int line) { - if (texts.isEmpty()) { - return; - } - if (line >= texts.size()) { - return; - } - GuiGraphicsExtractor gui = AllMusicClient.context; - if (gui == null) return; - TextItem entry = texts.get(line); - gui.text(Minecraft.getInstance().font, entry.component, - (int) x, (int) (y + entry.y), - applyAlpha(entry.color, alpha), entry.shadow); - } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - if (texts.isEmpty()) { - return; - } - GuiGraphicsExtractor gui = AllMusicClient.context; - if (gui == null) return; - Font font = Minecraft.getInstance().font; - - for (TextItem entry : texts) { - int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; - Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); - - int drawX = (int) point.x; - int drawY = (int) (point.y + entry.y); - int finalColor = applyAlpha(entry.color, alpha); - - if (maxWidth != -1 && entry.width > maxWidth) { - float maxOffset = entry.width - maxWidth; - float texOffset = maxOffset * state; - int revealWidth = (int) (maxWidth * state); - - gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); - gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); - gui.disableScissor(); - } else { - int revealWidth = (int) (entry.width * state); - gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); - gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); - gui.disableScissor(); - } - } - } - - @Override - public Point2f getLine(int line) { - if (line >= texts.size()) { - return new Point2f(0, 0); - } - TextItem entry = texts.get(line); - return new Point2f(entry.width, entry.height); - } - - private static int applyAlpha(int color, float alpha) { - int a = (int) (alpha * 255); - if (a < 0) a = 0; - if (a > 255) a = 255; - return (color & 0x00FFFFFF) | (a << 24); - } -} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java deleted file mode 100644 index d143b08b..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.renderpearl.api.pipeline.RenderPipeline; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import org.joml.Matrix3x2f; - -public record FloatRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x0, - float y0, float x1, float y1, float u0, float u1, float v0, float v1, int color, - ScreenRectangle scissorArea, - ScreenRectangle bounds) implements GuiElementRenderState { - public FloatRenderState(RenderPipeline renderPipeline, TextureSetup textureSetup, Matrix3x2f matrix3x2f, - float i, float j, float k, float l, float f, float g, float h, float m, int n, ScreenRectangle screenRectangle) { - this(renderPipeline, textureSetup, matrix3x2f, i, j, k, l, f, g, h, m, n, screenRectangle, getBounds(i, j, k, l, matrix3x2f, screenRectangle)); - } - - private static ScreenRectangle getBounds(float i, float j, float k, float l, Matrix3x2f matrix3x2f, ScreenRectangle screenRectangle) { - ScreenRectangle screenRectangle2 = (new ScreenRectangle((int) Math.ceil(i + 1), (int) Math.ceil(j + 1), (int) Math.ceil(k - i + 1), (int) Math.ceil(l - j + 1))).transformMaxBounds(matrix3x2f); - return screenRectangle != null ? screenRectangle.intersection(screenRectangle2) : screenRectangle2; - } - - public void buildVertices(VertexConsumer vertexConsumer) { - vertexConsumer.addVertexWith2DPose(pose(), x0(), y0()).setUv(u0(), v0()).setColor(color()); - vertexConsumer.addVertexWith2DPose(pose(), x0(), y1()).setUv(u0(), v1()).setColor(color()); - vertexConsumer.addVertexWith2DPose(pose(), x1(), y1()).setUv(u1(), v1()).setColor(color()); - vertexConsumer.addVertexWith2DPose(pose(), x1(), y0()).setUv(u1(), v0()).setColor(color()); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java deleted file mode 100644 index 88129399..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java +++ /dev/null @@ -1,645 +0,0 @@ -package com.coloryr.allmusic.client; - -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.*; -import net.minecraft.resources.Identifier; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Stack; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class MiniMessage { - // 正则表达式匹配标签 - private static final Pattern TAG_PATTERN = Pattern.compile( - "<(/?)(\\w+)(?:\\(([^)]+)\\))?(?::([^>]+))?>" - ); - - // 颜色映射表 - private static final Map COLOR_MAP = new HashMap<>(); - // 装饰器映射 - private static final Map DECORATION_MAP = new HashMap<>(); - - static { - COLOR_MAP.put("black", ChatFormatting.BLACK); - COLOR_MAP.put("dark_blue", ChatFormatting.DARK_BLUE); - COLOR_MAP.put("dark_green", ChatFormatting.DARK_GREEN); - COLOR_MAP.put("dark_aqua", ChatFormatting.DARK_AQUA); - COLOR_MAP.put("dark_red", ChatFormatting.DARK_RED); - COLOR_MAP.put("dark_purple", ChatFormatting.DARK_PURPLE); - COLOR_MAP.put("gold", ChatFormatting.GOLD); - COLOR_MAP.put("gray", ChatFormatting.GRAY); - COLOR_MAP.put("dark_gray", ChatFormatting.DARK_GRAY); - COLOR_MAP.put("blue", ChatFormatting.BLUE); - COLOR_MAP.put("green", ChatFormatting.GREEN); - COLOR_MAP.put("aqua", ChatFormatting.AQUA); - COLOR_MAP.put("red", ChatFormatting.RED); - COLOR_MAP.put("light_purple", ChatFormatting.LIGHT_PURPLE); - COLOR_MAP.put("yellow", ChatFormatting.YELLOW); - COLOR_MAP.put("white", ChatFormatting.WHITE); - } - - static { - DECORATION_MAP.put("bold", ChatFormatting.BOLD); - DECORATION_MAP.put("italic", ChatFormatting.ITALIC); - DECORATION_MAP.put("underlined", ChatFormatting.UNDERLINE); - DECORATION_MAP.put("strikethrough", ChatFormatting.STRIKETHROUGH); - DECORATION_MAP.put("obfuscated", ChatFormatting.OBFUSCATED); - } - - /** - * 解析 MiniMessage 字符串为 Component - */ - public static Component parse(String input) { - MutableComponent root = Component.literal(""); - Stack styleStack = new Stack<>(); - StyleContext currentStyle = new StyleContext(); - styleStack.push(currentStyle); - - StringBuilder textBuffer = new StringBuilder(); - int pos = 0; - int length = input.length(); - - while (pos < length) { - char c = input.charAt(pos); - - if (c == '<') { - // 先处理已积累的文本 - if (textBuffer.length() > 0) { - appendText(root, textBuffer.toString(), currentStyle); - textBuffer.setLength(0); - } - - // 查找标签结束位置 - int endTag = findClosingTag(input, pos); - if (endTag == -1) { - // 没有找到闭合标签,当作普通文本处理 - textBuffer.append(c); - pos++; - continue; - } - - String tagContent = input.substring(pos, endTag + 1); - Matcher matcher = TAG_PATTERN.matcher(tagContent); - - if (matcher.matches()) { - boolean isClosing = !matcher.group(1).isEmpty(); - String tagName = matcher.group(2).toLowerCase(Locale.ROOT); - String params = matcher.group(3); - String value = matcher.group(4); - - if (isClosing) { - // 关闭标签 - 只关闭对应的开始标签 - String expectedOpenTag = matcher.group(2).toLowerCase(Locale.ROOT); - - // 查找对应的开始标签并关闭 - if (styleStack.size() > 1) { - // 从栈顶向下查找匹配的开始标签 - int index = styleStack.size() - 1; - boolean found = false; - - while (index >= 0) { - StyleContext ctx = styleStack.get(index); - if (ctx.tagName != null && ctx.tagName.equals(expectedOpenTag)) { - // 找到匹配的开始标签,关闭到这一层 - while (styleStack.size() > index + 1) { - styleStack.pop(); - } - currentStyle = styleStack.peek(); - found = true; - break; - } - index--; - } - - if (!found) { - // 没找到匹配的标签,忽略此关闭标签 - } - } - } else { - // 处理各种标签 - StyleContext newStyle = currentStyle.copy(); - - switch (tagName) { - case "color": - handleColorTag(newStyle, params, value); - break; - case "shadow": - handleShadowTag(newStyle, params, value); - break; - case "decoration": - handleDecorationTag(newStyle, params, value); - break; - case "reset": - newStyle = new StyleContext(); - break; - case "rainbow": - handleRainbowTag(newStyle, params, value); - break; - case "gradient": - handleGradientTag(newStyle, params, value); - break; - case "transition": - handleTransitionTag(newStyle, params, value); - break; - case "font": - handleFontTag(newStyle, params, value); - break; - default: - // 未知标签,忽略 - break; - } - - // 记录标签名称以便关闭时匹配 - newStyle.tagName = tagName; - styleStack.push(newStyle); - currentStyle = newStyle; - } - } else { - // 无效标签,当作普通文本 - textBuffer.append(tagContent); - } - - pos = endTag + 1; - } else { - textBuffer.append(c); - pos++; - } - } - - // 处理剩余的文本 - if (textBuffer.length() > 0) { - appendText(root, textBuffer.toString(), currentStyle); - } - - return root; - } - - /** - * 处理颜色标签 - * 支持格式: , , - */ - private static void handleColorTag(StyleContext style, String params, String value) { - if (value != null) { - // 格式: 或 - TextColor textColor = parseColor(value); - if (textColor != null) { - style.color = textColor; - // 清除渐变、过渡和彩虹效果 - style.gradient = false; - style.transition = false; - style.rainbow = false; - } - } else if (params != null) { - // 格式: - if (params.equalsIgnoreCase("rgb")) { - String[] parts = value.split(","); - if (parts.length == 3) { - try { - int r = Integer.parseInt(parts[0].trim()); - int g = Integer.parseInt(parts[1].trim()); - int b = Integer.parseInt(parts[2].trim()); - style.color = TextColor.fromRgb((r << 16) | (g << 8) | b); - // 清除渐变、过渡和彩虹效果 - style.gradient = false; - style.transition = false; - style.rainbow = false; - } catch (NumberFormatException e) { - // 忽略解析错误 - } - } - } - } - } - - /** - * 处理阴影标签 - * 支持格式: , , , - */ - private static void handleShadowTag(StyleContext style, String params, String value) { - if (value == null) { - style.shadow = true; - } else if (value.equalsIgnoreCase("true")) { - style.shadow = true; - } else if (value.equalsIgnoreCase("false")) { - style.shadow = false; - } else { - // 自定义阴影颜色 - TextColor shadowColor = parseColor(value); - if (shadowColor != null) { - style.shadow = true; - style.shadowColor = shadowColor; - } - } - } - - /** - * 处理装饰标签 - * 支持格式: , , - */ - private static void handleDecorationTag(StyleContext style, String params, String value) { - if (params == null) return; - - String[] parts = params.split(":"); - String decorationName = parts[0].toLowerCase(Locale.ROOT); - boolean enabled = true; - - if (parts.length > 1) { - enabled = Boolean.parseBoolean(parts[1]); - } else if (value != null) { - enabled = Boolean.parseBoolean(value); - } - - ChatFormatting formatting = DECORATION_MAP.get(decorationName); - if (formatting != null) { - style.decorations.put(formatting, enabled); - } - } - - /** - * 处理彩虹色标签 - * 支持格式: , , , - */ - private static void handleRainbowTag(StyleContext style, String params, String value) { - float saturation = 1.0f; - float brightness = 1.0f; - - if (params != null) { - if (params.equalsIgnoreCase("saturation") && value != null) { - try { - saturation = Float.parseFloat(value); - } catch (NumberFormatException e) { - } - } else if (params.equalsIgnoreCase("brightness") && value != null) { - try { - brightness = Float.parseFloat(value); - } catch (NumberFormatException e) { - } - } - } - - style.rainbow = true; - style.gradient = false; - style.transition = false; - style.rainbowSaturation = saturation; - style.rainbowBrightness = brightness; - } - - /** - * 处理渐变色标签 - * 支持格式: , - * 渐变是逐字符过渡 - */ - private static void handleGradientTag(StyleContext style, String params, String value) { - if (value == null) return; - - String[] colors = value.split(":"); - if (colors.length == 2) { - TextColor start = parseColor(colors[0]); - TextColor end = parseColor(colors[1]); - - if (start != null && end != null) { - style.gradient = true; - style.transition = false; - style.rainbow = false; - style.gradientStart = start; - style.gradientEnd = end; - } - } else if (params != null && params.equalsIgnoreCase("rgb")) { - // 处理 RGB 格式 - String[] color1 = colors[0].split(","); - String[] color2 = colors[1].split(","); - if (color1.length == 3 && color2.length == 3) { - try { - int r1 = Integer.parseInt(color1[0].trim()); - int g1 = Integer.parseInt(color1[1].trim()); - int b1 = Integer.parseInt(color1[2].trim()); - int r2 = Integer.parseInt(color2[0].trim()); - int g2 = Integer.parseInt(color2[1].trim()); - int b2 = Integer.parseInt(color2[2].trim()); - - style.gradient = true; - style.transition = false; - style.rainbow = false; - style.gradientStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); - style.gradientEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); - } catch (NumberFormatException e) { - } - } - } - } - - /** - * 处理过渡色标签 - * 支持格式: , - * 过渡是整个文本块的颜色过渡(类似整体渐变效果) - */ - private static void handleTransitionTag(StyleContext style, String params, String value) { - if (value == null) return; - - String[] colors = value.split(":"); - if (colors.length == 2) { - TextColor start = parseColor(colors[0]); - TextColor end = parseColor(colors[1]); - - if (start != null && end != null) { - style.transition = true; - style.gradient = false; - style.rainbow = false; - style.transitionStart = start; - style.transitionEnd = end; - } - } else if (params != null && params.equalsIgnoreCase("rgb")) { - // 处理 RGB 格式 - String[] color1 = colors[0].split(","); - String[] color2 = colors[1].split(","); - if (color1.length == 3 && color2.length == 3) { - try { - int r1 = Integer.parseInt(color1[0].trim()); - int g1 = Integer.parseInt(color1[1].trim()); - int b1 = Integer.parseInt(color1[2].trim()); - int r2 = Integer.parseInt(color2[0].trim()); - int g2 = Integer.parseInt(color2[1].trim()); - int b2 = Integer.parseInt(color2[2].trim()); - - style.transition = true; - style.gradient = false; - style.rainbow = false; - style.transitionStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); - style.transitionEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); - } catch (NumberFormatException e) { - } - } - } - } - - /** - * 处理字体标签 - * 支持格式: , - */ - private static void handleFontTag(StyleContext style, String params, String value) { - String fontName = value != null ? value : params; - if (fontName != null) { - style.font = fontName; - } - } - - /** - * 解析颜色字符串 - * 支持: 颜色名称, #RRGGBB, #RGB, 十六进制数值 - */ - private static TextColor parseColor(String colorStr) { - if (colorStr == null) return null; - - // 检查是否为预定义颜色名称 - ChatFormatting formatting = COLOR_MAP.get(colorStr.toLowerCase(Locale.ROOT)); - if (formatting != null) { - return TextColor.fromLegacyFormat(formatting); - } - - // 检查十六进制格式 - if (colorStr.startsWith("#")) { - try { - if (colorStr.length() == 7) { - return TextColor.parseColor(colorStr).getOrThrow(); - } else if (colorStr.length() == 4) { - // 扩展 #RGB 到 #RRGGBB - String expanded = "#" + - colorStr.charAt(1) + colorStr.charAt(1) + - colorStr.charAt(2) + colorStr.charAt(2) + - colorStr.charAt(3) + colorStr.charAt(3); - return TextColor.parseColor(expanded).getOrThrow(); - } - } catch (Exception e) { - // 忽略解析错误 - } - } - - return null; - } - - /** - * 查找标签闭合位置 - */ - private static int findClosingTag(String input, int start) { - int pos = start + 1; - while (pos < input.length()) { - char c = input.charAt(pos); - if (c == '>') { - return pos; - } - pos++; - } - return -1; - } - - /** - * 添加文本到组件 - */ - private static void appendText(MutableComponent root, String text, StyleContext styleContext) { - if (text.isEmpty()) return; - - // 如果有渐变效果,需要逐字符处理 - if (styleContext.gradient && styleContext.gradientStart != null && styleContext.gradientEnd != null) { - appendGradientText(root, text, styleContext); - } - // 如果有过渡效果,需要逐字符处理 - else if (styleContext.transition && styleContext.transitionStart != null && styleContext.transitionEnd != null) { - appendTransitionText(root, text, styleContext); - } - // 如果有彩虹效果,需要逐字符处理 - else if (styleContext.rainbow) { - appendRainbowText(root, text, styleContext); - } - // 普通文本 - else { - MutableComponent component = Component.literal(text); - component.setStyle(styleContext.buildStyle()); - root.append(component); - } - } - - /** - * 带渐变效果的文本追加(逐字符颜色变化) - */ - private static void appendGradientText(MutableComponent root, String text, StyleContext styleContext) { - int length = text.length(); - Style baseStyle = styleContext.buildBaseStyle(); - - for (int i = 0; i < length; i++) { - char c = text.charAt(i); - float ratio = (float) i / (length - 1); - TextColor gradientColor = interpolateColor(styleContext.gradientStart, styleContext.gradientEnd, ratio); - - Style charStyle = baseStyle.withColor(gradientColor); - // 重新应用阴影 - if (styleContext.shadow != null && styleContext.shadow) { - if (styleContext.shadowColor != null) { - charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); - } else { - charStyle = charStyle.withShadowColor(0xFF000000); - } - } - - root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); - } - } - - /** - * 带过渡效果的文本追加(整体颜色过渡) - */ - private static void appendTransitionText(MutableComponent root, String text, StyleContext styleContext) { - Style baseStyle = styleContext.buildBaseStyle(); - - // 过渡效果:为整个文本块创建一个颜色过渡 - MutableComponent transitionComponent = Component.literal(text); - - // 应用过渡样式(这里使用颜色插值,但也可以根据需求调整) - if (styleContext.transitionStart != null && styleContext.transitionEnd != null) { - // 可以使用整体渐变或者固定起始色 - transitionComponent.setStyle(baseStyle.withColor(styleContext.transitionStart)); - } else { - transitionComponent.setStyle(baseStyle); - } - - root.append(transitionComponent); - } - - /** - * 带彩虹效果的文本追加 - */ - private static void appendRainbowText(MutableComponent root, String text, StyleContext styleContext) { - int length = text.length(); - Style baseStyle = styleContext.buildBaseStyle(); - - for (int i = 0; i < length; i++) { - char c = text.charAt(i); - float hue = (float) i / length; - int rgb = java.awt.Color.HSBtoRGB(hue, styleContext.rainbowSaturation, styleContext.rainbowBrightness); - TextColor rainbowColor = TextColor.fromRgb(rgb & 0xFFFFFF); - - Style charStyle = baseStyle.withColor(rainbowColor); - // 重新应用阴影 - if (styleContext.shadow != null && styleContext.shadow) { - if (styleContext.shadowColor != null) { - charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); - } else { - charStyle = charStyle.withShadowColor(0xFF000000); - } - } - - root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); - } - } - - /** - * 颜色插值 - */ - private static TextColor interpolateColor(TextColor start, TextColor end, float ratio) { - int startRgb = start.getValue(); - int endRgb = end.getValue(); - - int r1 = (startRgb >> 16) & 0xFF; - int g1 = (startRgb >> 8) & 0xFF; - int b1 = startRgb & 0xFF; - - int r2 = (endRgb >> 16) & 0xFF; - int g2 = (endRgb >> 8) & 0xFF; - int b2 = endRgb & 0xFF; - - int r = (int) (r1 + (r2 - r1) * ratio); - int g = (int) (g1 + (g2 - g1) * ratio); - int b = (int) (b1 + (b2 - b1) * ratio); - - return TextColor.fromRgb((r << 16) | (g << 8) | b); - } - - /** - * 样式上下文类,用于追踪嵌套样式 - */ - private static class StyleContext { - String tagName = null; // 记录标签名称,用于关闭匹配 - TextColor color = null; - Boolean shadow = null; - TextColor shadowColor = null; - Map decorations = new HashMap<>(); - boolean rainbow = false; - float rainbowSaturation = 1.0f; - float rainbowBrightness = 1.0f; - boolean gradient = false; - TextColor gradientStart = null; - TextColor gradientEnd = null; - boolean transition = false; - TextColor transitionStart = null; - TextColor transitionEnd = null; - String font = null; - - StyleContext copy() { - StyleContext copy = new StyleContext(); - copy.tagName = this.tagName; - copy.color = this.color; - copy.shadow = this.shadow; - copy.shadowColor = this.shadowColor; - copy.decorations.putAll(this.decorations); - copy.rainbow = this.rainbow; - copy.rainbowSaturation = this.rainbowSaturation; - copy.rainbowBrightness = this.rainbowBrightness; - copy.gradient = this.gradient; - copy.gradientStart = this.gradientStart; - copy.gradientEnd = this.gradientEnd; - copy.transition = this.transition; - copy.transitionStart = this.transitionStart; - copy.transitionEnd = this.transitionEnd; - copy.font = this.font; - return copy; - } - - /** - * 构建基础样式(不含颜色) - */ - Style buildBaseStyle() { - Style style = Style.EMPTY; - - // 应用装饰器 - for (Map.Entry entry : decorations.entrySet()) { - if (entry.getValue()) { - style = style.applyFormat(entry.getKey()); - } - } - - if (font != null) { - try { - style = style.withFont(new FontDescription.Resource(Identifier.parse(font))); - } catch (Exception e) { - // 忽略字体解析错误 - } - } - - return style; - } - - /** - * 构建完整样式(含颜色) - */ - Style buildStyle() { - Style style = buildBaseStyle(); - - // 只有在没有渐变、过渡和彩虹效果时才应用普通颜色 - if (!gradient && !transition && !rainbow && color != null) { - style = style.withColor(color); - } - - if (shadow != null && shadow) { - if (shadowColor != null) { - style = style.withShadowColor(shadowColor.getValue() | 0xFF000000); - } else { - style = style.withShadowColor(0xFF000000); - } - } - - return style; - } - } -} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java deleted file mode 100644 index fca17cc4..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicHud; -import com.coloryr.allmusic.client.core.Point2f; -import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; -import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.platform.NativeImage; -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.texture.DynamicTexture; -import org.joml.Matrix3x2fStack; - -import java.io.ByteArrayInputStream; - -public class PicRender extends PictureFrameBuffer { - private final DynamicTexture sourceTexture; - private final DynamicTexture rotateTexture; - - public PicRender(int size) { - sourceTexture = new DynamicTexture("allmusic source", size, size, false); - rotateTexture = new DynamicTexture("allmusic rotate", size, size, false); - } - - @Override - public void update(byte[] source, byte[] rotate) { - try { - ByteArrayInputStream stream = new ByteArrayInputStream(source); - var image1 = NativeImage.read(stream); - sourceTexture.setPixels(image1); - sourceTexture.upload(); - - stream = new ByteArrayInputStream(rotate); - var image2 = NativeImage.read(stream); - rotateTexture.setPixels(image2); - rotateTexture.upload(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { - Point2f point = AllMusicHud.getPos(size, size, x, y, dir); - - Matrix3x2fStack matrix = new Matrix3x2fStack(2); - - float a = (float) size / 2; - - if (rotate) { - matrix.translation(point.x + a, point.y + a); - matrix.pushMatrix().rotate((float) Math.toRadians(ang)); - } else { - matrix.translation(point.x + a, point.y + a); - } - - float x0 = -a; - float x1 = a; - float y0 = -a; - float y1 = a; - float u0 = 0; - float u1 = 1; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView(), rotate ? rotateTexture.getSampler() : sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java deleted file mode 100644 index 5ee542f5..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicHud; -import com.coloryr.allmusic.client.core.Point2f; -import com.coloryr.allmusic.client.core.render.TextureRender; -import com.coloryr.allmusic.codec.HudPosType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.texture.SimpleTexture; -import net.minecraft.client.renderer.texture.TextureContents; -import net.minecraft.resources.Identifier; -import org.joml.Matrix3x2f; - -public class TexRender extends TextureRender { - private final SimpleTexture sourceTexture; - - public TexRender(String texture) { - super(texture); - Identifier location = Identifier.fromNamespaceAndPath(AllMusicClient.MODID, texture); - sourceTexture = new SimpleTexture(location); - - try { - TextureContents contents = sourceTexture.loadContents(Minecraft.getInstance().getResourceManager()); - - width = contents.image().getWidth(); - height = contents.image().getHeight(); - - sourceTexture.apply(contents); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void drawPic(float x, float y, float alpha) { - Matrix3x2f matrix = new Matrix3x2f(); - - float w1 = (float) width / 2; - float h1 = (float) height / 2; - - matrix.translation(x + w1, y + h1); - - float x0 = -w1; - float x1 = w1; - float y0 = -h1; - float y1 = h1; - float u0 = 0; - float u1 = 1; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - @Override - public void drawPic(float x, float y, float width, float alpha) { - Matrix3x2f matrix = new Matrix3x2f(); - - float w1 = ((float) this.width / 2) * width; - float h1 = (float) height / 2; - - matrix.translation(x + w1, y + h1); - - float x0 = -w1; - float x1 = w1; - float y0 = -h1; - float y1 = h1; - float u0 = 0; - float u1 = width; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - @Override - public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { - Point2f point = AllMusicHud.getPos(width, height, x, y, dir); - - float w1 = width / 2; - float h1 = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(point.x + w1, point.y + h1); - - float x0 = -w1; - float x1 = w1; - float y0 = -h1; - float y1 = h1; - float u0 = 0; - float u1 = 1; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java deleted file mode 100644 index 7c1dcb83..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.coloryr.allmusic.client.mixins; - -import com.coloryr.allmusic.client.AllMusicClient; -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.Hud; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Hud.class) -public class GuiShow { - @Inject(method = "extractRenderState", at = @At(value = "HEAD")) - public void guiShow(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) { - AllMusicClient.update(graphics); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java deleted file mode 100644 index f77e15c4..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.coloryr.allmusic.client.mixins; - -import com.coloryr.allmusic.client.core.AllMusicCore; -import net.minecraft.client.Minecraft; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Minecraft.class) -public class QuitServer { - @Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;ZZ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;resetData()V")) - public void quit(CallbackInfo info) { - AllMusicCore.onServerQuit(); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java deleted file mode 100644 index 0d9d49ea..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.coloryr.allmusic.client.mixins; - -import com.coloryr.allmusic.client.core.AllMusicCore; -import net.minecraft.client.Minecraft; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Minecraft.class) -public class Register { - @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketProcessor;(Ljava/lang/Thread;)V")) - public void register(CallbackInfo info) { - AllMusicCore.renderInit(); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java deleted file mode 100644 index 5bd2ce83..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.coloryr.allmusic.client.mixins; - -import com.coloryr.allmusic.client.core.AllMusicCore; -import net.minecraft.client.resources.sounds.SoundInstance; -import net.minecraft.client.sounds.SoundEngine; -import net.minecraft.client.sounds.SoundManager; -import net.minecraft.sounds.SoundSource; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(SoundManager.class) -public class SoundEvent { - @Inject(method = "play", at = @At("HEAD"), cancellable = true) - public void play(SoundInstance sound, CallbackInfoReturnable cir) { - if (AllMusicCore.isPlay()) { - SoundSource data = sound.getSource(); - switch (data) { - case RECORDS, MUSIC: { - cir.cancel(); - cir.setReturnValue(SoundEngine.PlayResult.NOT_STARTED); - } - } - } - } - - @Inject(method = "playDelayed", at = @At("HEAD"), cancellable = true) - public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { - if (AllMusicCore.isPlay()) { - SoundSource data = soundInstance.getSource(); - switch (data) { - case RECORDS, MUSIC -> info.cancel(); - } - } - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java deleted file mode 100644 index ff2cc10c..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.coloryr.allmusic.client.mixins; - -import com.coloryr.allmusic.client.core.AllMusicCore; -import net.minecraft.client.Minecraft; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Minecraft.class) -public class Tick { - @Inject(method = "tick", at = @At("HEAD")) - public void tick(CallbackInfo info) { - AllMusicCore.tick(); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java deleted file mode 100644 index 62eb1259..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.coloryr.allmusic.client.others; - -import com.coloryr.allmusic.client.core.AllMusicCore; -import de.maxhenkel.voicechat.api.VoicechatPlugin; -import de.maxhenkel.voicechat.api.events.ClientReceiveSoundEvent; -import de.maxhenkel.voicechat.api.events.EventRegistration; - -public class VoiceChat implements VoicechatPlugin { - @Override - public String getPluginId() { - return "allmusic"; - } - - @Override - public void registerEvents(EventRegistration registration) { - registration.registerEvent(ClientReceiveSoundEvent.EntitySound.class, this::play); - registration.registerEvent(ClientReceiveSoundEvent.LocationalSound.class, this::play); - registration.registerEvent(ClientReceiveSoundEvent.StaticSound.class, this::play); - } - - public void play(ClientReceiveSoundEvent.EntitySound event) { - AllMusicCore.chat(); - } - - public void play(ClientReceiveSoundEvent.LocationalSound event) { - AllMusicCore.chat(); - } - - public void play(ClientReceiveSoundEvent.StaticSound event) { - AllMusicCore.chat(); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java deleted file mode 100644 index 8d0e96d4..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.coloryr.allmusic.comm; - -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; - -public class AllMusicInit implements ModInitializer { - @Override - public void onInitialize() { - PayloadTypeRegistry.clientboundPlay().register(MusicCodec.ID, MusicCodec.CODEC); - } -} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java deleted file mode 100644 index 6b0cee70..00000000 --- a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.coloryr.allmusic.comm; - -import com.coloryr.allmusic.codec.MusicPack; -import com.coloryr.allmusic.codec.MusicPacketCodec; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.Identifier; -import org.jspecify.annotations.NonNull; - -public record MusicCodec(MusicPack pack) implements CustomPacketPayload { - public static final Identifier ChannelID = Identifier.fromNamespaceAndPath("allmusic", "channel"); - - public static final Type ID = new Type<>(ChannelID); - public static final StreamCodec CODEC = - StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), - buf -> new MusicCodec(MusicPacketCodec.decode(buf))); - - @Override - public @NonNull Type type() { - return ID; - } -} diff --git a/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json b/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json deleted file mode 100644 index 2d2425a6..00000000 --- a/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "required": true, - "package": "com.coloryr.allmusic.client.mixins", - "compatibilityLevel": "JAVA_25", - "client": [ - "GuiShow", - "QuitServer", - "Register", - "SoundEvent", - "Tick" - ], - "injectors": { - "defaultRequire": 1 - }, - "overwrites": { - "requireAnnotations": true - } -} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png b/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png deleted file mode 100644 index b2b5fa790ca8e14661cd169aa56e1ca5dd818a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32781 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 diff --git a/client/fabric_26_3/src/main/resources/fabric.mod.json b/client/fabric_26_3/src/main/resources/fabric.mod.json deleted file mode 100644 index 06b7c0a8..00000000 --- a/client/fabric_26_3/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "schemaVersion": 1, - "id": "allmusic_client", - "version": "${version}", - "name": "AllMusic Client", - "description": "AllMusic Client Mod", - "authors": [ - "Color_yr" - ], - "contact": { - "homepage": "https://github.com/Coloryr/AllMusic", - "sources": "https://github.com/Coloryr/AllMusic" - }, - "license": "GPL-3.0", - "icon": "assets/allmusic_client/icon.png", - "environment": "*", - "entrypoints": { - "main": [ - "com.coloryr.allmusic.comm.AllMusicInit" - ], - "client": [ - "com.coloryr.allmusic.client.AllMusicClient" - ], - "voicechat": [ - "com.coloryr.allmusic.client.others.VoiceChat" - ] - }, - "mixins": [ - { - "config": "allmusic_client.mixins.json", - "environment": "client" - } - ], - "depends": { - "fabricloader": ">=0.19.5", - "minecraft": ">=26.3", - "fabric-api": "*" - }, - "suggests": { - "voicechat_api": "*" - } -} \ No newline at end of file diff --git a/client/neoforge_26_3/build.gradle.kts b/client/neoforge_26_3/build.gradle.kts deleted file mode 100644 index aa89b82c..00000000 --- a/client/neoforge_26_3/build.gradle.kts +++ /dev/null @@ -1,40 +0,0 @@ -plugins { - id("dev.architectury.loom-no-remap") version Versions.architecturyLoom -// id("architectury-plugin") version "3.5-SNAPSHOT" -} - -java.sourceCompatibility = JavaVersion.VERSION_25 -java.targetCompatibility = JavaVersion.VERSION_25 - -//architectury { -// platformSetupLoomIde() -// neoForge() -//} - -repositories { - maven("https://maven.neoforged.net/releases/") -} - -dependencies { - minecraft("com.mojang:minecraft:26.3") - neoForge("net.neoforged:neoforge:26.3.0.6-beta") - - compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") -} - -tasks { - processResources { - filesMatching("META-INF/neoforge.mods.toml") { - expand("version" to project.version) - } - } - - shadowJar { - archiveFileName.set("[neoforge-26.3]AllMusic_Client-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - } - - build { - dependsOn(shadowJar) - } -} diff --git a/client/neoforge_26_3/gradle.properties b/client/neoforge_26_3/gradle.properties deleted file mode 100644 index 7da18ea6..00000000 --- a/client/neoforge_26_3/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=neoforge diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java deleted file mode 100644 index e8f1067d..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicBridge; -import com.coloryr.allmusic.client.core.AllMusicCore; -import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; -import com.coloryr.allmusic.client.core.render.TextFrameBuffer; -import com.coloryr.allmusic.client.core.render.TextureRender; -import com.coloryr.allmusic.comm.AllMusicInit; -import com.coloryr.allmusic.comm.MusicCodec; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.network.Connection; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.server.packs.resources.Resource; -import net.minecraft.sounds.SoundSource; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.ModList; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.fml.loading.FMLPaths; -import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; -import net.neoforged.neoforge.client.event.ClientTickEvent; -import net.neoforged.neoforge.client.event.RenderGuiLayerEvent; -import net.neoforged.neoforge.client.event.sound.PlaySoundSourceEvent; -import net.neoforged.neoforge.client.event.sound.PlayStreamingSourceEvent; -import net.neoforged.neoforge.client.event.sound.SoundEngineLoadEvent; -import net.neoforged.neoforge.client.gui.VanillaGuiLayers; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.neoforged.neoforge.network.handling.IPayloadHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - -@EventBusSubscriber(modid = AllMusicClient.MODID, value = Dist.CLIENT) -public class AllMusicClient implements IPayloadHandler, AllMusicBridge { - public static final String MODID = "allmusic_client"; - public static boolean modui; - public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Client"); - public static GuiGraphicsExtractor context; - - @SubscribeEvent - public static void setup(final FMLClientSetupEvent event) { - AllMusicClient client = new AllMusicClient(); - AllMusicInit.handler = client; - AllMusicCore.init(FMLPaths.CONFIGDIR.get(), client); - - Minecraft.getInstance().execute(AllMusicCore::renderInit); - - if (ModList.get().isLoaded("modernui")) { - modui = true; - } - } - - @SubscribeEvent - public static void onSound(final PlaySoundSourceEvent e) { - if (!AllMusicCore.isPlay()) return; - SoundSource data = e.getSound().getSource(); - switch (data) { - case MUSIC, RECORDS -> e.getChannel().stop(); - } - } - - @SubscribeEvent - public static void onSound(final PlayStreamingSourceEvent e) { - if (!AllMusicCore.isPlay()) return; - SoundSource data = e.getSound().getSource(); - switch (data) { - case MUSIC, RECORDS -> e.getChannel().stop(); - } - } - - @SubscribeEvent - public static void onServerQuit(final ClientPlayerNetworkEvent.LoggingOut e) { - AllMusicCore.onServerQuit(); - } - - @SubscribeEvent - public static void onRenderOverlay(RenderGuiLayerEvent.Post e) { - if (e.getName().equals(VanillaGuiLayers.CAMERA_OVERLAYS)) { - context = e.getGuiGraphics(); - AllMusicCore.hudUpdate(); - } - } - - @SubscribeEvent - public static void onTick(ClientTickEvent.Post event) { - AllMusicCore.tick(); - } - - public void sendMessage(String data) { - data = "[AllMusic Client]" + data; - LOGGER.warn(data); - String finalData = data; - Minecraft.getInstance().execute(() -> { - if (Minecraft.getInstance().player == null) - return; - Minecraft.getInstance().player.sendSystemMessage(Component.literal(finalData)); - }); - } - - @Override - public void handle(MusicCodec pack, IPayloadContext iPayloadContext) { - try { - AllMusicCore.packDo(pack.pack()); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void stopPlayMusic() { - Minecraft.getInstance().getSoundManager().stop(null, SoundSource.MUSIC); - Minecraft.getInstance().getSoundManager().stop(null, SoundSource.RECORDS); - } - - public int getScreenWidth() { - return Minecraft.getInstance().getWindow().getGuiScaledWidth(); - } - - public int getScreenHeight() { - return Minecraft.getInstance().getWindow().getGuiScaledHeight(); - } - - public int getTextWidth(String item) { - return Minecraft.getInstance().font.width(item); - } - - public int getFontHeight() { - return Minecraft.getInstance().font.lineHeight; - } - - public float getVolume() { - return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); - } - - @Override - public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(name); - } - - @Override - public TextureRender makeTextureRender(String file) { - return new TexRender(file); - } - - @Override - public PictureFrameBuffer makePictureRender(int size) { - return new PicRender(size); - } - - @Override - public String readText(String file) { - try { - var man = Minecraft.getInstance().getResourceManager(); - Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); - try (InputStream inputStream = resource.open()) { - byte[] bytes = inputStream.readAllBytes(); - return new String(bytes, StandardCharsets.UTF_8); - } - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - @Override - public InputStream readFile(String file) { - try { - var man = Minecraft.getInstance().getResourceManager(); - Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); - return resource.open(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - @Override - public void kick() { - Minecraft client = Minecraft.getInstance(); - - ClientPacketListener packetListener = client.getConnection(); - if (packetListener != null) { - Connection connection = packetListener.getConnection(); - connection.disconnect(Component.nullToEmpty("Old AllMusic server")); - } - } -} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java deleted file mode 100644 index fe90072e..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicHud; -import com.coloryr.allmusic.client.core.Point2f; -import com.coloryr.allmusic.client.core.render.TextFrameBuffer; -import com.coloryr.allmusic.codec.HudPosType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.network.chat.Component; - -public class CoreRenderTarget extends TextFrameBuffer { - private final boolean isState; - - public CoreRenderTarget(String name) { - isState = name.equals("state"); - } - - @Override - public void putText(String text, int y, int color, boolean shadow) { - color = (color & 0x00FFFFFF) | 0xFF000000; - Component component = MiniMessage.parse(text); - Font font = Minecraft.getInstance().font; - int width = font.width(component); - if (width == 0) { - return; - } - - int height = font.lineHeight + (shadow ? 1 : 0); - if (isState) { - y = 0; - } - texts.add(new TextItem<>(width, height, y, component, shadow, color)); - } - - @Override - public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - if (texts.isEmpty()) { - return; - } - - GuiGraphicsExtractor gui = AllMusicClient.context; - if (gui == null) return; - Font font = Minecraft.getInstance().font; - - for (TextItem entry : texts) { - int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; - Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); - - int drawX = (int) point.x; - int drawY = (int) (point.y + entry.y); - int finalColor = applyAlpha(entry.color, alpha); - - if (maxWidth != -1 && entry.width > maxWidth) { - int scrollOffset = (int) getOffset(entry, maxWidth); - gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); - gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); - if (scrollOffset > 0) { - gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); - } - gui.disableScissor(); - } else { - gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); - } - } - } - - @Override - public void drawLine(float x, float y, float alpha, int line) { - if (texts.isEmpty()) { - return; - } - if (line >= texts.size()) { - return; - } - GuiGraphicsExtractor gui = AllMusicClient.context; - if (gui == null) return; - TextItem entry = texts.get(line); - gui.text(Minecraft.getInstance().font, entry.component, - (int) x, (int) (y + entry.y), - applyAlpha(entry.color, alpha), entry.shadow); - } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - if (texts.isEmpty()) { - return; - } - GuiGraphicsExtractor gui = AllMusicClient.context; - if (gui == null) return; - Font font = Minecraft.getInstance().font; - - for (TextItem entry : texts) { - int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; - Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); - - int drawX = (int) point.x; - int drawY = (int) (point.y + entry.y); - int finalColor = applyAlpha(entry.color, alpha); - - if (maxWidth != -1 && entry.width > maxWidth) { - float maxOffset = entry.width - maxWidth; - float texOffset = maxOffset * state; - int revealWidth = (int) (maxWidth * state); - - gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); - gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); - gui.disableScissor(); - } else { - int revealWidth = (int) (entry.width * state); - gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); - gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); - gui.disableScissor(); - } - } - } - - @Override - public Point2f getLine(int line) { - if (line >= texts.size()) { - return new Point2f(0, 0); - } - TextItem entry = texts.get(line); - return new Point2f(entry.width, entry.height); - } - - private static int applyAlpha(int color, float alpha) { - int a = (int) (alpha * 255); - if (a < 0) a = 0; - if (a > 255) a = 255; - return (color & 0x00FFFFFF) | (a << 24); - } -} \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java deleted file mode 100644 index d143b08b..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.mojang.blaze3d.vertex.VertexConsumer; -import com.mojang.renderpearl.api.pipeline.RenderPipeline; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import org.joml.Matrix3x2f; - -public record FloatRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x0, - float y0, float x1, float y1, float u0, float u1, float v0, float v1, int color, - ScreenRectangle scissorArea, - ScreenRectangle bounds) implements GuiElementRenderState { - public FloatRenderState(RenderPipeline renderPipeline, TextureSetup textureSetup, Matrix3x2f matrix3x2f, - float i, float j, float k, float l, float f, float g, float h, float m, int n, ScreenRectangle screenRectangle) { - this(renderPipeline, textureSetup, matrix3x2f, i, j, k, l, f, g, h, m, n, screenRectangle, getBounds(i, j, k, l, matrix3x2f, screenRectangle)); - } - - private static ScreenRectangle getBounds(float i, float j, float k, float l, Matrix3x2f matrix3x2f, ScreenRectangle screenRectangle) { - ScreenRectangle screenRectangle2 = (new ScreenRectangle((int) Math.ceil(i + 1), (int) Math.ceil(j + 1), (int) Math.ceil(k - i + 1), (int) Math.ceil(l - j + 1))).transformMaxBounds(matrix3x2f); - return screenRectangle != null ? screenRectangle.intersection(screenRectangle2) : screenRectangle2; - } - - public void buildVertices(VertexConsumer vertexConsumer) { - vertexConsumer.addVertexWith2DPose(pose(), x0(), y0()).setUv(u0(), v0()).setColor(color()); - vertexConsumer.addVertexWith2DPose(pose(), x0(), y1()).setUv(u0(), v1()).setColor(color()); - vertexConsumer.addVertexWith2DPose(pose(), x1(), y1()).setUv(u1(), v1()).setColor(color()); - vertexConsumer.addVertexWith2DPose(pose(), x1(), y0()).setUv(u1(), v0()).setColor(color()); - } -} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java deleted file mode 100644 index 88129399..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java +++ /dev/null @@ -1,645 +0,0 @@ -package com.coloryr.allmusic.client; - -import net.minecraft.ChatFormatting; -import net.minecraft.network.chat.*; -import net.minecraft.resources.Identifier; - -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.Stack; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class MiniMessage { - // 正则表达式匹配标签 - private static final Pattern TAG_PATTERN = Pattern.compile( - "<(/?)(\\w+)(?:\\(([^)]+)\\))?(?::([^>]+))?>" - ); - - // 颜色映射表 - private static final Map COLOR_MAP = new HashMap<>(); - // 装饰器映射 - private static final Map DECORATION_MAP = new HashMap<>(); - - static { - COLOR_MAP.put("black", ChatFormatting.BLACK); - COLOR_MAP.put("dark_blue", ChatFormatting.DARK_BLUE); - COLOR_MAP.put("dark_green", ChatFormatting.DARK_GREEN); - COLOR_MAP.put("dark_aqua", ChatFormatting.DARK_AQUA); - COLOR_MAP.put("dark_red", ChatFormatting.DARK_RED); - COLOR_MAP.put("dark_purple", ChatFormatting.DARK_PURPLE); - COLOR_MAP.put("gold", ChatFormatting.GOLD); - COLOR_MAP.put("gray", ChatFormatting.GRAY); - COLOR_MAP.put("dark_gray", ChatFormatting.DARK_GRAY); - COLOR_MAP.put("blue", ChatFormatting.BLUE); - COLOR_MAP.put("green", ChatFormatting.GREEN); - COLOR_MAP.put("aqua", ChatFormatting.AQUA); - COLOR_MAP.put("red", ChatFormatting.RED); - COLOR_MAP.put("light_purple", ChatFormatting.LIGHT_PURPLE); - COLOR_MAP.put("yellow", ChatFormatting.YELLOW); - COLOR_MAP.put("white", ChatFormatting.WHITE); - } - - static { - DECORATION_MAP.put("bold", ChatFormatting.BOLD); - DECORATION_MAP.put("italic", ChatFormatting.ITALIC); - DECORATION_MAP.put("underlined", ChatFormatting.UNDERLINE); - DECORATION_MAP.put("strikethrough", ChatFormatting.STRIKETHROUGH); - DECORATION_MAP.put("obfuscated", ChatFormatting.OBFUSCATED); - } - - /** - * 解析 MiniMessage 字符串为 Component - */ - public static Component parse(String input) { - MutableComponent root = Component.literal(""); - Stack styleStack = new Stack<>(); - StyleContext currentStyle = new StyleContext(); - styleStack.push(currentStyle); - - StringBuilder textBuffer = new StringBuilder(); - int pos = 0; - int length = input.length(); - - while (pos < length) { - char c = input.charAt(pos); - - if (c == '<') { - // 先处理已积累的文本 - if (textBuffer.length() > 0) { - appendText(root, textBuffer.toString(), currentStyle); - textBuffer.setLength(0); - } - - // 查找标签结束位置 - int endTag = findClosingTag(input, pos); - if (endTag == -1) { - // 没有找到闭合标签,当作普通文本处理 - textBuffer.append(c); - pos++; - continue; - } - - String tagContent = input.substring(pos, endTag + 1); - Matcher matcher = TAG_PATTERN.matcher(tagContent); - - if (matcher.matches()) { - boolean isClosing = !matcher.group(1).isEmpty(); - String tagName = matcher.group(2).toLowerCase(Locale.ROOT); - String params = matcher.group(3); - String value = matcher.group(4); - - if (isClosing) { - // 关闭标签 - 只关闭对应的开始标签 - String expectedOpenTag = matcher.group(2).toLowerCase(Locale.ROOT); - - // 查找对应的开始标签并关闭 - if (styleStack.size() > 1) { - // 从栈顶向下查找匹配的开始标签 - int index = styleStack.size() - 1; - boolean found = false; - - while (index >= 0) { - StyleContext ctx = styleStack.get(index); - if (ctx.tagName != null && ctx.tagName.equals(expectedOpenTag)) { - // 找到匹配的开始标签,关闭到这一层 - while (styleStack.size() > index + 1) { - styleStack.pop(); - } - currentStyle = styleStack.peek(); - found = true; - break; - } - index--; - } - - if (!found) { - // 没找到匹配的标签,忽略此关闭标签 - } - } - } else { - // 处理各种标签 - StyleContext newStyle = currentStyle.copy(); - - switch (tagName) { - case "color": - handleColorTag(newStyle, params, value); - break; - case "shadow": - handleShadowTag(newStyle, params, value); - break; - case "decoration": - handleDecorationTag(newStyle, params, value); - break; - case "reset": - newStyle = new StyleContext(); - break; - case "rainbow": - handleRainbowTag(newStyle, params, value); - break; - case "gradient": - handleGradientTag(newStyle, params, value); - break; - case "transition": - handleTransitionTag(newStyle, params, value); - break; - case "font": - handleFontTag(newStyle, params, value); - break; - default: - // 未知标签,忽略 - break; - } - - // 记录标签名称以便关闭时匹配 - newStyle.tagName = tagName; - styleStack.push(newStyle); - currentStyle = newStyle; - } - } else { - // 无效标签,当作普通文本 - textBuffer.append(tagContent); - } - - pos = endTag + 1; - } else { - textBuffer.append(c); - pos++; - } - } - - // 处理剩余的文本 - if (textBuffer.length() > 0) { - appendText(root, textBuffer.toString(), currentStyle); - } - - return root; - } - - /** - * 处理颜色标签 - * 支持格式: , , - */ - private static void handleColorTag(StyleContext style, String params, String value) { - if (value != null) { - // 格式: 或 - TextColor textColor = parseColor(value); - if (textColor != null) { - style.color = textColor; - // 清除渐变、过渡和彩虹效果 - style.gradient = false; - style.transition = false; - style.rainbow = false; - } - } else if (params != null) { - // 格式: - if (params.equalsIgnoreCase("rgb")) { - String[] parts = value.split(","); - if (parts.length == 3) { - try { - int r = Integer.parseInt(parts[0].trim()); - int g = Integer.parseInt(parts[1].trim()); - int b = Integer.parseInt(parts[2].trim()); - style.color = TextColor.fromRgb((r << 16) | (g << 8) | b); - // 清除渐变、过渡和彩虹效果 - style.gradient = false; - style.transition = false; - style.rainbow = false; - } catch (NumberFormatException e) { - // 忽略解析错误 - } - } - } - } - } - - /** - * 处理阴影标签 - * 支持格式: , , , - */ - private static void handleShadowTag(StyleContext style, String params, String value) { - if (value == null) { - style.shadow = true; - } else if (value.equalsIgnoreCase("true")) { - style.shadow = true; - } else if (value.equalsIgnoreCase("false")) { - style.shadow = false; - } else { - // 自定义阴影颜色 - TextColor shadowColor = parseColor(value); - if (shadowColor != null) { - style.shadow = true; - style.shadowColor = shadowColor; - } - } - } - - /** - * 处理装饰标签 - * 支持格式: , , - */ - private static void handleDecorationTag(StyleContext style, String params, String value) { - if (params == null) return; - - String[] parts = params.split(":"); - String decorationName = parts[0].toLowerCase(Locale.ROOT); - boolean enabled = true; - - if (parts.length > 1) { - enabled = Boolean.parseBoolean(parts[1]); - } else if (value != null) { - enabled = Boolean.parseBoolean(value); - } - - ChatFormatting formatting = DECORATION_MAP.get(decorationName); - if (formatting != null) { - style.decorations.put(formatting, enabled); - } - } - - /** - * 处理彩虹色标签 - * 支持格式: , , , - */ - private static void handleRainbowTag(StyleContext style, String params, String value) { - float saturation = 1.0f; - float brightness = 1.0f; - - if (params != null) { - if (params.equalsIgnoreCase("saturation") && value != null) { - try { - saturation = Float.parseFloat(value); - } catch (NumberFormatException e) { - } - } else if (params.equalsIgnoreCase("brightness") && value != null) { - try { - brightness = Float.parseFloat(value); - } catch (NumberFormatException e) { - } - } - } - - style.rainbow = true; - style.gradient = false; - style.transition = false; - style.rainbowSaturation = saturation; - style.rainbowBrightness = brightness; - } - - /** - * 处理渐变色标签 - * 支持格式: , - * 渐变是逐字符过渡 - */ - private static void handleGradientTag(StyleContext style, String params, String value) { - if (value == null) return; - - String[] colors = value.split(":"); - if (colors.length == 2) { - TextColor start = parseColor(colors[0]); - TextColor end = parseColor(colors[1]); - - if (start != null && end != null) { - style.gradient = true; - style.transition = false; - style.rainbow = false; - style.gradientStart = start; - style.gradientEnd = end; - } - } else if (params != null && params.equalsIgnoreCase("rgb")) { - // 处理 RGB 格式 - String[] color1 = colors[0].split(","); - String[] color2 = colors[1].split(","); - if (color1.length == 3 && color2.length == 3) { - try { - int r1 = Integer.parseInt(color1[0].trim()); - int g1 = Integer.parseInt(color1[1].trim()); - int b1 = Integer.parseInt(color1[2].trim()); - int r2 = Integer.parseInt(color2[0].trim()); - int g2 = Integer.parseInt(color2[1].trim()); - int b2 = Integer.parseInt(color2[2].trim()); - - style.gradient = true; - style.transition = false; - style.rainbow = false; - style.gradientStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); - style.gradientEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); - } catch (NumberFormatException e) { - } - } - } - } - - /** - * 处理过渡色标签 - * 支持格式: , - * 过渡是整个文本块的颜色过渡(类似整体渐变效果) - */ - private static void handleTransitionTag(StyleContext style, String params, String value) { - if (value == null) return; - - String[] colors = value.split(":"); - if (colors.length == 2) { - TextColor start = parseColor(colors[0]); - TextColor end = parseColor(colors[1]); - - if (start != null && end != null) { - style.transition = true; - style.gradient = false; - style.rainbow = false; - style.transitionStart = start; - style.transitionEnd = end; - } - } else if (params != null && params.equalsIgnoreCase("rgb")) { - // 处理 RGB 格式 - String[] color1 = colors[0].split(","); - String[] color2 = colors[1].split(","); - if (color1.length == 3 && color2.length == 3) { - try { - int r1 = Integer.parseInt(color1[0].trim()); - int g1 = Integer.parseInt(color1[1].trim()); - int b1 = Integer.parseInt(color1[2].trim()); - int r2 = Integer.parseInt(color2[0].trim()); - int g2 = Integer.parseInt(color2[1].trim()); - int b2 = Integer.parseInt(color2[2].trim()); - - style.transition = true; - style.gradient = false; - style.rainbow = false; - style.transitionStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); - style.transitionEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); - } catch (NumberFormatException e) { - } - } - } - } - - /** - * 处理字体标签 - * 支持格式: , - */ - private static void handleFontTag(StyleContext style, String params, String value) { - String fontName = value != null ? value : params; - if (fontName != null) { - style.font = fontName; - } - } - - /** - * 解析颜色字符串 - * 支持: 颜色名称, #RRGGBB, #RGB, 十六进制数值 - */ - private static TextColor parseColor(String colorStr) { - if (colorStr == null) return null; - - // 检查是否为预定义颜色名称 - ChatFormatting formatting = COLOR_MAP.get(colorStr.toLowerCase(Locale.ROOT)); - if (formatting != null) { - return TextColor.fromLegacyFormat(formatting); - } - - // 检查十六进制格式 - if (colorStr.startsWith("#")) { - try { - if (colorStr.length() == 7) { - return TextColor.parseColor(colorStr).getOrThrow(); - } else if (colorStr.length() == 4) { - // 扩展 #RGB 到 #RRGGBB - String expanded = "#" + - colorStr.charAt(1) + colorStr.charAt(1) + - colorStr.charAt(2) + colorStr.charAt(2) + - colorStr.charAt(3) + colorStr.charAt(3); - return TextColor.parseColor(expanded).getOrThrow(); - } - } catch (Exception e) { - // 忽略解析错误 - } - } - - return null; - } - - /** - * 查找标签闭合位置 - */ - private static int findClosingTag(String input, int start) { - int pos = start + 1; - while (pos < input.length()) { - char c = input.charAt(pos); - if (c == '>') { - return pos; - } - pos++; - } - return -1; - } - - /** - * 添加文本到组件 - */ - private static void appendText(MutableComponent root, String text, StyleContext styleContext) { - if (text.isEmpty()) return; - - // 如果有渐变效果,需要逐字符处理 - if (styleContext.gradient && styleContext.gradientStart != null && styleContext.gradientEnd != null) { - appendGradientText(root, text, styleContext); - } - // 如果有过渡效果,需要逐字符处理 - else if (styleContext.transition && styleContext.transitionStart != null && styleContext.transitionEnd != null) { - appendTransitionText(root, text, styleContext); - } - // 如果有彩虹效果,需要逐字符处理 - else if (styleContext.rainbow) { - appendRainbowText(root, text, styleContext); - } - // 普通文本 - else { - MutableComponent component = Component.literal(text); - component.setStyle(styleContext.buildStyle()); - root.append(component); - } - } - - /** - * 带渐变效果的文本追加(逐字符颜色变化) - */ - private static void appendGradientText(MutableComponent root, String text, StyleContext styleContext) { - int length = text.length(); - Style baseStyle = styleContext.buildBaseStyle(); - - for (int i = 0; i < length; i++) { - char c = text.charAt(i); - float ratio = (float) i / (length - 1); - TextColor gradientColor = interpolateColor(styleContext.gradientStart, styleContext.gradientEnd, ratio); - - Style charStyle = baseStyle.withColor(gradientColor); - // 重新应用阴影 - if (styleContext.shadow != null && styleContext.shadow) { - if (styleContext.shadowColor != null) { - charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); - } else { - charStyle = charStyle.withShadowColor(0xFF000000); - } - } - - root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); - } - } - - /** - * 带过渡效果的文本追加(整体颜色过渡) - */ - private static void appendTransitionText(MutableComponent root, String text, StyleContext styleContext) { - Style baseStyle = styleContext.buildBaseStyle(); - - // 过渡效果:为整个文本块创建一个颜色过渡 - MutableComponent transitionComponent = Component.literal(text); - - // 应用过渡样式(这里使用颜色插值,但也可以根据需求调整) - if (styleContext.transitionStart != null && styleContext.transitionEnd != null) { - // 可以使用整体渐变或者固定起始色 - transitionComponent.setStyle(baseStyle.withColor(styleContext.transitionStart)); - } else { - transitionComponent.setStyle(baseStyle); - } - - root.append(transitionComponent); - } - - /** - * 带彩虹效果的文本追加 - */ - private static void appendRainbowText(MutableComponent root, String text, StyleContext styleContext) { - int length = text.length(); - Style baseStyle = styleContext.buildBaseStyle(); - - for (int i = 0; i < length; i++) { - char c = text.charAt(i); - float hue = (float) i / length; - int rgb = java.awt.Color.HSBtoRGB(hue, styleContext.rainbowSaturation, styleContext.rainbowBrightness); - TextColor rainbowColor = TextColor.fromRgb(rgb & 0xFFFFFF); - - Style charStyle = baseStyle.withColor(rainbowColor); - // 重新应用阴影 - if (styleContext.shadow != null && styleContext.shadow) { - if (styleContext.shadowColor != null) { - charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); - } else { - charStyle = charStyle.withShadowColor(0xFF000000); - } - } - - root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); - } - } - - /** - * 颜色插值 - */ - private static TextColor interpolateColor(TextColor start, TextColor end, float ratio) { - int startRgb = start.getValue(); - int endRgb = end.getValue(); - - int r1 = (startRgb >> 16) & 0xFF; - int g1 = (startRgb >> 8) & 0xFF; - int b1 = startRgb & 0xFF; - - int r2 = (endRgb >> 16) & 0xFF; - int g2 = (endRgb >> 8) & 0xFF; - int b2 = endRgb & 0xFF; - - int r = (int) (r1 + (r2 - r1) * ratio); - int g = (int) (g1 + (g2 - g1) * ratio); - int b = (int) (b1 + (b2 - b1) * ratio); - - return TextColor.fromRgb((r << 16) | (g << 8) | b); - } - - /** - * 样式上下文类,用于追踪嵌套样式 - */ - private static class StyleContext { - String tagName = null; // 记录标签名称,用于关闭匹配 - TextColor color = null; - Boolean shadow = null; - TextColor shadowColor = null; - Map decorations = new HashMap<>(); - boolean rainbow = false; - float rainbowSaturation = 1.0f; - float rainbowBrightness = 1.0f; - boolean gradient = false; - TextColor gradientStart = null; - TextColor gradientEnd = null; - boolean transition = false; - TextColor transitionStart = null; - TextColor transitionEnd = null; - String font = null; - - StyleContext copy() { - StyleContext copy = new StyleContext(); - copy.tagName = this.tagName; - copy.color = this.color; - copy.shadow = this.shadow; - copy.shadowColor = this.shadowColor; - copy.decorations.putAll(this.decorations); - copy.rainbow = this.rainbow; - copy.rainbowSaturation = this.rainbowSaturation; - copy.rainbowBrightness = this.rainbowBrightness; - copy.gradient = this.gradient; - copy.gradientStart = this.gradientStart; - copy.gradientEnd = this.gradientEnd; - copy.transition = this.transition; - copy.transitionStart = this.transitionStart; - copy.transitionEnd = this.transitionEnd; - copy.font = this.font; - return copy; - } - - /** - * 构建基础样式(不含颜色) - */ - Style buildBaseStyle() { - Style style = Style.EMPTY; - - // 应用装饰器 - for (Map.Entry entry : decorations.entrySet()) { - if (entry.getValue()) { - style = style.applyFormat(entry.getKey()); - } - } - - if (font != null) { - try { - style = style.withFont(new FontDescription.Resource(Identifier.parse(font))); - } catch (Exception e) { - // 忽略字体解析错误 - } - } - - return style; - } - - /** - * 构建完整样式(含颜色) - */ - Style buildStyle() { - Style style = buildBaseStyle(); - - // 只有在没有渐变、过渡和彩虹效果时才应用普通颜色 - if (!gradient && !transition && !rainbow && color != null) { - style = style.withColor(color); - } - - if (shadow != null && shadow) { - if (shadowColor != null) { - style = style.withShadowColor(shadowColor.getValue() | 0xFF000000); - } else { - style = style.withShadowColor(0xFF000000); - } - } - - return style; - } - } -} \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java deleted file mode 100644 index 51fa9e23..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicHud; -import com.coloryr.allmusic.client.core.Point2f; -import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; -import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.platform.NativeImage; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.texture.DynamicTexture; -import org.joml.Matrix3x2fStack; - -import java.io.ByteArrayInputStream; - -public class PicRender extends PictureFrameBuffer { - private final DynamicTexture sourceTexture; - private final DynamicTexture rotateTexture; - - public PicRender(int size) { - sourceTexture = new DynamicTexture("allmusic source", size, size, false); - rotateTexture = new DynamicTexture("allmusic rotate", size, size, false); - } - - @Override - public void update(byte[] source, byte[] rotate) { - try { - ByteArrayInputStream stream = new ByteArrayInputStream(source); - var image1 = NativeImage.read(stream); - sourceTexture.setPixels(image1); - sourceTexture.upload(); - - stream = new ByteArrayInputStream(rotate); - var image2 = NativeImage.read(stream); - rotateTexture.setPixels(image2); - rotateTexture.upload(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { - Point2f point = AllMusicHud.getPos(size, size, x, y, dir); - - Matrix3x2fStack matrix = new Matrix3x2fStack(2); - - float a = (float) size / 2; - - if (rotate) { - matrix.translation(point.x + a, point.y + a); - matrix.pushMatrix().rotate((float) Math.toRadians(ang)); - } else { - matrix.translation(point.x + a, point.y + a); - } - - float x0 = -a; - float x1 = a; - float y0 = -a; - float y1 = a; - float u0 = 0; - float u1 = 1; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView(), rotate ? rotateTexture.getSampler() : sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } -} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java deleted file mode 100644 index 90c89199..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.coloryr.allmusic.client; - -import com.coloryr.allmusic.client.core.AllMusicHud; -import com.coloryr.allmusic.client.core.Point2f; -import com.coloryr.allmusic.client.core.render.TextureRender; -import com.coloryr.allmusic.codec.HudPosType; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.texture.SimpleTexture; -import net.minecraft.client.renderer.texture.TextureContents; -import net.minecraft.resources.Identifier; -import org.joml.Matrix3x2f; - -public class TexRender extends TextureRender { - private final SimpleTexture sourceTexture; - - public TexRender(String texture) { - super(texture); - Identifier location = Identifier.fromNamespaceAndPath(AllMusicClient.MODID, texture); - sourceTexture = new SimpleTexture(location); - - try { - TextureContents contents = sourceTexture.loadContents(Minecraft.getInstance().getResourceManager()); - - width = contents.image().getWidth(); - height = contents.image().getHeight(); - - sourceTexture.apply(contents); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void drawPic(float x, float y, float alpha) { - Matrix3x2f matrix = new Matrix3x2f(); - - float w1 = (float) width / 2; - float h1 = (float) height / 2; - - matrix.translation(x + w1, y + h1); - - float x0 = -w1; - float x1 = w1; - float y0 = -h1; - float y1 = h1; - float u0 = 0; - float u1 = 1; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } - - @Override - public void drawPic(float x, float y, float width, float alpha) { - Matrix3x2f matrix = new Matrix3x2f(); - - float w1 = ((float) this.width / 2) * width; - float h1 = (float) height / 2; - - matrix.translation(x + w1, y + h1); - - float x0 = -w1; - float x1 = w1; - float y0 = -h1; - float y1 = h1; - float u0 = 0; - float u1 = width; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } - - @Override - public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { - Point2f point = AllMusicHud.getPos(width, height, x, y, dir); - - float w1 = width / 2; - float h1 = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(point.x + w1, point.y + h1); - - float x0 = -w1; - float x1 = w1; - float y0 = -h1; - float y1 = h1; - float u0 = 0; - float u1 = 1; - float v0 = 0; - float v1 = 1; - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } -} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java deleted file mode 100644 index fab46779..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java +++ /dev/null @@ -1,15 +0,0 @@ -//package com.coloryr.allmusic.client.others; -// -//import icyllis.modernui.mc.text.ModernPreparedText; -//import net.minecraft.client.gui.Font; -//import net.minecraft.client.gui.navigation.ScreenRectangle; -//import net.minecraft.client.renderer.state.gui.GuiRenderState; -//import org.joml.Matrix3x2fc; -// -//public class ModernUI { -// public static void text(Font.PreparedText preparedText, GuiRenderState state, Matrix3x2fc pose, ScreenRectangle scissor) { -// if (preparedText instanceof ModernPreparedText) { -// ((ModernPreparedText) preparedText).submitRuns(state, pose, scissor); -// } -// } -//} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java deleted file mode 100644 index 9caead22..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.coloryr.allmusic.client.others; - -import com.coloryr.allmusic.client.core.AllMusicCore; -import de.maxhenkel.voicechat.api.ForgeVoicechatPlugin; -import de.maxhenkel.voicechat.api.VoicechatPlugin; -import de.maxhenkel.voicechat.api.events.ClientReceiveSoundEvent; -import de.maxhenkel.voicechat.api.events.EventRegistration; - -@ForgeVoicechatPlugin -public class VoiceChat implements VoicechatPlugin { - @Override - public String getPluginId() { - return "allmusic"; - } - - @Override - public void registerEvents(EventRegistration registration) { - registration.registerEvent(ClientReceiveSoundEvent.EntitySound.class, this::play); - registration.registerEvent(ClientReceiveSoundEvent.LocationalSound.class, this::play); - registration.registerEvent(ClientReceiveSoundEvent.StaticSound.class, this::play); - } - - public void play(ClientReceiveSoundEvent.EntitySound event) { - AllMusicCore.chat(); - } - - public void play(ClientReceiveSoundEvent.LocationalSound event) { - AllMusicCore.chat(); - } - - public void play(ClientReceiveSoundEvent.StaticSound event) { - AllMusicCore.chat(); - } -} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java deleted file mode 100644 index d9282960..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.coloryr.allmusic.comm; - -import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.neoforged.neoforge.network.handling.IPayloadHandler; - -@Mod(AllMusicInit.MODID) -public class AllMusicInit implements IPayloadHandler { - - public static final String MODID = "allmusic"; - - public static IPayloadHandler handler; - - public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { - modEventBus.addListener(this::register); - } - - public void register(final RegisterPayloadHandlersEvent event) { - event.registrar("1.0") - .optional() - .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); - } - - @Override - public void handle(MusicCodec data, IPayloadContext iPayloadContext) { - if (handler != null) { - handler.handle(data, iPayloadContext); - } - } -} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java deleted file mode 100644 index 434fa510..00000000 --- a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.coloryr.allmusic.comm; - -import com.coloryr.allmusic.codec.MusicPack; -import com.coloryr.allmusic.codec.MusicPacketCodec; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.Identifier; - -public record MusicCodec(MusicPack pack) implements CustomPacketPayload { - public static final Identifier channel = - Identifier.fromNamespaceAndPath("allmusic", "channel"); - - public static final Type TYPE = new Type<>(channel); - public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), - (buf) -> { - return new MusicCodec(MusicPacketCodec.decode(buf)); - }); - - @Override - public Type type() { - return TYPE; - } -} diff --git a/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml deleted file mode 100644 index 544e45e7..00000000 --- a/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml +++ /dev/null @@ -1,42 +0,0 @@ -modLoader = "javafml" -loaderVersion = "[2,)" -license = "GPL-3.0" -issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues" - -[[mods]] -modId = "allmusic" -version = "${version}" -displayName = "AllMusic" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Mod''' - -[[mods]] -modId = "allmusic_client" -version = "${version}" -displayName = "AllMusic Client" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Client Mod''' - -[[dependencies.allmusic_client]] -modId = "neoforge" -type = "required" -versionRange = "[26.3.0.6-beta,)" -ordering = "NONE" -side = "CLIENT" - -[[dependencies.allmusic_client]] -modId = "minecraft" -type = "required" -versionRange = "[26.3,)" -ordering = "NONE" -side = "CLIENT" - -[[dependencies.allmusic_client]] -modId="voicechat_api" -type="optional" -ordering="AFTER" -side="CLIENT" \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/resources/icon.png b/client/neoforge_26_3/src/main/resources/icon.png deleted file mode 100644 index b2b5fa790ca8e14661cd169aa56e1ca5dd818a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32781 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 diff --git a/onejar/fabric_26_3/build.gradle.kts b/onejar/fabric_26_3/build.gradle.kts deleted file mode 100644 index c109d33a..00000000 --- a/onejar/fabric_26_3/build.gradle.kts +++ /dev/null @@ -1,68 +0,0 @@ -plugins { - id("net.fabricmc.fabric-loom") version Versions.fabricLoom -} - -java.sourceCompatibility = JavaVersion.VERSION_25 -java.targetCompatibility = JavaVersion.VERSION_25 - -// 直接引用 client 和 server 模块的源码目录,无需手动复制 -val clientSourceDir = project(":client:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") -val serverSourceDir = project(":server:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") -val commSourceDir = project(":server:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") -val clientResDir = project(":client:fabric_26_3").projectDir.resolve("src/main/resources") -val serverResDir = project(":server:fabric_26_3").projectDir.resolve("src/main/resources") - -sourceSets { - main { - java { - setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) - } - resources { - setSrcDirs(listOf( - file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) - clientResDir, - serverResDir - )) - } - } -} - -dependencies { - minecraft("com.mojang:minecraft:26.3") - implementation("net.fabricmc:fabric-loader:0.19.5") - - implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") - - compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") - - implementation(include("net.kyori:adventure-platform-fabric:7.2.0")!!) -} - -tasks { - processResources { - // onejar 自己的 fabric.mod.json 优先(排在 srcDirs 第一位), - // client/server 的 fabric.mod.json 作为重复项被排除 - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - filesMatching("fabric.mod.json") { - expand( - "version" to project.version - ) - } - } - - // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar - loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) - - shadowJar { - archiveFileName.set("[fabric-26.3]AllMusic-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - -// relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") -// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") - } - - build { - dependsOn(shadowJar) - } -} diff --git a/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png b/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png deleted file mode 100644 index b2b5fa790ca8e14661cd169aa56e1ca5dd818a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32781 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 diff --git a/onejar/fabric_26_3/src/main/resources/fabric.mod.json b/onejar/fabric_26_3/src/main/resources/fabric.mod.json deleted file mode 100644 index 6f009cfc..00000000 --- a/onejar/fabric_26_3/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "schemaVersion": 1, - "id": "allmusic", - "version": "${version}", - "name": "AllMusic", - "description": "AllMusic Mod", - "authors": [ - "Color_yr" - ], - "contact": { - "homepage": "https://github.com/Coloryr/AllMusic", - "sources": "https://github.com/Coloryr/AllMusic" - }, - "license": "GPL-3.0", - "icon": "assets/allmusic/icon.png", - "environment": "*", - "entrypoints": { - "main": [ - "com.coloryr.allmusic.comm.AllMusicInit", - "com.coloryr.allmusic.server.AllMusicServer" - ], - "client": [ - "com.coloryr.allmusic.client.AllMusicClient" - ], - "voicechat": [ - "com.coloryr.allmusic.client.others.VoiceChat" - ] - }, - "mixins": [ - { - "config": "allmusic_client.mixins.json", - "environment": "client" - }, - { - "config": "allmusic_server.mixins.json", - "environment": "server" - } - ], - "depends": { - "fabricloader": ">=0.19.3", - "fabric-api": "*", - "minecraft": ">=26.2", - "adventure-platform-fabric": ">=7.0.0" - }, - "suggests": { - "modernui": "*", - "voicechat_api": "*" - } -} diff --git a/onejar/neoforge_26_3/build.gradle.kts b/onejar/neoforge_26_3/build.gradle.kts deleted file mode 100644 index ef978c25..00000000 --- a/onejar/neoforge_26_3/build.gradle.kts +++ /dev/null @@ -1,75 +0,0 @@ -import net.fabricmc.loom.build.nesting.NestableJarGenerationTask - -plugins { - id("dev.architectury.loom-no-remap") version Versions.architecturyLoom -// id("architectury-plugin") version "3.5-SNAPSHOT" -} - -java.sourceCompatibility = JavaVersion.VERSION_25 -java.targetCompatibility = JavaVersion.VERSION_25 - -// 直接引用 client 和 server 模块的源码目录,无需手动复制 -val clientSourceDir = project(":client:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") -val serverSourceDir = project(":server:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") -val commSourceDir = project(":server:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") -val clientResDir = project(":client:neoforge_26_3").projectDir.resolve("src/main/resources") - -sourceSets { - main { - java { - setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) - } - resources { - setSrcDirs(listOf( - file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) - clientResDir, - )) - } - } -} - -//architectury { -// platformSetupLoomIde() -// neoForge() -//} - -repositories { - maven("https://maven.neoforged.net/releases/") -} - -dependencies { - minecraft("com.mojang:minecraft:26.3") - neoForge("net.neoforged:neoforge:26.3.0.6-beta") - - compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") - - implementation(include("net.kyori:adventure-platform-neoforge:7.2.0")!!) -} - -tasks { - processResources { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - - filesMatching("META-INF/neoforge.mods.toml") { - expand("version" to project.version) - } - } - - // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar - val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) - val shadowJarTask = named("shadowJar", Jar::class) - shadowJarTask.configure { dependsOn(processIncludeJars) } - loom.nestJars( - shadowJarTask, - files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) - ) - - shadowJar { - archiveFileName.set("[neoforge-26.3]AllMusic-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - } - - build { - dependsOn(shadowJar) - } -} diff --git a/onejar/neoforge_26_3/gradle.properties b/onejar/neoforge_26_3/gradle.properties deleted file mode 100644 index 7da18ea6..00000000 --- a/onejar/neoforge_26_3/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=neoforge diff --git a/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml deleted file mode 100644 index e2e8e100..00000000 --- a/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml +++ /dev/null @@ -1,65 +0,0 @@ -modLoader = "javafml" -loaderVersion = "[2,)" -license = "GPL-3.0" -issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues/" - -[[mods]] -modId = "allmusic" -version = "${version}" -displayName = "AllMusic" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Mod''' - -[[mods]] -modId = "allmusic_client" -version = "${version}" -displayName = "AllMusic Client" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Client Mod''' - -[[mods]] -modId = "allmusic_server" -version = "${version}" -displayName = "AllMusic Server" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Server Mod''' - -[[dependencies.allmusic_client]] -modId = "neoforge" -type = "required" -versionRange = "[26.3.0.6-beta,)" -ordering = "NONE" -side = "CLIENT" - -[[dependencies.allmusic_client]] -modId = "minecraft" -type = "required" -versionRange = "[26.3,)" -ordering = "NONE" -side = "CLIENT" - -[[dependencies.allmusic_client]] -modId="voicechat_api" -type="optional" -ordering="AFTER" -side="CLIENT" - -[[dependencies.allmusic_server]] -modId = "neoforge" -type = "required" -versionRange = "[26.3.0.6-beta,)" -ordering = "NONE" -side = "BOTH" - -[[dependencies.allmusic_server]] -modId = "minecraft" -type = "required" -versionRange = "[26.3,)" -ordering = "NONE" -side = "BOTH" \ No newline at end of file diff --git a/onejar/neoforge_26_3/src/main/resources/icon.png b/onejar/neoforge_26_3/src/main/resources/icon.png deleted file mode 100644 index b2b5fa790ca8e14661cd169aa56e1ca5dd818a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32781 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 diff --git a/server/fabric_26_3/build.gradle.kts b/server/fabric_26_3/build.gradle.kts deleted file mode 100644 index eca95f96..00000000 --- a/server/fabric_26_3/build.gradle.kts +++ /dev/null @@ -1,37 +0,0 @@ -plugins { - id("net.fabricmc.fabric-loom") version Versions.fabricLoom -} - -java.sourceCompatibility = JavaVersion.VERSION_25 -java.targetCompatibility = JavaVersion.VERSION_25 - -dependencies { - minecraft("com.mojang:minecraft:26.3") - implementation("net.fabricmc:fabric-loader:0.19.5") - - implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") - - implementation(include("net.kyori:adventure-platform-fabric:7.2.0")!!) -} - -tasks { - processResources { - filesMatching("fabric.mod.json") { - expand( - "version" to project.version - ) - } - } - - // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar - loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) - - shadowJar { - archiveFileName.set("[fabric-26.3]AllMusic_Server-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - } - - build { - dependsOn(shadowJar) - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java deleted file mode 100644 index 8d0e96d4..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.coloryr.allmusic.comm; - -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; - -public class AllMusicInit implements ModInitializer { - @Override - public void onInitialize() { - PayloadTypeRegistry.clientboundPlay().register(MusicCodec.ID, MusicCodec.CODEC); - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java deleted file mode 100644 index 6b0cee70..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.coloryr.allmusic.comm; - -import com.coloryr.allmusic.codec.MusicPack; -import com.coloryr.allmusic.codec.MusicPacketCodec; -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.Identifier; -import org.jspecify.annotations.NonNull; - -public record MusicCodec(MusicPack pack) implements CustomPacketPayload { - public static final Identifier ChannelID = Identifier.fromNamespaceAndPath("allmusic", "channel"); - - public static final Type ID = new Type<>(ChannelID); - public static final StreamCodec CODEC = - StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), - buf -> new MusicCodec(MusicPacketCodec.decode(buf))); - - @Override - public @NonNull Type type() { - return ID; - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java deleted file mode 100644 index bc0bf10c..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.server.core.AllMusic; -import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; -import net.minecraft.server.MinecraftServer; - -import java.io.File; - -public class AllMusicServer implements ModInitializer { - public static MinecraftServer server; - - @Override - public void onInitialize() { - AllMusic.log = new LogFabric(); - AllMusic.side = new SideFabric(); - - CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) - -> CommandFabric.instance.register(dispatcher)); - - ServerLifecycleEvents.SERVER_STARTED.register((a) -> { - server = a; - AllMusic.init(new File(AllMusic.SERVER_DIR)); - AllMusic.start(); - Tasks.init(); - }); - - ServerLifecycleEvents.SERVER_STOPPING.register((a) -> { - AllMusic.stop(); - }); - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java deleted file mode 100644 index 5bf02199..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.server.core.command.CommandEX; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; - -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Predicate; - -public class CommandFabric implements Command, Predicate, SuggestionProvider { - public static CommandFabric instance = new CommandFabric(); - - public void register(CommandDispatcher dispatcher) { - dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) - .then(Commands.argument("args", StringArgumentType.greedyString()) - .suggests(this).executes(this))); - } - - @Override - public int run(CommandContext context) { - var item = context.getSource(); - - var input = context.getInput(); - var temp = input.split(" "); - var arg = new String[temp.length - 1]; - System.arraycopy(temp, 1, arg, 0, arg.length); - - CommandEX.execute(item, context.getSource().getTextName(), arg); - - return 0; - } - - @Override - public boolean test(CommandSourceStack stack) { - return true; - } - - @Override - public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { - var item = context.getSource(); - - var input = context.getInput(); - var temp = input.split(" "); - var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; - System.arraycopy(temp, 1, arg, 0, temp.length - 1); - - builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); - - List results = CommandEX.getTabList(item, item.getTextName(), arg); - - String remaining = builder.getRemaining().trim(); - - for (String s : results) { - if (s.startsWith(remaining)) { - builder.suggest(s); - } - } - - return builder.buildFuture(); - } -} \ No newline at end of file diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java deleted file mode 100644 index 6b749f8e..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.server.core.side.IAllMusicLogger; -import net.kyori.adventure.text.Component; - - -public class LogFabric implements IAllMusicLogger { - @Override - public void data(Component data) { - AllMusicServer.server.sendMessage(data); - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java deleted file mode 100644 index c6192e39..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.codec.MusicPack; -import com.coloryr.allmusic.comm.MusicCodec; -import com.coloryr.allmusic.server.core.AllMusic; -import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; -import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; -import com.coloryr.allmusic.server.core.side.BaseSide; -import com.coloryr.allmusic.server.event.MusicAddEvent; -import com.coloryr.allmusic.server.event.MusicPlayEvent; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.PermissionSet; -import net.minecraft.world.InteractionResult; - -import java.io.File; -import java.util.Collection; - -public class SideFabric extends BaseSide { - @Override - public void runTask(Runnable run) { - AllMusicServer.server.execute(run); - } - - @Override - public void runTask(Runnable run1, int delay) { - Tasks.add(new TaskItem() {{ - tick = delay; - run = run1; - }}); - } - - @Override - public boolean checkPermission(Object player) { - CommandSourceStack source = (CommandSourceStack) player; - return source.permissions() == PermissionSet.ALL_PERMISSIONS; - } - - @Override - public boolean isPlayer(Object player) { - CommandSourceStack source = (CommandSourceStack) player; - return source.isPlayer(); - } - - @Override - public boolean checkPermission(Object player, String permission) { - return checkPermission(player); - } - - @Override - public boolean needPlay(boolean islist) { - for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { - if (!com.coloryr.allmusic.server.core.AllMusic.isSkip(player.getName().getString(), null, false, islist)) { - return true; - } - } - return false; - } - - @Override - public Collection getPlayers() { - return AllMusicServer.server.getPlayerList().getPlayers(); - } - - @Override - public String getPlayerName(Object player) { - if (player instanceof ServerPlayer player1) { - return player1.getName().getString(); - } - - return null; - } - - @Override - public String getPlayerServer(Object player) { - return null; - } - - @Override - public void send(Object player, MusicPack pack) { - if (player instanceof ServerPlayer player1) { - send(player1, pack); - } - } - - @Override - public Object getPlayer(String player) { - return AllMusicServer.server.getPlayerList().getPlayer(player); - } - - @Override - public void sendBar(Object player, net.kyori.adventure.text.Component data) { - if (player instanceof ServerPlayer player1) { - player1.sendActionBar(data); - } - } - - @Override - public File getFolder() { - return new File(AllMusic.SERVER_DIR); - } - - @Override - public void broadcast(net.kyori.adventure.text.Component message) { - for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { - if (!AllMusic.isSkip(player.getName().getString(), null, false)) { - player.sendMessage(message); - } - } - } - - @Override - public void sendMessage(Object obj, net.kyori.adventure.text.Component message) { - if (obj instanceof CommandSourceStack source) { - source.sendMessage(message); - } - } - - @Override - public boolean onMusicPlay(SongInfoObj obj) { - return MusicPlayEvent.EVENT.invoker().interact(obj) != InteractionResult.PASS; - } - - @Override - public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { - CommandSourceStack source = (CommandSourceStack) obj; - return MusicAddEvent.EVENT.invoker().interact(source.getPlayer(), music) != InteractionResult.PASS; - } - - @Override - public Component miniMessage(String input) { - MiniMessage mm = MiniMessage.miniMessage(); - return mm.deserialize(input); - } - - @Override - public Component miniMessageRun(String input, String command) { - Component component = miniMessage(input); - return component.clickEvent(ClickEvent.runCommand(command)); - } - - @Override - public Component miniMessageSuggest(String input, String command) { - Component component = miniMessage(input); - return component.clickEvent(ClickEvent.suggestCommand(command)); - } - - private void send(ServerPlayer players, MusicPack data) { - if (players == null) - return; - runTask(() -> ServerPlayNetworking.send(players, new MusicCodec(data))); - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java deleted file mode 100644 index 40f9a5b9..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.coloryr.allmusic.server; - -public class TaskItem { - public int tick; - public Runnable run; -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java deleted file mode 100644 index d4e72d39..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.coloryr.allmusic.server; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class Tasks { - private static final List taskItems = new CopyOnWriteArrayList<>(); - - public static void init() { - AllMusicServer.server.addTickable(Tasks::tick); - } - - public static void tick() { - for (var item : new ArrayList<>(taskItems)) { - item.tick--; - if (item.tick <= 0) { - taskItems.remove(item); - item.run.run(); - } - } - } - - public static void add(TaskItem item) { - taskItems.add(item); - } -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java deleted file mode 100644 index 6ef1835c..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.coloryr.allmusic.server.event; - -import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.InteractionResult; - -public interface MusicAddEvent { - Event EVENT = EventFactory.createArrayBacked(MusicAddEvent.class, - (listeners) -> (player, music) -> { - for (MusicAddEvent listener : listeners) { - InteractionResult result = listener.interact(player, music); - - if (result != InteractionResult.PASS) { - return result; - } - } - - return InteractionResult.PASS; - }); - - InteractionResult interact(ServerPlayer player, PlayerAddMusicObj music); -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java deleted file mode 100644 index 852895f1..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.coloryr.allmusic.server.event; - -import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.world.InteractionResult; - -public interface MusicPlayEvent { - Event EVENT = EventFactory.createArrayBacked(MusicPlayEvent.class, - (listeners) -> (music) -> { - for (MusicPlayEvent listener : listeners) { - InteractionResult result = listener.interact(music); - - if (result != InteractionResult.PASS) { - return result; - } - } - - return InteractionResult.PASS; - }); - - /** - * 音乐播放事件,返回PASS表示通过 - * - * @param music 歌曲信息 - * @return 是否通过 - */ - InteractionResult interact(SongInfoObj music); -} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java deleted file mode 100644 index 60e133d4..00000000 --- a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.coloryr.allmusic.server.mixin; - -import com.coloryr.allmusic.server.core.AllMusic; -import com.coloryr.allmusic.server.core.music.PlayMusic; -import net.minecraft.network.Connection; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.network.CommonListenerCookie; -import net.minecraft.server.players.PlayerList; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(PlayerList.class) -public class ServerPlayerMixin { - @Inject(at = @At("HEAD"), method = "remove") - private void remove(ServerPlayer entity, CallbackInfo info) { - PlayMusic.removeNowPlayPlayer(entity.getName().getString()); - } - - @Inject(at = @At("TAIL"), method = "placeNewPlayer") - private void add(Connection connection, ServerPlayer player, CommonListenerCookie clientData, CallbackInfo ci) { - AllMusic.joinPlay(player.getName().getString()); - } -} diff --git a/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json b/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json deleted file mode 100644 index 6e607dbc..00000000 --- a/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "com.coloryr.allmusic.server.mixin", - "compatibilityLevel": "JAVA_25", - "server": [ - ], - "mixins": [ - "ServerPlayerMixin" - ], - "injectors": { - "defaultRequire": 1 - }, - "overwrites": { - "requireAnnotations": true - } -} diff --git a/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png b/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png deleted file mode 100644 index b2b5fa790ca8e14661cd169aa56e1ca5dd818a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32781 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 diff --git a/server/fabric_26_3/src/main/resources/fabric.mod.json b/server/fabric_26_3/src/main/resources/fabric.mod.json deleted file mode 100644 index b8127dee..00000000 --- a/server/fabric_26_3/src/main/resources/fabric.mod.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "schemaVersion": 1, - "id": "allmusic_server", - "version": "${version}", - "name": "AllMusic Server", - "description": "AllMusic Server Mod", - "authors": [ - "Color_yr" - ], - "contact": { - "homepage": "https://github.com/Coloryr/AllMusic", - "sources": "https://github.com/Coloryr/AllMusic" - }, - "license": "GPL-3.0", - "icon": "assets/allmusic_server/icon.png", - "environment": "*", - "entrypoints": { - "main": [ - "com.coloryr.allmusic.comm.AllMusicInit", - "com.coloryr.allmusic.server.AllMusicServer" - ] - }, - "mixins": [ - { - "config": "allmusic_server.mixins.json", - "environment": "server" - } - ], - "depends": { - "fabricloader": ">=0.19.5", - "minecraft": ">=26.3", - "fabric-api": "*", - "adventure-platform-fabric": ">=7.2.0" - }, - "suggests": { - } -} diff --git a/server/neoforge_26_3/build.gradle.kts b/server/neoforge_26_3/build.gradle.kts deleted file mode 100644 index 441c444f..00000000 --- a/server/neoforge_26_3/build.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -import net.fabricmc.loom.build.nesting.NestableJarGenerationTask - -plugins { - id("dev.architectury.loom-no-remap") version Versions.architecturyLoom -// id("architectury-plugin") version "3.5-SNAPSHOT" -} - -java.sourceCompatibility = JavaVersion.VERSION_25 -java.targetCompatibility = JavaVersion.VERSION_25 - -//architectury { -// platformSetupLoomIde() -// neoForge() -//} - -repositories { - maven("https://maven.neoforged.net/releases/") -} - -dependencies { - minecraft("com.mojang:minecraft:26.3") - neoForge("net.neoforged:neoforge:26.3.0.6-beta") - - implementation(include("net.kyori:adventure-platform-neoforge:7.2.0")!!) -} - -tasks { - processResources { - filesMatching("META-INF/neoforge.mods.toml") { - expand("version" to project.version) - } - } - - // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar - val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) - val shadowJarTask = named("shadowJar", Jar::class) - shadowJarTask.configure { dependsOn(processIncludeJars) } - loom.nestJars( - shadowJarTask, - files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) - ) - - shadowJar { - archiveFileName.set("[neoforge-26.3]AllMusic_Server-${project.version}.jar") - destinationDirectory.set(file("${parent!!.projectDir}/../build")) - } - - build { - dependsOn(shadowJar) - } -} diff --git a/server/neoforge_26_3/gradle.properties b/server/neoforge_26_3/gradle.properties deleted file mode 100644 index 7da18ea6..00000000 --- a/server/neoforge_26_3/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=neoforge diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java deleted file mode 100644 index d9282960..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.coloryr.allmusic.comm; - -import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.ModContainer; -import net.neoforged.fml.common.Mod; -import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.neoforged.neoforge.network.handling.IPayloadHandler; - -@Mod(AllMusicInit.MODID) -public class AllMusicInit implements IPayloadHandler { - - public static final String MODID = "allmusic"; - - public static IPayloadHandler handler; - - public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { - modEventBus.addListener(this::register); - } - - public void register(final RegisterPayloadHandlersEvent event) { - event.registrar("1.0") - .optional() - .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); - } - - @Override - public void handle(MusicCodec data, IPayloadContext iPayloadContext) { - if (handler != null) { - handler.handle(data, iPayloadContext); - } - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java deleted file mode 100644 index 434fa510..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.coloryr.allmusic.comm; - -import com.coloryr.allmusic.codec.MusicPack; -import com.coloryr.allmusic.codec.MusicPacketCodec; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.Identifier; - -public record MusicCodec(MusicPack pack) implements CustomPacketPayload { - public static final Identifier channel = - Identifier.fromNamespaceAndPath("allmusic", "channel"); - - public static final Type TYPE = new Type<>(channel); - public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), - (buf) -> { - return new MusicCodec(MusicPacketCodec.decode(buf)); - }); - - @Override - public Type type() { - return TYPE; - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java deleted file mode 100644 index 2e1109f1..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.server.core.AllMusic; -import com.coloryr.allmusic.server.core.music.PlayMusic; -import com.mojang.brigadier.CommandDispatcher; -import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.server.MinecraftServer; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; -import net.neoforged.neoforge.event.entity.player.PlayerEvent; -import net.neoforged.neoforge.event.server.ServerStartedEvent; -import net.neoforged.neoforge.event.server.ServerStoppingEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; - -@EventBusSubscriber(modid = "allmusic_server", value = Dist.DEDICATED_SERVER) -public class AllMusicServer { - public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Server"); - public static MinecraftServer server; - public static MinecraftServerAudiences audiences; - - @SubscribeEvent - public static void commonSetup(final FMLCommonSetupEvent event) { - AllMusic.log = new LogNeoForge(); - AllMusic.side = new SideNeoForge(); - } - - @SubscribeEvent - public static void onRegisterCommands(RegisterCommandsEvent event) { - if (event.getCommandSelection() != Commands.CommandSelection.INTEGRATED) { - CommandDispatcher dispatcher = event.getDispatcher(); - CommandNeoForge.instance.register(dispatcher); - } - } - - @SubscribeEvent - public static void onServerStarting(ServerStartedEvent event) { - if (event.getServer().isDedicatedServer()) { - audiences = MinecraftServerAudiences.of(event.getServer()); - server = event.getServer(); - AllMusic.init(new File(AllMusic.SERVER_DIR)); - AllMusic.start(); - Tasks.init(); - } - } - - @SubscribeEvent - public static void onServerStopping(ServerStoppingEvent event) { - if (event.getServer().isDedicatedServer()) { - AllMusic.stop(); - } - } - - @SubscribeEvent - public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { - if (server != null) { - PlayMusic.removeNowPlayPlayer(event.getEntity().getName().getString()); - } - } - - @SubscribeEvent - public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { - if (server != null) { - AllMusic.joinPlay(event.getEntity().getName().getString()); - } - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java deleted file mode 100644 index f6275e4e..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.server.core.command.CommandEX; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.suggestion.SuggestionProvider; -import com.mojang.brigadier.suggestion.Suggestions; -import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; - -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.function.Predicate; - -public class CommandNeoForge implements Command, Predicate, SuggestionProvider { - public static CommandNeoForge instance = new CommandNeoForge(); - - public void register(CommandDispatcher dispatcher) { - dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) - .then(Commands.argument("args", StringArgumentType.greedyString()) - .suggests(this).executes(this))); - } - - @Override - public int run(CommandContext context) { - var item = context.getSource(); - - var input = context.getInput(); - var temp = input.split(" "); - var arg = new String[temp.length - 1]; - System.arraycopy(temp, 1, arg, 0, arg.length); - - CommandEX.execute(item, context.getSource().getTextName(), arg); - - return 0; - } - - @Override - public boolean test(CommandSourceStack stack) { - return true; - } - - @Override - public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { - var item = context.getSource(); - - var input = context.getInput(); - var temp = input.split(" "); - var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; - System.arraycopy(temp, 1, arg, 0, temp.length - 1); - - if (input.endsWith(" ")) - builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); - - List results = CommandEX.getTabList(item, item.getTextName(), arg); - - for (String s : results) { - builder.suggest(s); - } - - return builder.buildFuture(); - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java deleted file mode 100644 index 2b12188a..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.server.core.side.IAllMusicLogger; -import net.kyori.adventure.text.Component; - -public class LogNeoForge implements IAllMusicLogger { - @Override - public void data(Component data) { - AllMusicServer.audiences.console().sendMessage(data); - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java deleted file mode 100644 index 7e79134a..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.coloryr.allmusic.server; - -import com.coloryr.allmusic.codec.MusicPack; -import com.coloryr.allmusic.comm.MusicCodec; -import com.coloryr.allmusic.server.core.AllMusic; -import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; -import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; -import com.coloryr.allmusic.server.core.side.BaseSide; -import com.coloryr.allmusic.server.event.MusicAddEvent; -import com.coloryr.allmusic.server.event.MusicPlayEvent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.ClickEvent; -import net.kyori.adventure.text.minimessage.MiniMessage; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.PermissionSet; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.network.PacketDistributor; - -import java.io.File; -import java.util.Collection; - -public class SideNeoForge extends BaseSide { - @Override - public void runTask(Runnable run) { - AllMusicServer.server.execute(run); - } - - @Override - public void runTask(Runnable run1, int delay) { - Tasks.add(new TaskItem() {{ - tick = delay; - run = run1; - }}); - } - - @Override - public boolean checkPermission(Object player, String permission) { - return checkPermission(player); - } - - @Override - public boolean checkPermission(Object player) { - CommandSourceStack source = (CommandSourceStack) player; - return source.permissions() == PermissionSet.ALL_PERMISSIONS; - } - - @Override - public boolean isPlayer(Object player) { - CommandSourceStack source = (CommandSourceStack) player; - return source.isPlayer(); - } - - @Override - public boolean needPlay(boolean islist) { - for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { - if (!AllMusic.isSkip(player.getName().getString(), null, false, islist)) { - return true; - } - } - return false; - } - - @Override - public Collection getPlayers() { - return AllMusicServer.server.getPlayerList().getPlayers(); - } - - @Override - public String getPlayerName(Object player) { - if (player instanceof ServerPlayer player1) { - return player1.getName().getString(); - } - - return null; - } - - @Override - public String getPlayerServer(Object player) { - return null; - } - - @Override - public void send(Object player, MusicPack pack) { - if (player instanceof ServerPlayer player1) { - send(player1, pack); - } - } - - @Override - public Object getPlayer(String player) { - return AllMusicServer.server.getPlayerList().getPlayerByName(player); - } - - @Override - public void sendBar(Object player, Component data) { - if (player instanceof ServerPlayer player1) { - AllMusicServer.audiences.audience(player1).sendActionBar(data); - } - } - - @Override - public File getFolder() { - return new File(AllMusic.SERVER_DIR); - } - - @Override - public void broadcast(Component message) { - for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { - if (!AllMusic.isSkip(player.getName().getString(), null, false)) { - AllMusicServer.audiences.audience(player).sendActionBar(message); - } - } - } - - @Override - public void sendMessage(Object obj, Component message) { - if (obj instanceof CommandSourceStack source) { - AllMusicServer.audiences.audience(source).sendActionBar(message); - } - } - - @Override - public boolean onMusicPlay(SongInfoObj obj) { - MusicPlayEvent event = new MusicPlayEvent(obj); - NeoForge.EVENT_BUS.post(event); - return event.isCancel(); - } - - @Override - public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { - CommandSourceStack source = (CommandSourceStack) obj; - MusicAddEvent event = new MusicAddEvent(music, source.getPlayer()); - NeoForge.EVENT_BUS.post(event); - return event.isCancel(); - } - - @Override - public Component miniMessage(String input) { - MiniMessage mm = MiniMessage.miniMessage(); - return mm.deserialize(input); - } - - @Override - public Component miniMessageRun(String input, String command) { - Component component = miniMessage(input); - return component.clickEvent(ClickEvent.runCommand(command)); - } - - @Override - public Component miniMessageSuggest(String input, String command) { - Component component = miniMessage(input); - return component.clickEvent(ClickEvent.suggestCommand(command)); - } - - private void send(ServerPlayer players, MusicPack data) { - if (players == null) - return; - runTask(() -> PacketDistributor.sendToPlayer(players, new MusicCodec(data))); - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java deleted file mode 100644 index 40f9a5b9..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.coloryr.allmusic.server; - -public class TaskItem { - public int tick; - public Runnable run; -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java deleted file mode 100644 index b861abcc..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.coloryr.allmusic.server; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class Tasks { - private static final List taskItems = new CopyOnWriteArrayList<>(); - - public static void init() { - AllMusicServer.server.addTickable(Tasks::tick); - } - - public static void tick() { - for (TaskItem item : new ArrayList<>(taskItems)) { - item.tick--; - if (item.tick <= 0) { - taskItems.remove(item); - item.run.run(); - } - } - } - - public static void add(TaskItem item) { - taskItems.add(item); - } -} \ No newline at end of file diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java deleted file mode 100644 index 7c730137..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.coloryr.allmusic.server.event; - -import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.bus.api.Event; - -public class MusicAddEvent extends Event { - /** - * 添加的音乐 - */ - private final PlayerAddMusicObj music; - /** - * 添加者 - */ - private final ServerPlayer player; - - private boolean cancel; - - public MusicAddEvent(PlayerAddMusicObj id, ServerPlayer player) { - this.music = id; - this.player = player; - } - - public boolean isCancel() { - return cancel; - } - - public void setCancel(boolean cancel) { - this.cancel = cancel; - } - - public ServerPlayer getPlayer() { - return player; - } - - public PlayerAddMusicObj getMusic() { - return music; - } -} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java deleted file mode 100644 index b16e0f95..00000000 --- a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.coloryr.allmusic.server.event; - -import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; -import net.neoforged.bus.api.Event; - -public class MusicPlayEvent extends Event { - private final SongInfoObj music; - private boolean cancel; - - public MusicPlayEvent(SongInfoObj music) { - this.music = music; - } - - public boolean isCancel() { - return cancel; - } - - public void setCancel(boolean cancel) { - this.cancel = cancel; - } - - public SongInfoObj getMusic() { - return music; - } -} diff --git a/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml deleted file mode 100644 index 177695c3..00000000 --- a/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml +++ /dev/null @@ -1,36 +0,0 @@ -modLoader = "javafml" -loaderVersion = "[2,)" -license = "GPL-3.0" -issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues" - -[[mods]] -modId = "allmusic" -version = "${version}" -displayName = "AllMusic" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Mod''' - -[[mods]] -modId = "allmusic_server" -version = "${version}" -displayName = "AllMusic Server" -displayURL = "https://github.com/Coloryr/AllMusic/" -logoFile = "icon.png" -authors = "Color_yr" -description = '''AllMusic Server Mod''' - -[[dependencies.allmusic_server]] -modId = "neoforge" -type = "required" -versionRange = "[26.3.0.6-beta,)" -ordering = "NONE" -side = "BOTH" - -[[dependencies.allmusic_server]] -modId = "minecraft" -type = "required" -versionRange = "[26.3,)" -ordering = "NONE" -side = "BOTH" \ No newline at end of file diff --git a/server/neoforge_26_3/src/main/resources/icon.png b/server/neoforge_26_3/src/main/resources/icon.png deleted file mode 100644 index b2b5fa790ca8e14661cd169aa56e1ca5dd818a76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32781 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 diff --git a/settings.gradle.kts b/settings.gradle.kts index dceaacb3..86acdff1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,7 +14,6 @@ include(":client:fabric_1_21_8") include(":client:fabric_1_21_11") include(":client:fabric_26_1") include(":client:fabric_26_2") -include(":client:fabric_26_3") include(":client:forge_1_7_10") include(":client:forge_1_12_2") @@ -28,7 +27,6 @@ include(":client:neoforge_1_21_6") include(":client:neoforge_1_21_11") include(":client:neoforge_26_1") include(":client:neoforge_26_2") -include(":client:neoforge_26_3") include(":server") @@ -40,7 +38,6 @@ include(":server:fabric_1_21_6") include(":server:fabric_1_21_11") include(":server:fabric_26_1") include(":server:fabric_26_2") -include(":server:fabric_26_3") include(":server:forge_1_7_10") include(":server:forge_1_12_2") @@ -53,7 +50,6 @@ include(":server:neoforge_1_21_6") include(":server:neoforge_1_21_11") include(":server:neoforge_26_1") include(":server:neoforge_26_2") -include(":server:neoforge_26_3") include(":server:spigot") include(":server:paper") @@ -78,7 +74,6 @@ include(":onejar:fabric_1_21_6") include(":onejar:fabric_1_21_11") include(":onejar:fabric_26_1") include(":onejar:fabric_26_2") -include(":onejar:fabric_26_3") include(":onejar:neoforge_1_21") include(":onejar:neoforge_1_21_4") @@ -86,7 +81,6 @@ include(":onejar:neoforge_1_21_6") include(":onejar:neoforge_1_21_11") include(":onejar:neoforge_26_1") include(":onejar:neoforge_26_2") -include(":onejar:neoforge_26_3") pluginManagement { repositories { From 2c92d0fe6d92a873367cbca523b899051b4dfabb Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 20:53:04 +0800 Subject: [PATCH 37/46] fix: restore upstream 26.3 modules and verify compatibility in CI --- .github/workflows/client.yml | 16 +- .github/workflows/compatibility.yml | 175 +++++ .github/workflows/onejar.yml | 16 +- .github/workflows/server.yml | 16 +- build.gradle.kts | 7 +- client/fabric_26_3/build.gradle.kts | 34 + .../allmusic/client/AllMusicClient.java | 135 ++++ .../allmusic/client/CoreRenderTarget.java | 136 ++++ .../allmusic/client/FloatRenderState.java | 30 + .../coloryr/allmusic/client/MiniMessage.java | 645 ++++++++++++++++++ .../coloryr/allmusic/client/PicRender.java | 72 ++ .../coloryr/allmusic/client/TexRender.java | 109 +++ .../allmusic/client/mixins/GuiShow.java | 18 + .../allmusic/client/mixins/QuitServer.java | 16 + .../allmusic/client/mixins/Register.java | 16 + .../allmusic/client/mixins/SoundEvent.java | 38 ++ .../coloryr/allmusic/client/mixins/Tick.java | 16 + .../allmusic/client/others/VoiceChat.java | 32 + .../coloryr/allmusic/comm/AllMusicInit.java | 11 + .../com/coloryr/allmusic/comm/MusicCodec.java | 23 + .../resources/allmusic_client.mixins.json | 18 + .../resources/assets/allmusic_client/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 42 ++ client/neoforge_26_3/build.gradle.kts | 40 ++ client/neoforge_26_3/gradle.properties | 1 + .../allmusic/client/AllMusicClient.java | 193 ++++++ .../allmusic/client/CoreRenderTarget.java | 133 ++++ .../allmusic/client/FloatRenderState.java | 30 + .../coloryr/allmusic/client/MiniMessage.java | 645 ++++++++++++++++++ .../coloryr/allmusic/client/PicRender.java | 71 ++ .../coloryr/allmusic/client/TexRender.java | 109 +++ .../allmusic/client/others/ModernUI.java | 15 + .../allmusic/client/others/VoiceChat.java | 34 + .../coloryr/allmusic/comm/AllMusicInit.java | 33 + .../com/coloryr/allmusic/comm/MusicCodec.java | 24 + .../resources/META-INF/neoforge.mods.toml | 42 ++ .../neoforge_26_3/src/main/resources/icon.png | Bin 0 -> 32781 bytes gradlew | 0 onejar/fabric_26_3/build.gradle.kts | 68 ++ .../main/resources/assets/allmusic/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 49 ++ onejar/neoforge_26_3/build.gradle.kts | 75 ++ onejar/neoforge_26_3/gradle.properties | 1 + .../resources/META-INF/neoforge.mods.toml | 65 ++ .../neoforge_26_3/src/main/resources/icon.png | Bin 0 -> 32781 bytes server/fabric_26_3/build.gradle.kts | 37 + .../coloryr/allmusic/comm/AllMusicInit.java | 11 + .../com/coloryr/allmusic/comm/MusicCodec.java | 23 + .../allmusic/server/AllMusicServer.java | 33 + .../allmusic/server/CommandFabric.java | 69 ++ .../coloryr/allmusic/server/LogFabric.java | 12 + .../coloryr/allmusic/server/SideFabric.java | 157 +++++ .../com/coloryr/allmusic/server/TaskItem.java | 6 + .../com/coloryr/allmusic/server/Tasks.java | 27 + .../allmusic/server/event/MusicAddEvent.java | 24 + .../allmusic/server/event/MusicPlayEvent.java | 29 + .../server/mixin/ServerPlayerMixin.java | 25 + .../resources/allmusic_server.mixins.json | 17 + .../resources/assets/allmusic_server/icon.png | Bin 0 -> 32781 bytes .../src/main/resources/fabric.mod.json | 37 + server/neoforge_26_3/build.gradle.kts | 51 ++ server/neoforge_26_3/gradle.properties | 1 + .../coloryr/allmusic/comm/AllMusicInit.java | 33 + .../com/coloryr/allmusic/comm/MusicCodec.java | 24 + .../allmusic/server/AllMusicServer.java | 74 ++ .../allmusic/server/CommandNeoForge.java | 66 ++ .../coloryr/allmusic/server/LogNeoForge.java | 11 + .../coloryr/allmusic/server/SideNeoForge.java | 161 +++++ .../com/coloryr/allmusic/server/TaskItem.java | 6 + .../com/coloryr/allmusic/server/Tasks.java | 27 + .../allmusic/server/event/MusicAddEvent.java | 39 ++ .../allmusic/server/event/MusicPlayEvent.java | 25 + .../resources/META-INF/neoforge.mods.toml | 36 + .../neoforge_26_3/src/main/resources/icon.png | Bin 0 -> 32781 bytes settings.gradle.kts | 6 + 75 files changed, 4309 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/compatibility.yml create mode 100644 client/fabric_26_3/build.gradle.kts create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 client/fabric_26_3/src/main/resources/allmusic_client.mixins.json create mode 100644 client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png create mode 100644 client/fabric_26_3/src/main/resources/fabric.mod.json create mode 100644 client/neoforge_26_3/build.gradle.kts create mode 100644 client/neoforge_26_3/gradle.properties create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 client/neoforge_26_3/src/main/resources/icon.png mode change 100644 => 100755 gradlew create mode 100644 onejar/fabric_26_3/build.gradle.kts create mode 100644 onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png create mode 100644 onejar/fabric_26_3/src/main/resources/fabric.mod.json create mode 100644 onejar/neoforge_26_3/build.gradle.kts create mode 100644 onejar/neoforge_26_3/gradle.properties create mode 100644 onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 onejar/neoforge_26_3/src/main/resources/icon.png create mode 100644 server/fabric_26_3/build.gradle.kts create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java create mode 100644 server/fabric_26_3/src/main/resources/allmusic_server.mixins.json create mode 100644 server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png create mode 100644 server/fabric_26_3/src/main/resources/fabric.mod.json create mode 100644 server/neoforge_26_3/build.gradle.kts create mode 100644 server/neoforge_26_3/gradle.properties create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java create mode 100644 server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java create mode 100644 server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 server/neoforge_26_3/src/main/resources/icon.png diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index 37169be8..947571ef 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -28,7 +28,14 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' - - name: Set up JDK 25 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - name: Set up JDK 25 (Minecraft toolchain) + id: java25 uses: actions/setup-java@v4 with: java-version: '25' @@ -37,8 +44,13 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: build shell: cmd + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} run: | - gradlew :client:fabric_26_2:build --configure-on-demand --parallel --max-workers=4 + set "JAVA_HOME=%JAVA_HOME_21%" + set "PATH=%JAVA_HOME_21%\bin;%PATH%" + gradlew.bat -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :client:fabric_26_2:build --configure-on-demand --parallel --max-workers=4 - name: update uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml new file mode 100644 index 00000000..7c2edb57 --- /dev/null +++ b/.github/workflows/compatibility.yml @@ -0,0 +1,175 @@ +name: AllMusic Compatibility + +on: + push: + branches: [chore/minecraft-26.3] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: allmusic-compatibility-${{ github.ref }} + cancel-in-progress: true + +jobs: + legacy-client: + name: Legacy Fabric 1.20.1 client + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + - name: Set up JDK 25 (Minecraft 26.3 toolchain) + id: java25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + - uses: gradle/actions/setup-gradle@v4 + - name: Build legacy client + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} + run: | + export JAVA_HOME="$JAVA_HOME_21" + export PATH="$JAVA_HOME/bin:$PATH" + ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :client:fabric_1_20_1:build + + modern-clients: + name: Minecraft 26.3 clients + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + task: + - :client:fabric_26_3:build + - :client:neoforge_26_3:build + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + - name: Set up JDK 25 (Minecraft 26.3 toolchain) + id: java25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + - uses: gradle/actions/setup-gradle@v4 + - name: Build client + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} + TASK: ${{ matrix.task }} + run: | + export JAVA_HOME="$JAVA_HOME_21" + export PATH="$JAVA_HOME/bin:$PATH" + ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + + legacy-server: + name: Legacy Paper server + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + - name: Set up JDK 25 (Minecraft 26.3 toolchain) + id: java25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + - uses: gradle/actions/setup-gradle@v4 + - name: Build legacy server + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} + run: | + export JAVA_HOME="$JAVA_HOME_21" + export PATH="$JAVA_HOME/bin:$PATH" + ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :server:paper:build + + modern-servers: + name: Minecraft 26.3 servers + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + task: + - :server:fabric_26_3:build + - :server:neoforge_26_3:build + - :server:paper_26_3:build + - :server:folia_26_3:build + - :server:spigot_26_3:build + - :server:velocity_26_3:build + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + - name: Set up JDK 25 (Minecraft 26.3 toolchain) + id: java25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + - uses: gradle/actions/setup-gradle@v4 + - name: Build server + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} + TASK: ${{ matrix.task }} + run: | + export JAVA_HOME="$JAVA_HOME_21" + export PATH="$JAVA_HOME/bin:$PATH" + ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + + onejar: + name: Minecraft 26.3 one-jars + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + task: + - :onejar:fabric_26_3:build + - :onejar:neoforge_26_3:build + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '21' + - name: Set up JDK 25 (Minecraft 26.3 toolchain) + id: java25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + - uses: gradle/actions/setup-gradle@v4 + - name: Build one-jar + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} + TASK: ${{ matrix.task }} + run: | + export JAVA_HOME="$JAVA_HOME_21" + export PATH="$JAVA_HOME/bin:$PATH" + ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" diff --git a/.github/workflows/onejar.yml b/.github/workflows/onejar.yml index fd87c273..08e99e21 100644 --- a/.github/workflows/onejar.yml +++ b/.github/workflows/onejar.yml @@ -27,7 +27,14 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' - - name: Set up JDK 25 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - name: Set up JDK 25 (Minecraft toolchain) + id: java25 uses: actions/setup-java@v4 with: java-version: '25' @@ -36,8 +43,13 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: build shell: cmd + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} run: | - gradlew :onejar:buildOneJar + set "JAVA_HOME=%JAVA_HOME_21%" + set "PATH=%JAVA_HOME_21%\bin;%PATH%" + gradlew.bat -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :onejar:buildOneJar - name: update uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index 3fd01265..b2aad8e8 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -27,7 +27,14 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' - - name: Set up JDK 25 + - name: Set up JDK 21 (Gradle launcher) + id: java21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + - name: Set up JDK 25 (Minecraft toolchain) + id: java25 uses: actions/setup-java@v4 with: java-version: '25' @@ -36,8 +43,13 @@ jobs: uses: gradle/actions/setup-gradle@v3 - name: build shell: cmd + env: + JAVA_HOME_21: ${{ steps.java21.outputs.path }} + JAVA_HOME_25: ${{ steps.java25.outputs.path }} run: | - gradlew :server:buildServer + set "JAVA_HOME=%JAVA_HOME_21%" + set "PATH=%JAVA_HOME_21%\bin;%PATH%" + gradlew.bat -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :server:buildServer - name: update uses: actions/upload-artifact@v4 with: diff --git a/build.gradle.kts b/build.gradle.kts index c6a9b98c..cd3debab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,8 +50,13 @@ allprojects { } afterEvaluate { + // Launch Gradle on Java 21, but compile Minecraft 26.x adapters with Java 25. + // Legacy module bytecode targets stay unchanged. + java.toolchain.languageVersion.set(JavaLanguageVersion.of( + if (java.targetCompatibility == JavaVersion.VERSION_25) 25 else 21 + )) if (plugins.hasPlugin("com.gtnewhorizons.retrofuturagradle")) return@afterEvaluate - if (plugins.hasPlugin("fabric-loom")) return@afterEvaluate + if (plugins.hasPlugin("fabric-loom") || plugins.hasPlugin("net.fabricmc.fabric-loom")) return@afterEvaluate val rfgObfAttr = Attribute.of("com.gtnewhorizons.retrofuturagradle.obfuscation", String::class.java) val rfgDeobfAttr = Attribute.of("rfgDeobfuscatorTransformed", Boolean::class.javaObjectType) diff --git a/client/fabric_26_3/build.gradle.kts b/client/fabric_26_3/build.gradle.kts new file mode 100644 index 00000000..eb685e4a --- /dev/null +++ b/client/fabric_26_3/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + id("net.fabricmc.fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +dependencies { + minecraft("com.mojang:minecraft:26.3") + implementation("net.fabricmc:fabric-loader:0.19.5") + + implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") +} + +tasks { + processResources { + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + shadowJar { + archiveFileName.set("[fabric-26.3]AllMusic_Client-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java new file mode 100644 index 00000000..97073e42 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -0,0 +1,135 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicBridge; +import com.coloryr.allmusic.client.core.AllMusicCore; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.comm.MusicCodec; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.sounds.SoundSource; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class AllMusicClient implements ClientModInitializer, AllMusicBridge { + public static final String MODID = "allmusic_client"; + public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); + public static GuiGraphicsExtractor context; + + public static void update(GuiGraphicsExtractor draw) { + context = draw; + AllMusicCore.hudUpdate(); + } + + public int getScreenWidth() { + return Minecraft.getInstance().getWindow().getGuiScaledWidth(); + } + + public int getScreenHeight() { + return Minecraft.getInstance().getWindow().getGuiScaledHeight(); + } + + public int getTextWidth(String item) { + return Minecraft.getInstance().font.width(item); + } + + public int getFontHeight() { + return Minecraft.getInstance().font.lineHeight; + } + + public void sendMessage(String data) { + data = "[AllMusic Client]" + data; + LOGGER.warn(data); + String finalData = data; + Minecraft.getInstance().execute(() -> { + if (Minecraft.getInstance().player == null) + return; + Minecraft.getInstance().player.sendSystemMessage(Component.literal(finalData)); + }); + } + + public float getVolume() { + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); + } + + @Override + public void stopPlayMusic() { + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.MUSIC); + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.RECORDS); + } + + @Override + public TextFrameBuffer makeTextRender(String name) { + return new CoreRenderTarget(name); + } + + @Override + public TextureRender makeTextureRender(String file) { + return new TexRender(file); + } + + @Override + public PictureFrameBuffer makePictureRender(int size) { + return new PicRender(size); + } + + @Override + public String readText(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + try (InputStream inputStream = resource.open()) { + byte[] bytes = inputStream.readAllBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public InputStream readFile(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + return resource.open(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void kick() { + Minecraft client = Minecraft.getInstance(); + + ClientPacketListener packetListener = client.getConnection(); + if (packetListener != null) { + Connection connection = packetListener.getConnection(); + connection.disconnect(Component.nullToEmpty("Old AllMusic server")); + } + } + + @Override + public void onInitializeClient() { + ClientPlayNetworking.registerGlobalReceiver(MusicCodec.ID, (pack, handler) -> AllMusicCore.packDo(pack.pack())); + + AllMusicCore.init(FabricLoader.getInstance().getConfigDir(), this); + + ClientLifecycleEvents.CLIENT_STOPPING.register((a) -> AllMusicCore.stop()); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java new file mode 100644 index 00000000..9124fb0d --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -0,0 +1,136 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.network.chat.Component; + +import java.util.ArrayList; +import java.util.List; + +public class CoreRenderTarget extends TextFrameBuffer { + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); + } + + @Override + public void putText(String text, int y, int color, boolean shadow) { + color = (color & 0x00FFFFFF) | 0xFF000000; + Component component = MiniMessage.parse(text); + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { + return; + } + + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; + } + texts.add(new TextItem<>(width, height, y, component, shadow, color)); + } + + @Override + public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) getOffset(entry, maxWidth); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } + } + } + + @Override + public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } + if (line >= texts.size()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextItem entry = texts.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } + } + + @Override + public Point2f getLine(int line) { + if (line >= texts.size()) { + return new Point2f(0, 0); + } + TextItem entry = texts.get(line); + return new Point2f(entry.width, entry.height); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); + } +} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java new file mode 100644 index 00000000..d143b08b --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java @@ -0,0 +1,30 @@ +package com.coloryr.allmusic.client; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.renderpearl.api.pipeline.RenderPipeline; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.state.gui.GuiElementRenderState; +import org.joml.Matrix3x2f; + +public record FloatRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x0, + float y0, float x1, float y1, float u0, float u1, float v0, float v1, int color, + ScreenRectangle scissorArea, + ScreenRectangle bounds) implements GuiElementRenderState { + public FloatRenderState(RenderPipeline renderPipeline, TextureSetup textureSetup, Matrix3x2f matrix3x2f, + float i, float j, float k, float l, float f, float g, float h, float m, int n, ScreenRectangle screenRectangle) { + this(renderPipeline, textureSetup, matrix3x2f, i, j, k, l, f, g, h, m, n, screenRectangle, getBounds(i, j, k, l, matrix3x2f, screenRectangle)); + } + + private static ScreenRectangle getBounds(float i, float j, float k, float l, Matrix3x2f matrix3x2f, ScreenRectangle screenRectangle) { + ScreenRectangle screenRectangle2 = (new ScreenRectangle((int) Math.ceil(i + 1), (int) Math.ceil(j + 1), (int) Math.ceil(k - i + 1), (int) Math.ceil(l - j + 1))).transformMaxBounds(matrix3x2f); + return screenRectangle != null ? screenRectangle.intersection(screenRectangle2) : screenRectangle2; + } + + public void buildVertices(VertexConsumer vertexConsumer) { + vertexConsumer.addVertexWith2DPose(pose(), x0(), y0()).setUv(u0(), v0()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x0(), y1()).setUv(u0(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y1()).setUv(u1(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y0()).setUv(u1(), v0()).setColor(color()); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java new file mode 100644 index 00000000..88129399 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java @@ -0,0 +1,645 @@ +package com.coloryr.allmusic.client; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.*; +import net.minecraft.resources.Identifier; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MiniMessage { + // 正则表达式匹配标签 + private static final Pattern TAG_PATTERN = Pattern.compile( + "<(/?)(\\w+)(?:\\(([^)]+)\\))?(?::([^>]+))?>" + ); + + // 颜色映射表 + private static final Map COLOR_MAP = new HashMap<>(); + // 装饰器映射 + private static final Map DECORATION_MAP = new HashMap<>(); + + static { + COLOR_MAP.put("black", ChatFormatting.BLACK); + COLOR_MAP.put("dark_blue", ChatFormatting.DARK_BLUE); + COLOR_MAP.put("dark_green", ChatFormatting.DARK_GREEN); + COLOR_MAP.put("dark_aqua", ChatFormatting.DARK_AQUA); + COLOR_MAP.put("dark_red", ChatFormatting.DARK_RED); + COLOR_MAP.put("dark_purple", ChatFormatting.DARK_PURPLE); + COLOR_MAP.put("gold", ChatFormatting.GOLD); + COLOR_MAP.put("gray", ChatFormatting.GRAY); + COLOR_MAP.put("dark_gray", ChatFormatting.DARK_GRAY); + COLOR_MAP.put("blue", ChatFormatting.BLUE); + COLOR_MAP.put("green", ChatFormatting.GREEN); + COLOR_MAP.put("aqua", ChatFormatting.AQUA); + COLOR_MAP.put("red", ChatFormatting.RED); + COLOR_MAP.put("light_purple", ChatFormatting.LIGHT_PURPLE); + COLOR_MAP.put("yellow", ChatFormatting.YELLOW); + COLOR_MAP.put("white", ChatFormatting.WHITE); + } + + static { + DECORATION_MAP.put("bold", ChatFormatting.BOLD); + DECORATION_MAP.put("italic", ChatFormatting.ITALIC); + DECORATION_MAP.put("underlined", ChatFormatting.UNDERLINE); + DECORATION_MAP.put("strikethrough", ChatFormatting.STRIKETHROUGH); + DECORATION_MAP.put("obfuscated", ChatFormatting.OBFUSCATED); + } + + /** + * 解析 MiniMessage 字符串为 Component + */ + public static Component parse(String input) { + MutableComponent root = Component.literal(""); + Stack styleStack = new Stack<>(); + StyleContext currentStyle = new StyleContext(); + styleStack.push(currentStyle); + + StringBuilder textBuffer = new StringBuilder(); + int pos = 0; + int length = input.length(); + + while (pos < length) { + char c = input.charAt(pos); + + if (c == '<') { + // 先处理已积累的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + textBuffer.setLength(0); + } + + // 查找标签结束位置 + int endTag = findClosingTag(input, pos); + if (endTag == -1) { + // 没有找到闭合标签,当作普通文本处理 + textBuffer.append(c); + pos++; + continue; + } + + String tagContent = input.substring(pos, endTag + 1); + Matcher matcher = TAG_PATTERN.matcher(tagContent); + + if (matcher.matches()) { + boolean isClosing = !matcher.group(1).isEmpty(); + String tagName = matcher.group(2).toLowerCase(Locale.ROOT); + String params = matcher.group(3); + String value = matcher.group(4); + + if (isClosing) { + // 关闭标签 - 只关闭对应的开始标签 + String expectedOpenTag = matcher.group(2).toLowerCase(Locale.ROOT); + + // 查找对应的开始标签并关闭 + if (styleStack.size() > 1) { + // 从栈顶向下查找匹配的开始标签 + int index = styleStack.size() - 1; + boolean found = false; + + while (index >= 0) { + StyleContext ctx = styleStack.get(index); + if (ctx.tagName != null && ctx.tagName.equals(expectedOpenTag)) { + // 找到匹配的开始标签,关闭到这一层 + while (styleStack.size() > index + 1) { + styleStack.pop(); + } + currentStyle = styleStack.peek(); + found = true; + break; + } + index--; + } + + if (!found) { + // 没找到匹配的标签,忽略此关闭标签 + } + } + } else { + // 处理各种标签 + StyleContext newStyle = currentStyle.copy(); + + switch (tagName) { + case "color": + handleColorTag(newStyle, params, value); + break; + case "shadow": + handleShadowTag(newStyle, params, value); + break; + case "decoration": + handleDecorationTag(newStyle, params, value); + break; + case "reset": + newStyle = new StyleContext(); + break; + case "rainbow": + handleRainbowTag(newStyle, params, value); + break; + case "gradient": + handleGradientTag(newStyle, params, value); + break; + case "transition": + handleTransitionTag(newStyle, params, value); + break; + case "font": + handleFontTag(newStyle, params, value); + break; + default: + // 未知标签,忽略 + break; + } + + // 记录标签名称以便关闭时匹配 + newStyle.tagName = tagName; + styleStack.push(newStyle); + currentStyle = newStyle; + } + } else { + // 无效标签,当作普通文本 + textBuffer.append(tagContent); + } + + pos = endTag + 1; + } else { + textBuffer.append(c); + pos++; + } + } + + // 处理剩余的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + } + + return root; + } + + /** + * 处理颜色标签 + * 支持格式: , , + */ + private static void handleColorTag(StyleContext style, String params, String value) { + if (value != null) { + // 格式: 或 + TextColor textColor = parseColor(value); + if (textColor != null) { + style.color = textColor; + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } + } else if (params != null) { + // 格式: + if (params.equalsIgnoreCase("rgb")) { + String[] parts = value.split(","); + if (parts.length == 3) { + try { + int r = Integer.parseInt(parts[0].trim()); + int g = Integer.parseInt(parts[1].trim()); + int b = Integer.parseInt(parts[2].trim()); + style.color = TextColor.fromRgb((r << 16) | (g << 8) | b); + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } catch (NumberFormatException e) { + // 忽略解析错误 + } + } + } + } + } + + /** + * 处理阴影标签 + * 支持格式: , , , + */ + private static void handleShadowTag(StyleContext style, String params, String value) { + if (value == null) { + style.shadow = true; + } else if (value.equalsIgnoreCase("true")) { + style.shadow = true; + } else if (value.equalsIgnoreCase("false")) { + style.shadow = false; + } else { + // 自定义阴影颜色 + TextColor shadowColor = parseColor(value); + if (shadowColor != null) { + style.shadow = true; + style.shadowColor = shadowColor; + } + } + } + + /** + * 处理装饰标签 + * 支持格式: , , + */ + private static void handleDecorationTag(StyleContext style, String params, String value) { + if (params == null) return; + + String[] parts = params.split(":"); + String decorationName = parts[0].toLowerCase(Locale.ROOT); + boolean enabled = true; + + if (parts.length > 1) { + enabled = Boolean.parseBoolean(parts[1]); + } else if (value != null) { + enabled = Boolean.parseBoolean(value); + } + + ChatFormatting formatting = DECORATION_MAP.get(decorationName); + if (formatting != null) { + style.decorations.put(formatting, enabled); + } + } + + /** + * 处理彩虹色标签 + * 支持格式: , , , + */ + private static void handleRainbowTag(StyleContext style, String params, String value) { + float saturation = 1.0f; + float brightness = 1.0f; + + if (params != null) { + if (params.equalsIgnoreCase("saturation") && value != null) { + try { + saturation = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } else if (params.equalsIgnoreCase("brightness") && value != null) { + try { + brightness = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } + } + + style.rainbow = true; + style.gradient = false; + style.transition = false; + style.rainbowSaturation = saturation; + style.rainbowBrightness = brightness; + } + + /** + * 处理渐变色标签 + * 支持格式: , + * 渐变是逐字符过渡 + */ + private static void handleGradientTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = start; + style.gradientEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.gradientEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理过渡色标签 + * 支持格式: , + * 过渡是整个文本块的颜色过渡(类似整体渐变效果) + */ + private static void handleTransitionTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = start; + style.transitionEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.transitionEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理字体标签 + * 支持格式: , + */ + private static void handleFontTag(StyleContext style, String params, String value) { + String fontName = value != null ? value : params; + if (fontName != null) { + style.font = fontName; + } + } + + /** + * 解析颜色字符串 + * 支持: 颜色名称, #RRGGBB, #RGB, 十六进制数值 + */ + private static TextColor parseColor(String colorStr) { + if (colorStr == null) return null; + + // 检查是否为预定义颜色名称 + ChatFormatting formatting = COLOR_MAP.get(colorStr.toLowerCase(Locale.ROOT)); + if (formatting != null) { + return TextColor.fromLegacyFormat(formatting); + } + + // 检查十六进制格式 + if (colorStr.startsWith("#")) { + try { + if (colorStr.length() == 7) { + return TextColor.parseColor(colorStr).getOrThrow(); + } else if (colorStr.length() == 4) { + // 扩展 #RGB 到 #RRGGBB + String expanded = "#" + + colorStr.charAt(1) + colorStr.charAt(1) + + colorStr.charAt(2) + colorStr.charAt(2) + + colorStr.charAt(3) + colorStr.charAt(3); + return TextColor.parseColor(expanded).getOrThrow(); + } + } catch (Exception e) { + // 忽略解析错误 + } + } + + return null; + } + + /** + * 查找标签闭合位置 + */ + private static int findClosingTag(String input, int start) { + int pos = start + 1; + while (pos < input.length()) { + char c = input.charAt(pos); + if (c == '>') { + return pos; + } + pos++; + } + return -1; + } + + /** + * 添加文本到组件 + */ + private static void appendText(MutableComponent root, String text, StyleContext styleContext) { + if (text.isEmpty()) return; + + // 如果有渐变效果,需要逐字符处理 + if (styleContext.gradient && styleContext.gradientStart != null && styleContext.gradientEnd != null) { + appendGradientText(root, text, styleContext); + } + // 如果有过渡效果,需要逐字符处理 + else if (styleContext.transition && styleContext.transitionStart != null && styleContext.transitionEnd != null) { + appendTransitionText(root, text, styleContext); + } + // 如果有彩虹效果,需要逐字符处理 + else if (styleContext.rainbow) { + appendRainbowText(root, text, styleContext); + } + // 普通文本 + else { + MutableComponent component = Component.literal(text); + component.setStyle(styleContext.buildStyle()); + root.append(component); + } + } + + /** + * 带渐变效果的文本追加(逐字符颜色变化) + */ + private static void appendGradientText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float ratio = (float) i / (length - 1); + TextColor gradientColor = interpolateColor(styleContext.gradientStart, styleContext.gradientEnd, ratio); + + Style charStyle = baseStyle.withColor(gradientColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 带过渡效果的文本追加(整体颜色过渡) + */ + private static void appendTransitionText(MutableComponent root, String text, StyleContext styleContext) { + Style baseStyle = styleContext.buildBaseStyle(); + + // 过渡效果:为整个文本块创建一个颜色过渡 + MutableComponent transitionComponent = Component.literal(text); + + // 应用过渡样式(这里使用颜色插值,但也可以根据需求调整) + if (styleContext.transitionStart != null && styleContext.transitionEnd != null) { + // 可以使用整体渐变或者固定起始色 + transitionComponent.setStyle(baseStyle.withColor(styleContext.transitionStart)); + } else { + transitionComponent.setStyle(baseStyle); + } + + root.append(transitionComponent); + } + + /** + * 带彩虹效果的文本追加 + */ + private static void appendRainbowText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float hue = (float) i / length; + int rgb = java.awt.Color.HSBtoRGB(hue, styleContext.rainbowSaturation, styleContext.rainbowBrightness); + TextColor rainbowColor = TextColor.fromRgb(rgb & 0xFFFFFF); + + Style charStyle = baseStyle.withColor(rainbowColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 颜色插值 + */ + private static TextColor interpolateColor(TextColor start, TextColor end, float ratio) { + int startRgb = start.getValue(); + int endRgb = end.getValue(); + + int r1 = (startRgb >> 16) & 0xFF; + int g1 = (startRgb >> 8) & 0xFF; + int b1 = startRgb & 0xFF; + + int r2 = (endRgb >> 16) & 0xFF; + int g2 = (endRgb >> 8) & 0xFF; + int b2 = endRgb & 0xFF; + + int r = (int) (r1 + (r2 - r1) * ratio); + int g = (int) (g1 + (g2 - g1) * ratio); + int b = (int) (b1 + (b2 - b1) * ratio); + + return TextColor.fromRgb((r << 16) | (g << 8) | b); + } + + /** + * 样式上下文类,用于追踪嵌套样式 + */ + private static class StyleContext { + String tagName = null; // 记录标签名称,用于关闭匹配 + TextColor color = null; + Boolean shadow = null; + TextColor shadowColor = null; + Map decorations = new HashMap<>(); + boolean rainbow = false; + float rainbowSaturation = 1.0f; + float rainbowBrightness = 1.0f; + boolean gradient = false; + TextColor gradientStart = null; + TextColor gradientEnd = null; + boolean transition = false; + TextColor transitionStart = null; + TextColor transitionEnd = null; + String font = null; + + StyleContext copy() { + StyleContext copy = new StyleContext(); + copy.tagName = this.tagName; + copy.color = this.color; + copy.shadow = this.shadow; + copy.shadowColor = this.shadowColor; + copy.decorations.putAll(this.decorations); + copy.rainbow = this.rainbow; + copy.rainbowSaturation = this.rainbowSaturation; + copy.rainbowBrightness = this.rainbowBrightness; + copy.gradient = this.gradient; + copy.gradientStart = this.gradientStart; + copy.gradientEnd = this.gradientEnd; + copy.transition = this.transition; + copy.transitionStart = this.transitionStart; + copy.transitionEnd = this.transitionEnd; + copy.font = this.font; + return copy; + } + + /** + * 构建基础样式(不含颜色) + */ + Style buildBaseStyle() { + Style style = Style.EMPTY; + + // 应用装饰器 + for (Map.Entry entry : decorations.entrySet()) { + if (entry.getValue()) { + style = style.applyFormat(entry.getKey()); + } + } + + if (font != null) { + try { + style = style.withFont(new FontDescription.Resource(Identifier.parse(font))); + } catch (Exception e) { + // 忽略字体解析错误 + } + } + + return style; + } + + /** + * 构建完整样式(含颜色) + */ + Style buildStyle() { + Style style = buildBaseStyle(); + + // 只有在没有渐变、过渡和彩虹效果时才应用普通颜色 + if (!gradient && !transition && !rainbow && color != null) { + style = style.withColor(color); + } + + if (shadow != null && shadow) { + if (shadowColor != null) { + style = style.withShadowColor(shadowColor.getValue() | 0xFF000000); + } else { + style = style.withShadowColor(0xFF000000); + } + } + + return style; + } + } +} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java new file mode 100644 index 00000000..fca17cc4 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java @@ -0,0 +1,72 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.DynamicTexture; +import org.joml.Matrix3x2fStack; + +import java.io.ByteArrayInputStream; + +public class PicRender extends PictureFrameBuffer { + private final DynamicTexture sourceTexture; + private final DynamicTexture rotateTexture; + + public PicRender(int size) { + sourceTexture = new DynamicTexture("allmusic source", size, size, false); + rotateTexture = new DynamicTexture("allmusic rotate", size, size, false); + } + + @Override + public void update(byte[] source, byte[] rotate) { + try { + ByteArrayInputStream stream = new ByteArrayInputStream(source); + var image1 = NativeImage.read(stream); + sourceTexture.setPixels(image1); + sourceTexture.upload(); + + stream = new ByteArrayInputStream(rotate); + var image2 = NativeImage.read(stream); + rotateTexture.setPixels(image2); + rotateTexture.upload(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(size, size, x, y, dir); + + Matrix3x2fStack matrix = new Matrix3x2fStack(2); + + float a = (float) size / 2; + + if (rotate) { + matrix.translation(point.x + a, point.y + a); + matrix.pushMatrix().rotate((float) Math.toRadians(ang)); + } else { + matrix.translation(point.x + a, point.y + a); + } + + float x0 = -a; + float x1 = a; + float y0 = -a; + float y1 = a; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView(), rotate ? rotateTexture.getSampler() : sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java new file mode 100644 index 00000000..5ee542f5 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java @@ -0,0 +1,109 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.client.renderer.texture.TextureContents; +import net.minecraft.resources.Identifier; +import org.joml.Matrix3x2f; + +public class TexRender extends TextureRender { + private final SimpleTexture sourceTexture; + + public TexRender(String texture) { + super(texture); + Identifier location = Identifier.fromNamespaceAndPath(AllMusicClient.MODID, texture); + sourceTexture = new SimpleTexture(location); + + try { + TextureContents contents = sourceTexture.loadContents(Minecraft.getInstance().getResourceManager()); + + width = contents.image().getWidth(); + height = contents.image().getHeight(); + + sourceTexture.apply(contents); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void drawPic(float x, float y, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = (float) width / 2; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } + + @Override + public void drawPic(float x, float y, float width, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = ((float) this.width / 2) * width; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = width; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } + + @Override + public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(width, height, x, y, dir); + + float w1 = width / 2; + float h1 = height / 2; + + Matrix3x2f matrix = new Matrix3x2f().translation(point.x + w1, point.y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java new file mode 100644 index 00000000..7c1dcb83 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/GuiShow.java @@ -0,0 +1,18 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.AllMusicClient; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.gui.Hud; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Hud.class) +public class GuiShow { + @Inject(method = "extractRenderState", at = @At(value = "HEAD")) + public void guiShow(GuiGraphicsExtractor graphics, DeltaTracker deltaTracker, CallbackInfo ci) { + AllMusicClient.update(graphics); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java new file mode 100644 index 00000000..f77e15c4 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/QuitServer.java @@ -0,0 +1,16 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class QuitServer { + @Inject(method = "disconnect(Lnet/minecraft/client/gui/screens/Screen;ZZ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;resetData()V")) + public void quit(CallbackInfo info) { + AllMusicCore.onServerQuit(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java new file mode 100644 index 00000000..0d9d49ea --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Register.java @@ -0,0 +1,16 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class Register { + @Inject(method = "", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketProcessor;(Ljava/lang/Thread;)V")) + public void register(CallbackInfo info) { + AllMusicCore.renderInit(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java new file mode 100644 index 00000000..5bd2ce83 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/SoundEvent.java @@ -0,0 +1,38 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.resources.sounds.SoundInstance; +import net.minecraft.client.sounds.SoundEngine; +import net.minecraft.client.sounds.SoundManager; +import net.minecraft.sounds.SoundSource; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(SoundManager.class) +public class SoundEvent { + @Inject(method = "play", at = @At("HEAD"), cancellable = true) + public void play(SoundInstance sound, CallbackInfoReturnable cir) { + if (AllMusicCore.isPlay()) { + SoundSource data = sound.getSource(); + switch (data) { + case RECORDS, MUSIC: { + cir.cancel(); + cir.setReturnValue(SoundEngine.PlayResult.NOT_STARTED); + } + } + } + } + + @Inject(method = "playDelayed", at = @At("HEAD"), cancellable = true) + public void play(SoundInstance soundInstance, int delay, CallbackInfo info) { + if (AllMusicCore.isPlay()) { + SoundSource data = soundInstance.getSource(); + switch (data) { + case RECORDS, MUSIC -> info.cancel(); + } + } + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java new file mode 100644 index 00000000..ff2cc10c --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/mixins/Tick.java @@ -0,0 +1,16 @@ +package com.coloryr.allmusic.client.mixins; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Minecraft.class) +public class Tick { + @Inject(method = "tick", at = @At("HEAD")) + public void tick(CallbackInfo info) { + AllMusicCore.tick(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java new file mode 100644 index 00000000..62eb1259 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java @@ -0,0 +1,32 @@ +package com.coloryr.allmusic.client.others; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import de.maxhenkel.voicechat.api.VoicechatPlugin; +import de.maxhenkel.voicechat.api.events.ClientReceiveSoundEvent; +import de.maxhenkel.voicechat.api.events.EventRegistration; + +public class VoiceChat implements VoicechatPlugin { + @Override + public String getPluginId() { + return "allmusic"; + } + + @Override + public void registerEvents(EventRegistration registration) { + registration.registerEvent(ClientReceiveSoundEvent.EntitySound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.LocationalSound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.StaticSound.class, this::play); + } + + public void play(ClientReceiveSoundEvent.EntitySound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.LocationalSound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.StaticSound event) { + AllMusicCore.chat(); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..8d0e96d4 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.comm; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; + +public class AllMusicInit implements ModInitializer { + @Override + public void onInitialize() { + PayloadTypeRegistry.clientboundPlay().register(MusicCodec.ID, MusicCodec.CODEC); + } +} diff --git a/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..6b0cee70 --- /dev/null +++ b/client/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,23 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier ChannelID = Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type ID = new Type<>(ChannelID); + public static final StreamCodec CODEC = + StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + buf -> new MusicCodec(MusicPacketCodec.decode(buf))); + + @Override + public @NonNull Type type() { + return ID; + } +} diff --git a/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json b/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json new file mode 100644 index 00000000..2d2425a6 --- /dev/null +++ b/client/fabric_26_3/src/main/resources/allmusic_client.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "package": "com.coloryr.allmusic.client.mixins", + "compatibilityLevel": "JAVA_25", + "client": [ + "GuiShow", + "QuitServer", + "Register", + "SoundEvent", + "Tick" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +} \ No newline at end of file diff --git a/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png b/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/client/fabric_26_3/src/main/resources/fabric.mod.json b/client/fabric_26_3/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..06b7c0a8 --- /dev/null +++ b/client/fabric_26_3/src/main/resources/fabric.mod.json @@ -0,0 +1,42 @@ +{ + "schemaVersion": 1, + "id": "allmusic_client", + "version": "${version}", + "name": "AllMusic Client", + "description": "AllMusic Client Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic_client/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit" + ], + "client": [ + "com.coloryr.allmusic.client.AllMusicClient" + ], + "voicechat": [ + "com.coloryr.allmusic.client.others.VoiceChat" + ] + }, + "mixins": [ + { + "config": "allmusic_client.mixins.json", + "environment": "client" + } + ], + "depends": { + "fabricloader": ">=0.19.5", + "minecraft": ">=26.3", + "fabric-api": "*" + }, + "suggests": { + "voicechat_api": "*" + } +} \ No newline at end of file diff --git a/client/neoforge_26_3/build.gradle.kts b/client/neoforge_26_3/build.gradle.kts new file mode 100644 index 00000000..aa89b82c --- /dev/null +++ b/client/neoforge_26_3/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + neoForge("net.neoforged:neoforge:26.3.0.6-beta") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") +} + +tasks { + processResources { + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + shadowJar { + archiveFileName.set("[neoforge-26.3]AllMusic_Client-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/client/neoforge_26_3/gradle.properties b/client/neoforge_26_3/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/client/neoforge_26_3/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java new file mode 100644 index 00000000..e8f1067d --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -0,0 +1,193 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicBridge; +import com.coloryr.allmusic.client.core.AllMusicCore; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.comm.AllMusicInit; +import com.coloryr.allmusic.comm.MusicCodec; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.Connection; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.Identifier; +import net.minecraft.server.packs.resources.Resource; +import net.minecraft.sounds.SoundSource; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.ModList; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; +import net.neoforged.neoforge.client.event.ClientTickEvent; +import net.neoforged.neoforge.client.event.RenderGuiLayerEvent; +import net.neoforged.neoforge.client.event.sound.PlaySoundSourceEvent; +import net.neoforged.neoforge.client.event.sound.PlayStreamingSourceEvent; +import net.neoforged.neoforge.client.event.sound.SoundEngineLoadEvent; +import net.neoforged.neoforge.client.gui.VanillaGuiLayers; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +@EventBusSubscriber(modid = AllMusicClient.MODID, value = Dist.CLIENT) +public class AllMusicClient implements IPayloadHandler, AllMusicBridge { + public static final String MODID = "allmusic_client"; + public static boolean modui; + public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Client"); + public static GuiGraphicsExtractor context; + + @SubscribeEvent + public static void setup(final FMLClientSetupEvent event) { + AllMusicClient client = new AllMusicClient(); + AllMusicInit.handler = client; + AllMusicCore.init(FMLPaths.CONFIGDIR.get(), client); + + Minecraft.getInstance().execute(AllMusicCore::renderInit); + + if (ModList.get().isLoaded("modernui")) { + modui = true; + } + } + + @SubscribeEvent + public static void onSound(final PlaySoundSourceEvent e) { + if (!AllMusicCore.isPlay()) return; + SoundSource data = e.getSound().getSource(); + switch (data) { + case MUSIC, RECORDS -> e.getChannel().stop(); + } + } + + @SubscribeEvent + public static void onSound(final PlayStreamingSourceEvent e) { + if (!AllMusicCore.isPlay()) return; + SoundSource data = e.getSound().getSource(); + switch (data) { + case MUSIC, RECORDS -> e.getChannel().stop(); + } + } + + @SubscribeEvent + public static void onServerQuit(final ClientPlayerNetworkEvent.LoggingOut e) { + AllMusicCore.onServerQuit(); + } + + @SubscribeEvent + public static void onRenderOverlay(RenderGuiLayerEvent.Post e) { + if (e.getName().equals(VanillaGuiLayers.CAMERA_OVERLAYS)) { + context = e.getGuiGraphics(); + AllMusicCore.hudUpdate(); + } + } + + @SubscribeEvent + public static void onTick(ClientTickEvent.Post event) { + AllMusicCore.tick(); + } + + public void sendMessage(String data) { + data = "[AllMusic Client]" + data; + LOGGER.warn(data); + String finalData = data; + Minecraft.getInstance().execute(() -> { + if (Minecraft.getInstance().player == null) + return; + Minecraft.getInstance().player.sendSystemMessage(Component.literal(finalData)); + }); + } + + @Override + public void handle(MusicCodec pack, IPayloadContext iPayloadContext) { + try { + AllMusicCore.packDo(pack.pack()); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void stopPlayMusic() { + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.MUSIC); + Minecraft.getInstance().getSoundManager().stop(null, SoundSource.RECORDS); + } + + public int getScreenWidth() { + return Minecraft.getInstance().getWindow().getGuiScaledWidth(); + } + + public int getScreenHeight() { + return Minecraft.getInstance().getWindow().getGuiScaledHeight(); + } + + public int getTextWidth(String item) { + return Minecraft.getInstance().font.width(item); + } + + public int getFontHeight() { + return Minecraft.getInstance().font.lineHeight; + } + + public float getVolume() { + return Minecraft.getInstance().options.getFinalSoundSourceVolume(SoundSource.RECORDS); + } + + @Override + public TextFrameBuffer makeTextRender(String name) { + return new CoreRenderTarget(name); + } + + @Override + public TextureRender makeTextureRender(String file) { + return new TexRender(file); + } + + @Override + public PictureFrameBuffer makePictureRender(int size) { + return new PicRender(size); + } + + @Override + public String readText(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + try (InputStream inputStream = resource.open()) { + byte[] bytes = inputStream.readAllBytes(); + return new String(bytes, StandardCharsets.UTF_8); + } + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public InputStream readFile(String file) { + try { + var man = Minecraft.getInstance().getResourceManager(); + Resource resource = man.getResource(Identifier.fromNamespaceAndPath(MODID, file)).orElseThrow(); + return resource.open(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + @Override + public void kick() { + Minecraft client = Minecraft.getInstance(); + + ClientPacketListener packetListener = client.getConnection(); + if (packetListener != null) { + Connection connection = packetListener.getConnection(); + connection.disconnect(Component.nullToEmpty("Old AllMusic server")); + } + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java new file mode 100644 index 00000000..fe90072e --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -0,0 +1,133 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.network.chat.Component; + +public class CoreRenderTarget extends TextFrameBuffer { + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); + } + + @Override + public void putText(String text, int y, int color, boolean shadow) { + color = (color & 0x00FFFFFF) | 0xFF000000; + Component component = MiniMessage.parse(text); + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { + return; + } + + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; + } + texts.add(new TextItem<>(width, height, y, component, shadow, color)); + } + + @Override + public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) getOffset(entry, maxWidth); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } + } + } + + @Override + public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } + if (line >= texts.size()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextItem entry = texts.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextItem entry : texts) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } + } + + @Override + public Point2f getLine(int line) { + if (line >= texts.size()) { + return new Point2f(0, 0); + } + TextItem entry = texts.get(line); + return new Point2f(entry.width, entry.height); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); + } +} \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java new file mode 100644 index 00000000..d143b08b --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/FloatRenderState.java @@ -0,0 +1,30 @@ +package com.coloryr.allmusic.client; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import com.mojang.renderpearl.api.pipeline.RenderPipeline; +import net.minecraft.client.gui.navigation.ScreenRectangle; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.state.gui.GuiElementRenderState; +import org.joml.Matrix3x2f; + +public record FloatRenderState(RenderPipeline pipeline, TextureSetup textureSetup, Matrix3x2f pose, float x0, + float y0, float x1, float y1, float u0, float u1, float v0, float v1, int color, + ScreenRectangle scissorArea, + ScreenRectangle bounds) implements GuiElementRenderState { + public FloatRenderState(RenderPipeline renderPipeline, TextureSetup textureSetup, Matrix3x2f matrix3x2f, + float i, float j, float k, float l, float f, float g, float h, float m, int n, ScreenRectangle screenRectangle) { + this(renderPipeline, textureSetup, matrix3x2f, i, j, k, l, f, g, h, m, n, screenRectangle, getBounds(i, j, k, l, matrix3x2f, screenRectangle)); + } + + private static ScreenRectangle getBounds(float i, float j, float k, float l, Matrix3x2f matrix3x2f, ScreenRectangle screenRectangle) { + ScreenRectangle screenRectangle2 = (new ScreenRectangle((int) Math.ceil(i + 1), (int) Math.ceil(j + 1), (int) Math.ceil(k - i + 1), (int) Math.ceil(l - j + 1))).transformMaxBounds(matrix3x2f); + return screenRectangle != null ? screenRectangle.intersection(screenRectangle2) : screenRectangle2; + } + + public void buildVertices(VertexConsumer vertexConsumer) { + vertexConsumer.addVertexWith2DPose(pose(), x0(), y0()).setUv(u0(), v0()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x0(), y1()).setUv(u0(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y1()).setUv(u1(), v1()).setColor(color()); + vertexConsumer.addVertexWith2DPose(pose(), x1(), y0()).setUv(u1(), v0()).setColor(color()); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java new file mode 100644 index 00000000..88129399 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/MiniMessage.java @@ -0,0 +1,645 @@ +package com.coloryr.allmusic.client; + +import net.minecraft.ChatFormatting; +import net.minecraft.network.chat.*; +import net.minecraft.resources.Identifier; + +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MiniMessage { + // 正则表达式匹配标签 + private static final Pattern TAG_PATTERN = Pattern.compile( + "<(/?)(\\w+)(?:\\(([^)]+)\\))?(?::([^>]+))?>" + ); + + // 颜色映射表 + private static final Map COLOR_MAP = new HashMap<>(); + // 装饰器映射 + private static final Map DECORATION_MAP = new HashMap<>(); + + static { + COLOR_MAP.put("black", ChatFormatting.BLACK); + COLOR_MAP.put("dark_blue", ChatFormatting.DARK_BLUE); + COLOR_MAP.put("dark_green", ChatFormatting.DARK_GREEN); + COLOR_MAP.put("dark_aqua", ChatFormatting.DARK_AQUA); + COLOR_MAP.put("dark_red", ChatFormatting.DARK_RED); + COLOR_MAP.put("dark_purple", ChatFormatting.DARK_PURPLE); + COLOR_MAP.put("gold", ChatFormatting.GOLD); + COLOR_MAP.put("gray", ChatFormatting.GRAY); + COLOR_MAP.put("dark_gray", ChatFormatting.DARK_GRAY); + COLOR_MAP.put("blue", ChatFormatting.BLUE); + COLOR_MAP.put("green", ChatFormatting.GREEN); + COLOR_MAP.put("aqua", ChatFormatting.AQUA); + COLOR_MAP.put("red", ChatFormatting.RED); + COLOR_MAP.put("light_purple", ChatFormatting.LIGHT_PURPLE); + COLOR_MAP.put("yellow", ChatFormatting.YELLOW); + COLOR_MAP.put("white", ChatFormatting.WHITE); + } + + static { + DECORATION_MAP.put("bold", ChatFormatting.BOLD); + DECORATION_MAP.put("italic", ChatFormatting.ITALIC); + DECORATION_MAP.put("underlined", ChatFormatting.UNDERLINE); + DECORATION_MAP.put("strikethrough", ChatFormatting.STRIKETHROUGH); + DECORATION_MAP.put("obfuscated", ChatFormatting.OBFUSCATED); + } + + /** + * 解析 MiniMessage 字符串为 Component + */ + public static Component parse(String input) { + MutableComponent root = Component.literal(""); + Stack styleStack = new Stack<>(); + StyleContext currentStyle = new StyleContext(); + styleStack.push(currentStyle); + + StringBuilder textBuffer = new StringBuilder(); + int pos = 0; + int length = input.length(); + + while (pos < length) { + char c = input.charAt(pos); + + if (c == '<') { + // 先处理已积累的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + textBuffer.setLength(0); + } + + // 查找标签结束位置 + int endTag = findClosingTag(input, pos); + if (endTag == -1) { + // 没有找到闭合标签,当作普通文本处理 + textBuffer.append(c); + pos++; + continue; + } + + String tagContent = input.substring(pos, endTag + 1); + Matcher matcher = TAG_PATTERN.matcher(tagContent); + + if (matcher.matches()) { + boolean isClosing = !matcher.group(1).isEmpty(); + String tagName = matcher.group(2).toLowerCase(Locale.ROOT); + String params = matcher.group(3); + String value = matcher.group(4); + + if (isClosing) { + // 关闭标签 - 只关闭对应的开始标签 + String expectedOpenTag = matcher.group(2).toLowerCase(Locale.ROOT); + + // 查找对应的开始标签并关闭 + if (styleStack.size() > 1) { + // 从栈顶向下查找匹配的开始标签 + int index = styleStack.size() - 1; + boolean found = false; + + while (index >= 0) { + StyleContext ctx = styleStack.get(index); + if (ctx.tagName != null && ctx.tagName.equals(expectedOpenTag)) { + // 找到匹配的开始标签,关闭到这一层 + while (styleStack.size() > index + 1) { + styleStack.pop(); + } + currentStyle = styleStack.peek(); + found = true; + break; + } + index--; + } + + if (!found) { + // 没找到匹配的标签,忽略此关闭标签 + } + } + } else { + // 处理各种标签 + StyleContext newStyle = currentStyle.copy(); + + switch (tagName) { + case "color": + handleColorTag(newStyle, params, value); + break; + case "shadow": + handleShadowTag(newStyle, params, value); + break; + case "decoration": + handleDecorationTag(newStyle, params, value); + break; + case "reset": + newStyle = new StyleContext(); + break; + case "rainbow": + handleRainbowTag(newStyle, params, value); + break; + case "gradient": + handleGradientTag(newStyle, params, value); + break; + case "transition": + handleTransitionTag(newStyle, params, value); + break; + case "font": + handleFontTag(newStyle, params, value); + break; + default: + // 未知标签,忽略 + break; + } + + // 记录标签名称以便关闭时匹配 + newStyle.tagName = tagName; + styleStack.push(newStyle); + currentStyle = newStyle; + } + } else { + // 无效标签,当作普通文本 + textBuffer.append(tagContent); + } + + pos = endTag + 1; + } else { + textBuffer.append(c); + pos++; + } + } + + // 处理剩余的文本 + if (textBuffer.length() > 0) { + appendText(root, textBuffer.toString(), currentStyle); + } + + return root; + } + + /** + * 处理颜色标签 + * 支持格式: , , + */ + private static void handleColorTag(StyleContext style, String params, String value) { + if (value != null) { + // 格式: 或 + TextColor textColor = parseColor(value); + if (textColor != null) { + style.color = textColor; + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } + } else if (params != null) { + // 格式: + if (params.equalsIgnoreCase("rgb")) { + String[] parts = value.split(","); + if (parts.length == 3) { + try { + int r = Integer.parseInt(parts[0].trim()); + int g = Integer.parseInt(parts[1].trim()); + int b = Integer.parseInt(parts[2].trim()); + style.color = TextColor.fromRgb((r << 16) | (g << 8) | b); + // 清除渐变、过渡和彩虹效果 + style.gradient = false; + style.transition = false; + style.rainbow = false; + } catch (NumberFormatException e) { + // 忽略解析错误 + } + } + } + } + } + + /** + * 处理阴影标签 + * 支持格式: , , , + */ + private static void handleShadowTag(StyleContext style, String params, String value) { + if (value == null) { + style.shadow = true; + } else if (value.equalsIgnoreCase("true")) { + style.shadow = true; + } else if (value.equalsIgnoreCase("false")) { + style.shadow = false; + } else { + // 自定义阴影颜色 + TextColor shadowColor = parseColor(value); + if (shadowColor != null) { + style.shadow = true; + style.shadowColor = shadowColor; + } + } + } + + /** + * 处理装饰标签 + * 支持格式: , , + */ + private static void handleDecorationTag(StyleContext style, String params, String value) { + if (params == null) return; + + String[] parts = params.split(":"); + String decorationName = parts[0].toLowerCase(Locale.ROOT); + boolean enabled = true; + + if (parts.length > 1) { + enabled = Boolean.parseBoolean(parts[1]); + } else if (value != null) { + enabled = Boolean.parseBoolean(value); + } + + ChatFormatting formatting = DECORATION_MAP.get(decorationName); + if (formatting != null) { + style.decorations.put(formatting, enabled); + } + } + + /** + * 处理彩虹色标签 + * 支持格式: , , , + */ + private static void handleRainbowTag(StyleContext style, String params, String value) { + float saturation = 1.0f; + float brightness = 1.0f; + + if (params != null) { + if (params.equalsIgnoreCase("saturation") && value != null) { + try { + saturation = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } else if (params.equalsIgnoreCase("brightness") && value != null) { + try { + brightness = Float.parseFloat(value); + } catch (NumberFormatException e) { + } + } + } + + style.rainbow = true; + style.gradient = false; + style.transition = false; + style.rainbowSaturation = saturation; + style.rainbowBrightness = brightness; + } + + /** + * 处理渐变色标签 + * 支持格式: , + * 渐变是逐字符过渡 + */ + private static void handleGradientTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = start; + style.gradientEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.gradient = true; + style.transition = false; + style.rainbow = false; + style.gradientStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.gradientEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理过渡色标签 + * 支持格式: , + * 过渡是整个文本块的颜色过渡(类似整体渐变效果) + */ + private static void handleTransitionTag(StyleContext style, String params, String value) { + if (value == null) return; + + String[] colors = value.split(":"); + if (colors.length == 2) { + TextColor start = parseColor(colors[0]); + TextColor end = parseColor(colors[1]); + + if (start != null && end != null) { + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = start; + style.transitionEnd = end; + } + } else if (params != null && params.equalsIgnoreCase("rgb")) { + // 处理 RGB 格式 + String[] color1 = colors[0].split(","); + String[] color2 = colors[1].split(","); + if (color1.length == 3 && color2.length == 3) { + try { + int r1 = Integer.parseInt(color1[0].trim()); + int g1 = Integer.parseInt(color1[1].trim()); + int b1 = Integer.parseInt(color1[2].trim()); + int r2 = Integer.parseInt(color2[0].trim()); + int g2 = Integer.parseInt(color2[1].trim()); + int b2 = Integer.parseInt(color2[2].trim()); + + style.transition = true; + style.gradient = false; + style.rainbow = false; + style.transitionStart = TextColor.fromRgb((r1 << 16) | (g1 << 8) | b1); + style.transitionEnd = TextColor.fromRgb((r2 << 16) | (g2 << 8) | b2); + } catch (NumberFormatException e) { + } + } + } + } + + /** + * 处理字体标签 + * 支持格式: , + */ + private static void handleFontTag(StyleContext style, String params, String value) { + String fontName = value != null ? value : params; + if (fontName != null) { + style.font = fontName; + } + } + + /** + * 解析颜色字符串 + * 支持: 颜色名称, #RRGGBB, #RGB, 十六进制数值 + */ + private static TextColor parseColor(String colorStr) { + if (colorStr == null) return null; + + // 检查是否为预定义颜色名称 + ChatFormatting formatting = COLOR_MAP.get(colorStr.toLowerCase(Locale.ROOT)); + if (formatting != null) { + return TextColor.fromLegacyFormat(formatting); + } + + // 检查十六进制格式 + if (colorStr.startsWith("#")) { + try { + if (colorStr.length() == 7) { + return TextColor.parseColor(colorStr).getOrThrow(); + } else if (colorStr.length() == 4) { + // 扩展 #RGB 到 #RRGGBB + String expanded = "#" + + colorStr.charAt(1) + colorStr.charAt(1) + + colorStr.charAt(2) + colorStr.charAt(2) + + colorStr.charAt(3) + colorStr.charAt(3); + return TextColor.parseColor(expanded).getOrThrow(); + } + } catch (Exception e) { + // 忽略解析错误 + } + } + + return null; + } + + /** + * 查找标签闭合位置 + */ + private static int findClosingTag(String input, int start) { + int pos = start + 1; + while (pos < input.length()) { + char c = input.charAt(pos); + if (c == '>') { + return pos; + } + pos++; + } + return -1; + } + + /** + * 添加文本到组件 + */ + private static void appendText(MutableComponent root, String text, StyleContext styleContext) { + if (text.isEmpty()) return; + + // 如果有渐变效果,需要逐字符处理 + if (styleContext.gradient && styleContext.gradientStart != null && styleContext.gradientEnd != null) { + appendGradientText(root, text, styleContext); + } + // 如果有过渡效果,需要逐字符处理 + else if (styleContext.transition && styleContext.transitionStart != null && styleContext.transitionEnd != null) { + appendTransitionText(root, text, styleContext); + } + // 如果有彩虹效果,需要逐字符处理 + else if (styleContext.rainbow) { + appendRainbowText(root, text, styleContext); + } + // 普通文本 + else { + MutableComponent component = Component.literal(text); + component.setStyle(styleContext.buildStyle()); + root.append(component); + } + } + + /** + * 带渐变效果的文本追加(逐字符颜色变化) + */ + private static void appendGradientText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float ratio = (float) i / (length - 1); + TextColor gradientColor = interpolateColor(styleContext.gradientStart, styleContext.gradientEnd, ratio); + + Style charStyle = baseStyle.withColor(gradientColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 带过渡效果的文本追加(整体颜色过渡) + */ + private static void appendTransitionText(MutableComponent root, String text, StyleContext styleContext) { + Style baseStyle = styleContext.buildBaseStyle(); + + // 过渡效果:为整个文本块创建一个颜色过渡 + MutableComponent transitionComponent = Component.literal(text); + + // 应用过渡样式(这里使用颜色插值,但也可以根据需求调整) + if (styleContext.transitionStart != null && styleContext.transitionEnd != null) { + // 可以使用整体渐变或者固定起始色 + transitionComponent.setStyle(baseStyle.withColor(styleContext.transitionStart)); + } else { + transitionComponent.setStyle(baseStyle); + } + + root.append(transitionComponent); + } + + /** + * 带彩虹效果的文本追加 + */ + private static void appendRainbowText(MutableComponent root, String text, StyleContext styleContext) { + int length = text.length(); + Style baseStyle = styleContext.buildBaseStyle(); + + for (int i = 0; i < length; i++) { + char c = text.charAt(i); + float hue = (float) i / length; + int rgb = java.awt.Color.HSBtoRGB(hue, styleContext.rainbowSaturation, styleContext.rainbowBrightness); + TextColor rainbowColor = TextColor.fromRgb(rgb & 0xFFFFFF); + + Style charStyle = baseStyle.withColor(rainbowColor); + // 重新应用阴影 + if (styleContext.shadow != null && styleContext.shadow) { + if (styleContext.shadowColor != null) { + charStyle = charStyle.withShadowColor(styleContext.shadowColor.getValue() | 0xFF000000); + } else { + charStyle = charStyle.withShadowColor(0xFF000000); + } + } + + root.append(Component.literal(String.valueOf(c)).withStyle(charStyle)); + } + } + + /** + * 颜色插值 + */ + private static TextColor interpolateColor(TextColor start, TextColor end, float ratio) { + int startRgb = start.getValue(); + int endRgb = end.getValue(); + + int r1 = (startRgb >> 16) & 0xFF; + int g1 = (startRgb >> 8) & 0xFF; + int b1 = startRgb & 0xFF; + + int r2 = (endRgb >> 16) & 0xFF; + int g2 = (endRgb >> 8) & 0xFF; + int b2 = endRgb & 0xFF; + + int r = (int) (r1 + (r2 - r1) * ratio); + int g = (int) (g1 + (g2 - g1) * ratio); + int b = (int) (b1 + (b2 - b1) * ratio); + + return TextColor.fromRgb((r << 16) | (g << 8) | b); + } + + /** + * 样式上下文类,用于追踪嵌套样式 + */ + private static class StyleContext { + String tagName = null; // 记录标签名称,用于关闭匹配 + TextColor color = null; + Boolean shadow = null; + TextColor shadowColor = null; + Map decorations = new HashMap<>(); + boolean rainbow = false; + float rainbowSaturation = 1.0f; + float rainbowBrightness = 1.0f; + boolean gradient = false; + TextColor gradientStart = null; + TextColor gradientEnd = null; + boolean transition = false; + TextColor transitionStart = null; + TextColor transitionEnd = null; + String font = null; + + StyleContext copy() { + StyleContext copy = new StyleContext(); + copy.tagName = this.tagName; + copy.color = this.color; + copy.shadow = this.shadow; + copy.shadowColor = this.shadowColor; + copy.decorations.putAll(this.decorations); + copy.rainbow = this.rainbow; + copy.rainbowSaturation = this.rainbowSaturation; + copy.rainbowBrightness = this.rainbowBrightness; + copy.gradient = this.gradient; + copy.gradientStart = this.gradientStart; + copy.gradientEnd = this.gradientEnd; + copy.transition = this.transition; + copy.transitionStart = this.transitionStart; + copy.transitionEnd = this.transitionEnd; + copy.font = this.font; + return copy; + } + + /** + * 构建基础样式(不含颜色) + */ + Style buildBaseStyle() { + Style style = Style.EMPTY; + + // 应用装饰器 + for (Map.Entry entry : decorations.entrySet()) { + if (entry.getValue()) { + style = style.applyFormat(entry.getKey()); + } + } + + if (font != null) { + try { + style = style.withFont(new FontDescription.Resource(Identifier.parse(font))); + } catch (Exception e) { + // 忽略字体解析错误 + } + } + + return style; + } + + /** + * 构建完整样式(含颜色) + */ + Style buildStyle() { + Style style = buildBaseStyle(); + + // 只有在没有渐变、过渡和彩虹效果时才应用普通颜色 + if (!gradient && !transition && !rainbow && color != null) { + style = style.withColor(color); + } + + if (shadow != null && shadow) { + if (shadowColor != null) { + style = style.withShadowColor(shadowColor.getValue() | 0xFF000000); + } else { + style = style.withShadowColor(0xFF000000); + } + } + + return style; + } + } +} \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java new file mode 100644 index 00000000..51fa9e23 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/PicRender.java @@ -0,0 +1,71 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; +import com.coloryr.allmusic.codec.HudPosType; +import com.mojang.blaze3d.platform.NativeImage; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.DynamicTexture; +import org.joml.Matrix3x2fStack; + +import java.io.ByteArrayInputStream; + +public class PicRender extends PictureFrameBuffer { + private final DynamicTexture sourceTexture; + private final DynamicTexture rotateTexture; + + public PicRender(int size) { + sourceTexture = new DynamicTexture("allmusic source", size, size, false); + rotateTexture = new DynamicTexture("allmusic rotate", size, size, false); + } + + @Override + public void update(byte[] source, byte[] rotate) { + try { + ByteArrayInputStream stream = new ByteArrayInputStream(source); + var image1 = NativeImage.read(stream); + sourceTexture.setPixels(image1); + sourceTexture.upload(); + + stream = new ByteArrayInputStream(rotate); + var image2 = NativeImage.read(stream); + rotateTexture.setPixels(image2); + rotateTexture.upload(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(size, size, x, y, dir); + + Matrix3x2fStack matrix = new Matrix3x2fStack(2); + + float a = (float) size / 2; + + if (rotate) { + matrix.translation(point.x + a, point.y + a); + matrix.pushMatrix().rotate((float) Math.toRadians(ang)); + } else { + matrix.translation(point.x + a, point.y + a); + } + + float x0 = -a; + float x1 = a; + float y0 = -a; + float y1 = a; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView(), rotate ? rotateTexture.getSampler() : sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java new file mode 100644 index 00000000..90c89199 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/TexRender.java @@ -0,0 +1,109 @@ +package com.coloryr.allmusic.client; + +import com.coloryr.allmusic.client.core.AllMusicHud; +import com.coloryr.allmusic.client.core.Point2f; +import com.coloryr.allmusic.client.core.render.TextureRender; +import com.coloryr.allmusic.codec.HudPosType; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.render.TextureSetup; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.texture.SimpleTexture; +import net.minecraft.client.renderer.texture.TextureContents; +import net.minecraft.resources.Identifier; +import org.joml.Matrix3x2f; + +public class TexRender extends TextureRender { + private final SimpleTexture sourceTexture; + + public TexRender(String texture) { + super(texture); + Identifier location = Identifier.fromNamespaceAndPath(AllMusicClient.MODID, texture); + sourceTexture = new SimpleTexture(location); + + try { + TextureContents contents = sourceTexture.loadContents(Minecraft.getInstance().getResourceManager()); + + width = contents.image().getWidth(); + height = contents.image().getHeight(); + + sourceTexture.apply(contents); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void drawPic(float x, float y, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = (float) width / 2; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } + + @Override + public void drawPic(float x, float y, float width, float alpha) { + Matrix3x2f matrix = new Matrix3x2f(); + + float w1 = ((float) this.width / 2) * width; + float h1 = (float) height / 2; + + matrix.translation(x + w1, y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = width; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } + + @Override + public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { + Point2f point = AllMusicHud.getPos(width, height, x, y, dir); + + float w1 = width / 2; + float h1 = height / 2; + + Matrix3x2f matrix = new Matrix3x2f().translation(point.x + w1, point.y + h1); + + float x0 = -w1; + float x1 = w1; + float y0 = -h1; + float y1 = h1; + float u0 = 0; + float u1 = 1; + float v0 = 0; + float v1 = 1; + + int color = 0xFFFFFF00 + (int) (255 * alpha); + + AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, + TextureSetup.singleTexture(sourceTexture.getTextureView(), sourceTexture.getSampler()), + matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java new file mode 100644 index 00000000..fab46779 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/ModernUI.java @@ -0,0 +1,15 @@ +//package com.coloryr.allmusic.client.others; +// +//import icyllis.modernui.mc.text.ModernPreparedText; +//import net.minecraft.client.gui.Font; +//import net.minecraft.client.gui.navigation.ScreenRectangle; +//import net.minecraft.client.renderer.state.gui.GuiRenderState; +//import org.joml.Matrix3x2fc; +// +//public class ModernUI { +// public static void text(Font.PreparedText preparedText, GuiRenderState state, Matrix3x2fc pose, ScreenRectangle scissor) { +// if (preparedText instanceof ModernPreparedText) { +// ((ModernPreparedText) preparedText).submitRuns(state, pose, scissor); +// } +// } +//} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java new file mode 100644 index 00000000..9caead22 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/client/others/VoiceChat.java @@ -0,0 +1,34 @@ +package com.coloryr.allmusic.client.others; + +import com.coloryr.allmusic.client.core.AllMusicCore; +import de.maxhenkel.voicechat.api.ForgeVoicechatPlugin; +import de.maxhenkel.voicechat.api.VoicechatPlugin; +import de.maxhenkel.voicechat.api.events.ClientReceiveSoundEvent; +import de.maxhenkel.voicechat.api.events.EventRegistration; + +@ForgeVoicechatPlugin +public class VoiceChat implements VoicechatPlugin { + @Override + public String getPluginId() { + return "allmusic"; + } + + @Override + public void registerEvents(EventRegistration registration) { + registration.registerEvent(ClientReceiveSoundEvent.EntitySound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.LocationalSound.class, this::play); + registration.registerEvent(ClientReceiveSoundEvent.StaticSound.class, this::play); + } + + public void play(ClientReceiveSoundEvent.EntitySound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.LocationalSound event) { + AllMusicCore.chat(); + } + + public void play(ClientReceiveSoundEvent.StaticSound event) { + AllMusicCore.chat(); + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..d9282960 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.comm; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +@Mod(AllMusicInit.MODID) +public class AllMusicInit implements IPayloadHandler { + + public static final String MODID = "allmusic"; + + public static IPayloadHandler handler; + + public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { + modEventBus.addListener(this::register); + } + + public void register(final RegisterPayloadHandlersEvent event) { + event.registrar("1.0") + .optional() + .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); + } + + @Override + public void handle(MusicCodec data, IPayloadContext iPayloadContext) { + if (handler != null) { + handler.handle(data, iPayloadContext); + } + } +} diff --git a/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..434fa510 --- /dev/null +++ b/client/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier channel = + Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type TYPE = new Type<>(channel); + public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + (buf) -> { + return new MusicCodec(MusicPacketCodec.decode(buf)); + }); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..544e45e7 --- /dev/null +++ b/client/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,42 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_client" +version = "${version}" +displayName = "AllMusic Client" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Client Mod''' + +[[dependencies.allmusic_client]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId="voicechat_api" +type="optional" +ordering="AFTER" +side="CLIENT" \ No newline at end of file diff --git a/client/neoforge_26_3/src/main/resources/icon.png b/client/neoforge_26_3/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/onejar/fabric_26_3/build.gradle.kts b/onejar/fabric_26_3/build.gradle.kts new file mode 100644 index 00000000..c109d33a --- /dev/null +++ b/onejar/fabric_26_3/build.gradle.kts @@ -0,0 +1,68 @@ +plugins { + id("net.fabricmc.fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:fabric_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:fabric_26_3").projectDir.resolve("src/main/resources") +val serverResDir = project(":server:fabric_26_3").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + serverResDir + )) + } + } +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + implementation("net.fabricmc:fabric-loader:0.19.5") + + implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") + + implementation(include("net.kyori:adventure-platform-fabric:7.2.0")!!) +} + +tasks { + processResources { + // onejar 自己的 fabric.mod.json 优先(排在 srcDirs 第一位), + // client/server 的 fabric.mod.json 作为重复项被排除 + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + + shadowJar { + archiveFileName.set("[fabric-26.3]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + +// relocate("net.kyori", "com.coloryr.allmusic.libs.net.kyori") +// relocate("com.google.gson", "com.coloryr.allmusic.libs.com.google.gson") + } + + build { + dependsOn(shadowJar) + } +} diff --git a/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png b/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/onejar/fabric_26_3/src/main/resources/fabric.mod.json b/onejar/fabric_26_3/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..6f009cfc --- /dev/null +++ b/onejar/fabric_26_3/src/main/resources/fabric.mod.json @@ -0,0 +1,49 @@ +{ + "schemaVersion": 1, + "id": "allmusic", + "version": "${version}", + "name": "AllMusic", + "description": "AllMusic Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit", + "com.coloryr.allmusic.server.AllMusicServer" + ], + "client": [ + "com.coloryr.allmusic.client.AllMusicClient" + ], + "voicechat": [ + "com.coloryr.allmusic.client.others.VoiceChat" + ] + }, + "mixins": [ + { + "config": "allmusic_client.mixins.json", + "environment": "client" + }, + { + "config": "allmusic_server.mixins.json", + "environment": "server" + } + ], + "depends": { + "fabricloader": ">=0.19.3", + "fabric-api": "*", + "minecraft": ">=26.2", + "adventure-platform-fabric": ">=7.0.0" + }, + "suggests": { + "modernui": "*", + "voicechat_api": "*" + } +} diff --git a/onejar/neoforge_26_3/build.gradle.kts b/onejar/neoforge_26_3/build.gradle.kts new file mode 100644 index 00000000..ef978c25 --- /dev/null +++ b/onejar/neoforge_26_3/build.gradle.kts @@ -0,0 +1,75 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/client") +val serverSourceDir = project(":server:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/server") +val commSourceDir = project(":server:neoforge_26_3").projectDir.resolve("src/main/java/com/coloryr/allmusic/comm") +val clientResDir = project(":client:neoforge_26_3").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir, commSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + )) + } + } +} + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + neoForge("net.neoforged:neoforge:26.3.0.6-beta") + + compileOnly("de.maxhenkel.voicechat:voicechat-api:2.6.0") + + implementation(include("net.kyori:adventure-platform-neoforge:7.2.0")!!) +} + +tasks { + processResources { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + + shadowJar { + archiveFileName.set("[neoforge-26.3]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/onejar/neoforge_26_3/gradle.properties b/onejar/neoforge_26_3/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/onejar/neoforge_26_3/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..e2e8e100 --- /dev/null +++ b/onejar/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,65 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues/" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_client" +version = "${version}" +displayName = "AllMusic Client" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Client Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_client]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "CLIENT" + +[[dependencies.allmusic_client]] +modId="voicechat_api" +type="optional" +ordering="AFTER" +side="CLIENT" + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/onejar/neoforge_26_3/src/main/resources/icon.png b/onejar/neoforge_26_3/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/server/fabric_26_3/build.gradle.kts b/server/fabric_26_3/build.gradle.kts new file mode 100644 index 00000000..eca95f96 --- /dev/null +++ b/server/fabric_26_3/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + id("net.fabricmc.fabric-loom") version Versions.fabricLoom +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +dependencies { + minecraft("com.mojang:minecraft:26.3") + implementation("net.fabricmc:fabric-loader:0.19.5") + + implementation("net.fabricmc.fabric-api:fabric-api:0.161.0+26.3") + + implementation(include("net.kyori:adventure-platform-fabric:7.2.0")!!) +} + +tasks { + processResources { + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + loom.nestJars(named("shadowJar", Jar::class), configurations.getByName("include")) + + shadowJar { + archiveFileName.set("[fabric-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..8d0e96d4 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.comm; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; + +public class AllMusicInit implements ModInitializer { + @Override + public void onInitialize() { + PayloadTypeRegistry.clientboundPlay().register(MusicCodec.ID, MusicCodec.CODEC); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..6b0cee70 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,23 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; +import org.jspecify.annotations.NonNull; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier ChannelID = Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type ID = new Type<>(ChannelID); + public static final StreamCodec CODEC = + StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + buf -> new MusicCodec(MusicPacketCodec.decode(buf))); + + @Override + public @NonNull Type type() { + return ID; + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java new file mode 100644 index 00000000..bc0bf10c --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.minecraft.server.MinecraftServer; + +import java.io.File; + +public class AllMusicServer implements ModInitializer { + public static MinecraftServer server; + + @Override + public void onInitialize() { + AllMusic.log = new LogFabric(); + AllMusic.side = new SideFabric(); + + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) + -> CommandFabric.instance.register(dispatcher)); + + ServerLifecycleEvents.SERVER_STARTED.register((a) -> { + server = a; + AllMusic.init(new File(AllMusic.SERVER_DIR)); + AllMusic.start(); + Tasks.init(); + }); + + ServerLifecycleEvents.SERVER_STOPPING.register((a) -> { + AllMusic.stop(); + }); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java new file mode 100644 index 00000000..5bf02199 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/CommandFabric.java @@ -0,0 +1,69 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class CommandFabric implements Command, Predicate, SuggestionProvider { + public static CommandFabric instance = new CommandFabric(); + + public void register(CommandDispatcher dispatcher) { + dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) + .then(Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this).executes(this))); + } + + @Override + public int run(CommandContext context) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, arg.length); + + CommandEX.execute(item, context.getSource().getTextName(), arg); + + return 0; + } + + @Override + public boolean test(CommandSourceStack stack) { + return true; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, temp.length - 1); + + builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); + + List results = CommandEX.getTabList(item, item.getTextName(), arg); + + String remaining = builder.getRemaining().trim(); + + for (String s : results) { + if (s.startsWith(remaining)) { + builder.suggest(s); + } + } + + return builder.buildFuture(); + } +} \ No newline at end of file diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java new file mode 100644 index 00000000..6b749f8e --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/LogFabric.java @@ -0,0 +1,12 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + + +public class LogFabric implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicServer.server.sendMessage(data); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java new file mode 100644 index 00000000..c6192e39 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/SideFabric.java @@ -0,0 +1,157 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.comm.MusicCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.PermissionSet; +import net.minecraft.world.InteractionResult; + +import java.io.File; +import java.util.Collection; + +public class SideFabric extends BaseSide { + @Override + public void runTask(Runnable run) { + AllMusicServer.server.execute(run); + } + + @Override + public void runTask(Runnable run1, int delay) { + Tasks.add(new TaskItem() {{ + tick = delay; + run = run1; + }}); + } + + @Override + public boolean checkPermission(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.permissions() == PermissionSet.ALL_PERMISSIONS; + } + + @Override + public boolean isPlayer(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.isPlayer(); + } + + @Override + public boolean checkPermission(Object player, String permission) { + return checkPermission(player); + } + + @Override + public boolean needPlay(boolean islist) { + for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!com.coloryr.allmusic.server.core.AllMusic.isSkip(player.getName().getString(), null, false, islist)) { + return true; + } + } + return false; + } + + @Override + public Collection getPlayers() { + return AllMusicServer.server.getPlayerList().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ServerPlayer player1) { + return player1.getName().getString(); + } + + return null; + } + + @Override + public String getPlayerServer(Object player) { + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ServerPlayer player1) { + send(player1, pack); + } + } + + @Override + public Object getPlayer(String player) { + return AllMusicServer.server.getPlayerList().getPlayer(player); + } + + @Override + public void sendBar(Object player, net.kyori.adventure.text.Component data) { + if (player instanceof ServerPlayer player1) { + player1.sendActionBar(data); + } + } + + @Override + public File getFolder() { + return new File(AllMusic.SERVER_DIR); + } + + @Override + public void broadcast(net.kyori.adventure.text.Component message) { + for (var player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false)) { + player.sendMessage(message); + } + } + } + + @Override + public void sendMessage(Object obj, net.kyori.adventure.text.Component message) { + if (obj instanceof CommandSourceStack source) { + source.sendMessage(message); + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + return MusicPlayEvent.EVENT.invoker().interact(obj) != InteractionResult.PASS; + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + CommandSourceStack source = (CommandSourceStack) obj; + return MusicAddEvent.EVENT.invoker().interact(source.getPlayer(), music) != InteractionResult.PASS; + } + + @Override + public Component miniMessage(String input) { + MiniMessage mm = MiniMessage.miniMessage(); + return mm.deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.runCommand(command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.suggestCommand(command)); + } + + private void send(ServerPlayer players, MusicPack data) { + if (players == null) + return; + runTask(() -> ServerPlayNetworking.send(players, new MusicCodec(data))); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java new file mode 100644 index 00000000..40f9a5b9 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java @@ -0,0 +1,6 @@ +package com.coloryr.allmusic.server; + +public class TaskItem { + public int tick; + public Runnable run; +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java new file mode 100644 index 00000000..d4e72d39 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java @@ -0,0 +1,27 @@ +package com.coloryr.allmusic.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tasks { + private static final List taskItems = new CopyOnWriteArrayList<>(); + + public static void init() { + AllMusicServer.server.addTickable(Tasks::tick); + } + + public static void tick() { + for (var item : new ArrayList<>(taskItems)) { + item.tick--; + if (item.tick <= 0) { + taskItems.remove(item); + item.run.run(); + } + } + } + + public static void add(TaskItem item) { + taskItems.add(item); + } +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..6ef1835c --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; + +public interface MusicAddEvent { + Event EVENT = EventFactory.createArrayBacked(MusicAddEvent.class, + (listeners) -> (player, music) -> { + for (MusicAddEvent listener : listeners) { + InteractionResult result = listener.interact(player, music); + + if (result != InteractionResult.PASS) { + return result; + } + } + + return InteractionResult.PASS; + }); + + InteractionResult interact(ServerPlayer player, PlayerAddMusicObj music); +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..852895f1 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,29 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.world.InteractionResult; + +public interface MusicPlayEvent { + Event EVENT = EventFactory.createArrayBacked(MusicPlayEvent.class, + (listeners) -> (music) -> { + for (MusicPlayEvent listener : listeners) { + InteractionResult result = listener.interact(music); + + if (result != InteractionResult.PASS) { + return result; + } + } + + return InteractionResult.PASS; + }); + + /** + * 音乐播放事件,返回PASS表示通过 + * + * @param music 歌曲信息 + * @return 是否通过 + */ + InteractionResult interact(SongInfoObj music); +} diff --git a/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java new file mode 100644 index 00000000..60e133d4 --- /dev/null +++ b/server/fabric_26_3/src/main/java/com/coloryr/allmusic/server/mixin/ServerPlayerMixin.java @@ -0,0 +1,25 @@ +package com.coloryr.allmusic.server.mixin; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import net.minecraft.network.Connection; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.CommonListenerCookie; +import net.minecraft.server.players.PlayerList; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(PlayerList.class) +public class ServerPlayerMixin { + @Inject(at = @At("HEAD"), method = "remove") + private void remove(ServerPlayer entity, CallbackInfo info) { + PlayMusic.removeNowPlayPlayer(entity.getName().getString()); + } + + @Inject(at = @At("TAIL"), method = "placeNewPlayer") + private void add(Connection connection, ServerPlayer player, CommonListenerCookie clientData, CallbackInfo ci) { + AllMusic.joinPlay(player.getName().getString()); + } +} diff --git a/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json b/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json new file mode 100644 index 00000000..6e607dbc --- /dev/null +++ b/server/fabric_26_3/src/main/resources/allmusic_server.mixins.json @@ -0,0 +1,17 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.coloryr.allmusic.server.mixin", + "compatibilityLevel": "JAVA_25", + "server": [ + ], + "mixins": [ + "ServerPlayerMixin" + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +} diff --git a/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png b/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/server/fabric_26_3/src/main/resources/fabric.mod.json b/server/fabric_26_3/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..b8127dee --- /dev/null +++ b/server/fabric_26_3/src/main/resources/fabric.mod.json @@ -0,0 +1,37 @@ +{ + "schemaVersion": 1, + "id": "allmusic_server", + "version": "${version}", + "name": "AllMusic Server", + "description": "AllMusic Server Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic_server/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.comm.AllMusicInit", + "com.coloryr.allmusic.server.AllMusicServer" + ] + }, + "mixins": [ + { + "config": "allmusic_server.mixins.json", + "environment": "server" + } + ], + "depends": { + "fabricloader": ">=0.19.5", + "minecraft": ">=26.3", + "fabric-api": "*", + "adventure-platform-fabric": ">=7.2.0" + }, + "suggests": { + } +} diff --git a/server/neoforge_26_3/build.gradle.kts b/server/neoforge_26_3/build.gradle.kts new file mode 100644 index 00000000..441c444f --- /dev/null +++ b/server/neoforge_26_3/build.gradle.kts @@ -0,0 +1,51 @@ +import net.fabricmc.loom.build.nesting.NestableJarGenerationTask + +plugins { + id("dev.architectury.loom-no-remap") version Versions.architecturyLoom +// id("architectury-plugin") version "3.5-SNAPSHOT" +} + +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +//architectury { +// platformSetupLoomIde() +// neoForge() +//} + +repositories { + maven("https://maven.neoforged.net/releases/") +} + +dependencies { + minecraft("com.mojang:minecraft:26.3") + neoForge("net.neoforged:neoforge:26.3.0.6-beta") + + implementation(include("net.kyori:adventure-platform-neoforge:7.2.0")!!) +} + +tasks { + processResources { + filesMatching("META-INF/neoforge.mods.toml") { + expand("version" to project.version) + } + } + + // loom 的 jar-in-jar 嵌套默认只挂在 jar 任务上,需要手动接到 shadowJar + val processIncludeJars = named("processIncludeJars", NestableJarGenerationTask::class) + val shadowJarTask = named("shadowJar", Jar::class) + shadowJarTask.configure { dependsOn(processIncludeJars) } + loom.nestJars( + shadowJarTask, + files(fileTree(processIncludeJars.flatMap { it.outputDirectory }).matching { include("**/*.jar") }) + ) + + shadowJar { + archiveFileName.set("[neoforge-26.3]AllMusic_Server-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(shadowJar) + } +} diff --git a/server/neoforge_26_3/gradle.properties b/server/neoforge_26_3/gradle.properties new file mode 100644 index 00000000..7da18ea6 --- /dev/null +++ b/server/neoforge_26_3/gradle.properties @@ -0,0 +1 @@ +loom.platform=neoforge diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java new file mode 100644 index 00000000..d9282960 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/AllMusicInit.java @@ -0,0 +1,33 @@ +package com.coloryr.allmusic.comm; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.neoforged.neoforge.network.handling.IPayloadHandler; + +@Mod(AllMusicInit.MODID) +public class AllMusicInit implements IPayloadHandler { + + public static final String MODID = "allmusic"; + + public static IPayloadHandler handler; + + public AllMusicInit(IEventBus modEventBus, ModContainer modContainer) { + modEventBus.addListener(this::register); + } + + public void register(final RegisterPayloadHandlersEvent event) { + event.registrar("1.0") + .optional() + .playToClient(MusicCodec.TYPE, MusicCodec.CODEC, this); + } + + @Override + public void handle(MusicCodec data, IPayloadContext iPayloadContext) { + if (handler != null) { + handler.handle(data, iPayloadContext); + } + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java new file mode 100644 index 00000000..434fa510 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/comm/MusicCodec.java @@ -0,0 +1,24 @@ +package com.coloryr.allmusic.comm; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.codec.MusicPacketCodec; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.Identifier; + +public record MusicCodec(MusicPack pack) implements CustomPacketPayload { + public static final Identifier channel = + Identifier.fromNamespaceAndPath("allmusic", "channel"); + + public static final Type TYPE = new Type<>(channel); + public static final StreamCodec CODEC = StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack), + (buf) -> { + return new MusicCodec(MusicPacketCodec.decode(buf)); + }); + + @Override + public Type type() { + return TYPE; + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java new file mode 100644 index 00000000..2e1109f1 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/AllMusicServer.java @@ -0,0 +1,74 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.music.PlayMusic; +import com.mojang.brigadier.CommandDispatcher; +import net.kyori.adventure.platform.modcommon.MinecraftServerAudiences; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.MinecraftServer; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStoppingEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; + +@EventBusSubscriber(modid = "allmusic_server", value = Dist.DEDICATED_SERVER) +public class AllMusicServer { + public static final Logger LOGGER = LoggerFactory.getLogger("AllMusic Server"); + public static MinecraftServer server; + public static MinecraftServerAudiences audiences; + + @SubscribeEvent + public static void commonSetup(final FMLCommonSetupEvent event) { + AllMusic.log = new LogNeoForge(); + AllMusic.side = new SideNeoForge(); + } + + @SubscribeEvent + public static void onRegisterCommands(RegisterCommandsEvent event) { + if (event.getCommandSelection() != Commands.CommandSelection.INTEGRATED) { + CommandDispatcher dispatcher = event.getDispatcher(); + CommandNeoForge.instance.register(dispatcher); + } + } + + @SubscribeEvent + public static void onServerStarting(ServerStartedEvent event) { + if (event.getServer().isDedicatedServer()) { + audiences = MinecraftServerAudiences.of(event.getServer()); + server = event.getServer(); + AllMusic.init(new File(AllMusic.SERVER_DIR)); + AllMusic.start(); + Tasks.init(); + } + } + + @SubscribeEvent + public static void onServerStopping(ServerStoppingEvent event) { + if (event.getServer().isDedicatedServer()) { + AllMusic.stop(); + } + } + + @SubscribeEvent + public static void onPlayerLoggedOut(PlayerEvent.PlayerLoggedOutEvent event) { + if (server != null) { + PlayMusic.removeNowPlayPlayer(event.getEntity().getName().getString()); + } + } + + @SubscribeEvent + public static void onPlayerLoggedIn(PlayerEvent.PlayerLoggedInEvent event) { + if (server != null) { + AllMusic.joinPlay(event.getEntity().getName().getString()); + } + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java new file mode 100644 index 00000000..f6275e4e --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/CommandNeoForge.java @@ -0,0 +1,66 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.command.CommandEX; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +public class CommandNeoForge implements Command, Predicate, SuggestionProvider { + public static CommandNeoForge instance = new CommandNeoForge(); + + public void register(CommandDispatcher dispatcher) { + dispatcher.register(((Commands.literal("music").requires(this)).executes(this)) + .then(Commands.argument("args", StringArgumentType.greedyString()) + .suggests(this).executes(this))); + } + + @Override + public int run(CommandContext context) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, arg.length); + + CommandEX.execute(item, context.getSource().getTextName(), arg); + + return 0; + } + + @Override + public boolean test(CommandSourceStack stack) { + return true; + } + + @Override + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + var item = context.getSource(); + + var input = context.getInput(); + var temp = input.split(" "); + var arg = new String[input.endsWith(" ") ? temp.length : temp.length - 1]; + System.arraycopy(temp, 1, arg, 0, temp.length - 1); + + if (input.endsWith(" ")) + builder = builder.createOffset(builder.getInput().lastIndexOf(32) + 1); + + List results = CommandEX.getTabList(item, item.getTextName(), arg); + + for (String s : results) { + builder.suggest(s); + } + + return builder.buildFuture(); + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java new file mode 100644 index 00000000..2b12188a --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/LogNeoForge.java @@ -0,0 +1,11 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.server.core.side.IAllMusicLogger; +import net.kyori.adventure.text.Component; + +public class LogNeoForge implements IAllMusicLogger { + @Override + public void data(Component data) { + AllMusicServer.audiences.console().sendMessage(data); + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java new file mode 100644 index 00000000..7e79134a --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/SideNeoForge.java @@ -0,0 +1,161 @@ +package com.coloryr.allmusic.server; + +import com.coloryr.allmusic.codec.MusicPack; +import com.coloryr.allmusic.comm.MusicCodec; +import com.coloryr.allmusic.server.core.AllMusic; +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import com.coloryr.allmusic.server.core.side.BaseSide; +import com.coloryr.allmusic.server.event.MusicAddEvent; +import com.coloryr.allmusic.server.event.MusicPlayEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.minimessage.MiniMessage; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.PermissionSet; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.network.PacketDistributor; + +import java.io.File; +import java.util.Collection; + +public class SideNeoForge extends BaseSide { + @Override + public void runTask(Runnable run) { + AllMusicServer.server.execute(run); + } + + @Override + public void runTask(Runnable run1, int delay) { + Tasks.add(new TaskItem() {{ + tick = delay; + run = run1; + }}); + } + + @Override + public boolean checkPermission(Object player, String permission) { + return checkPermission(player); + } + + @Override + public boolean checkPermission(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.permissions() == PermissionSet.ALL_PERMISSIONS; + } + + @Override + public boolean isPlayer(Object player) { + CommandSourceStack source = (CommandSourceStack) player; + return source.isPlayer(); + } + + @Override + public boolean needPlay(boolean islist) { + for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false, islist)) { + return true; + } + } + return false; + } + + @Override + public Collection getPlayers() { + return AllMusicServer.server.getPlayerList().getPlayers(); + } + + @Override + public String getPlayerName(Object player) { + if (player instanceof ServerPlayer player1) { + return player1.getName().getString(); + } + + return null; + } + + @Override + public String getPlayerServer(Object player) { + return null; + } + + @Override + public void send(Object player, MusicPack pack) { + if (player instanceof ServerPlayer player1) { + send(player1, pack); + } + } + + @Override + public Object getPlayer(String player) { + return AllMusicServer.server.getPlayerList().getPlayerByName(player); + } + + @Override + public void sendBar(Object player, Component data) { + if (player instanceof ServerPlayer player1) { + AllMusicServer.audiences.audience(player1).sendActionBar(data); + } + } + + @Override + public File getFolder() { + return new File(AllMusic.SERVER_DIR); + } + + @Override + public void broadcast(Component message) { + for (ServerPlayer player : AllMusicServer.server.getPlayerList().getPlayers()) { + if (!AllMusic.isSkip(player.getName().getString(), null, false)) { + AllMusicServer.audiences.audience(player).sendActionBar(message); + } + } + } + + @Override + public void sendMessage(Object obj, Component message) { + if (obj instanceof CommandSourceStack source) { + AllMusicServer.audiences.audience(source).sendActionBar(message); + } + } + + @Override + public boolean onMusicPlay(SongInfoObj obj) { + MusicPlayEvent event = new MusicPlayEvent(obj); + NeoForge.EVENT_BUS.post(event); + return event.isCancel(); + } + + @Override + public boolean onMusicAdd(Object obj, PlayerAddMusicObj music) { + CommandSourceStack source = (CommandSourceStack) obj; + MusicAddEvent event = new MusicAddEvent(music, source.getPlayer()); + NeoForge.EVENT_BUS.post(event); + return event.isCancel(); + } + + @Override + public Component miniMessage(String input) { + MiniMessage mm = MiniMessage.miniMessage(); + return mm.deserialize(input); + } + + @Override + public Component miniMessageRun(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.runCommand(command)); + } + + @Override + public Component miniMessageSuggest(String input, String command) { + Component component = miniMessage(input); + return component.clickEvent(ClickEvent.suggestCommand(command)); + } + + private void send(ServerPlayer players, MusicPack data) { + if (players == null) + return; + runTask(() -> PacketDistributor.sendToPlayer(players, new MusicCodec(data))); + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java new file mode 100644 index 00000000..40f9a5b9 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/TaskItem.java @@ -0,0 +1,6 @@ +package com.coloryr.allmusic.server; + +public class TaskItem { + public int tick; + public Runnable run; +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java new file mode 100644 index 00000000..b861abcc --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/Tasks.java @@ -0,0 +1,27 @@ +package com.coloryr.allmusic.server; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +public class Tasks { + private static final List taskItems = new CopyOnWriteArrayList<>(); + + public static void init() { + AllMusicServer.server.addTickable(Tasks::tick); + } + + public static void tick() { + for (TaskItem item : new ArrayList<>(taskItems)) { + item.tick--; + if (item.tick <= 0) { + taskItems.remove(item); + item.run.run(); + } + } + } + + public static void add(TaskItem item) { + taskItems.add(item); + } +} \ No newline at end of file diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java new file mode 100644 index 00000000..7c730137 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicAddEvent.java @@ -0,0 +1,39 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.PlayerAddMusicObj; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.bus.api.Event; + +public class MusicAddEvent extends Event { + /** + * 添加的音乐 + */ + private final PlayerAddMusicObj music; + /** + * 添加者 + */ + private final ServerPlayer player; + + private boolean cancel; + + public MusicAddEvent(PlayerAddMusicObj id, ServerPlayer player) { + this.music = id; + this.player = player; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public ServerPlayer getPlayer() { + return player; + } + + public PlayerAddMusicObj getMusic() { + return music; + } +} diff --git a/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java new file mode 100644 index 00000000..b16e0f95 --- /dev/null +++ b/server/neoforge_26_3/src/main/java/com/coloryr/allmusic/server/event/MusicPlayEvent.java @@ -0,0 +1,25 @@ +package com.coloryr.allmusic.server.event; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; +import net.neoforged.bus.api.Event; + +public class MusicPlayEvent extends Event { + private final SongInfoObj music; + private boolean cancel; + + public MusicPlayEvent(SongInfoObj music) { + this.music = music; + } + + public boolean isCancel() { + return cancel; + } + + public void setCancel(boolean cancel) { + this.cancel = cancel; + } + + public SongInfoObj getMusic() { + return music; + } +} diff --git a/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml b/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 00000000..177695c3 --- /dev/null +++ b/server/neoforge_26_3/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,36 @@ +modLoader = "javafml" +loaderVersion = "[2,)" +license = "GPL-3.0" +issueTrackerURL = "https://github.com/Coloryr/AllMusic/issues" + +[[mods]] +modId = "allmusic" +version = "${version}" +displayName = "AllMusic" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Mod''' + +[[mods]] +modId = "allmusic_server" +version = "${version}" +displayName = "AllMusic Server" +displayURL = "https://github.com/Coloryr/AllMusic/" +logoFile = "icon.png" +authors = "Color_yr" +description = '''AllMusic Server Mod''' + +[[dependencies.allmusic_server]] +modId = "neoforge" +type = "required" +versionRange = "[26.3.0.6-beta,)" +ordering = "NONE" +side = "BOTH" + +[[dependencies.allmusic_server]] +modId = "minecraft" +type = "required" +versionRange = "[26.3,)" +ordering = "NONE" +side = "BOTH" \ No newline at end of file diff --git a/server/neoforge_26_3/src/main/resources/icon.png b/server/neoforge_26_3/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b5fa790ca8e14661cd169aa56e1ca5dd818a76 GIT binary patch literal 32781 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 literal 0 HcmV?d00001 diff --git a/settings.gradle.kts b/settings.gradle.kts index 86acdff1..3e613230 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ include(":client:fabric_1_21_8") include(":client:fabric_1_21_11") include(":client:fabric_26_1") include(":client:fabric_26_2") +include(":client:fabric_26_3") include(":client:forge_1_7_10") include(":client:forge_1_12_2") @@ -27,6 +28,7 @@ include(":client:neoforge_1_21_6") include(":client:neoforge_1_21_11") include(":client:neoforge_26_1") include(":client:neoforge_26_2") +include(":client:neoforge_26_3") include(":server") @@ -50,6 +52,8 @@ include(":server:neoforge_1_21_6") include(":server:neoforge_1_21_11") include(":server:neoforge_26_1") include(":server:neoforge_26_2") +include(":server:fabric_26_3") +include(":server:neoforge_26_3") include(":server:spigot") include(":server:paper") @@ -81,6 +85,8 @@ include(":onejar:neoforge_1_21_6") include(":onejar:neoforge_1_21_11") include(":onejar:neoforge_26_1") include(":onejar:neoforge_26_2") +include(":onejar:fabric_26_3") +include(":onejar:neoforge_26_3") pluginManagement { repositories { From 9b1c3e9f9fbc8c6c26c8d876851796f336016ee6 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 20:55:14 +0800 Subject: [PATCH 38/46] chore: preserve Gradle wrapper mode --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 gradlew diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 From 5570d8495c66bc6dc23a5b1e9aa8605fa3659e8f Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 21:09:37 +0800 Subject: [PATCH 39/46] ci: scope AllMusic compatibility builds by platform --- .github/workflows/compatibility.yml | 10 +- settings.gradle.kts | 209 +++++++++++++++++----------- 2 files changed, 129 insertions(+), 90 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index 7c2edb57..cba8d4e2 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -39,7 +39,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :client:fabric_1_20_1:build + ./gradlew -Pallmusic.scope=legacy-client -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :client:fabric_1_20_1:build modern-clients: name: Minecraft 26.3 clients @@ -73,7 +73,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + ./gradlew -Pallmusic.scope=modern-client-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" legacy-server: name: Legacy Paper server @@ -100,7 +100,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :server:paper:build + ./gradlew -Pallmusic.scope=legacy-server -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :server:paper:build modern-servers: name: Minecraft 26.3 servers @@ -138,7 +138,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + ./gradlew -Pallmusic.scope=modern-server-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" onejar: name: Minecraft 26.3 one-jars @@ -172,4 +172,4 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + ./gradlew -Pallmusic.scope=onejar-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" diff --git a/settings.gradle.kts b/settings.gradle.kts index 3e613230..5e183301 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,92 +1,131 @@ rootProject.name = "AllMusic" -include(":codec") - -include(":client") - -include(":client:fabric_1_16_5") -include(":client:fabric_1_20_1") -include(":client:fabric_1_21") -include(":client:fabric_1_21_3") -include(":client:fabric_1_21_4") -include(":client:fabric_1_21_6") -include(":client:fabric_1_21_8") -include(":client:fabric_1_21_11") -include(":client:fabric_26_1") -include(":client:fabric_26_2") -include(":client:fabric_26_3") - -include(":client:forge_1_7_10") -include(":client:forge_1_12_2") -include(":client:forge_1_16_5") -include(":client:forge_1_20_1") - -include(":client:neoforge_1_21") -include(":client:neoforge_1_21_3") -include(":client:neoforge_1_21_4") -include(":client:neoforge_1_21_6") -include(":client:neoforge_1_21_11") -include(":client:neoforge_26_1") -include(":client:neoforge_26_2") -include(":client:neoforge_26_3") - -include(":server") - -include(":server:fabric_1_16_5") -include(":server:fabric_1_20_1") -include(":server:fabric_1_21") -include(":server:fabric_1_21_4") -include(":server:fabric_1_21_6") -include(":server:fabric_1_21_11") -include(":server:fabric_26_1") -include(":server:fabric_26_2") - -include(":server:forge_1_7_10") -include(":server:forge_1_12_2") -include(":server:forge_1_16_5") -include(":server:forge_1_20_1") - -include(":server:neoforge_1_21") -include(":server:neoforge_1_21_4") -include(":server:neoforge_1_21_6") -include(":server:neoforge_1_21_11") -include(":server:neoforge_26_1") -include(":server:neoforge_26_2") -include(":server:fabric_26_3") -include(":server:neoforge_26_3") - -include(":server:spigot") -include(":server:paper") -include(":server:folia") -include(":server:paper") -include(":server:bungeecord") -include(":server:velocity") +// CI can select one compatibility scope so an unrelated legacy plugin cannot +// prevent a focused client, server, or one-jar build from configuring. +val allmusicScope = providers.gradleProperty("allmusic.scope").orNull +val scopedProjects: Set? = when (allmusicScope) { + "legacy-client" -> setOf(":codec", ":client", ":client:fabric_1_20_1") + "client-26.2" -> setOf(":codec", ":client", ":client:fabric_26_2") + "modern-client-26.3" -> setOf(":codec", ":client", ":client:fabric_26_3", ":client:neoforge_26_3") + "legacy-server" -> setOf(":codec", ":server", ":server:paper") + "server-all" -> setOf( + ":codec", ":server", + ":server:fabric_1_16_5", ":server:fabric_1_20_1", ":server:fabric_1_21", + ":server:fabric_1_21_4", ":server:fabric_1_21_6", ":server:fabric_1_21_11", + ":server:fabric_26_1", ":server:fabric_26_2", ":server:fabric_26_3", + ":server:neoforge_1_21", ":server:neoforge_1_21_4", + ":server:neoforge_1_21_6", ":server:neoforge_1_21_11", + ":server:neoforge_26_1", ":server:neoforge_26_2", ":server:neoforge_26_3", + ":server:spigot", ":server:paper", ":server:folia", + ":server:bungeecord", ":server:velocity", + ":server:paper_26_3", ":server:folia_26_3", + ":server:spigot_26_3", ":server:velocity_26_3" + ) + "modern-server-26.3" -> setOf( + ":codec", ":server", ":server:fabric_26_3", ":server:neoforge_26_3", + ":server:paper_26_3", ":server:folia_26_3", + ":server:spigot_26_3", ":server:velocity_26_3" + ) + "onejar-26.3" -> setOf( + ":codec", ":client", ":server", ":onejar", + ":client:fabric_26_3", ":client:neoforge_26_3", + ":server:fabric_26_3", ":server:neoforge_26_3", + ":onejar:fabric_26_3", ":onejar:neoforge_26_3" + ) + else -> null +} + +fun includeProject(path: String) { + if (scopedProjects == null || path in scopedProjects) include(path) +} + +includeProject(":codec") + +includeProject(":client") + +includeProject(":client:fabric_1_16_5") +includeProject(":client:fabric_1_20_1") +includeProject(":client:fabric_1_21") +includeProject(":client:fabric_1_21_3") +includeProject(":client:fabric_1_21_4") +includeProject(":client:fabric_1_21_6") +includeProject(":client:fabric_1_21_8") +includeProject(":client:fabric_1_21_11") +includeProject(":client:fabric_26_1") +includeProject(":client:fabric_26_2") +includeProject(":client:fabric_26_3") + +includeProject(":client:forge_1_7_10") +includeProject(":client:forge_1_12_2") +includeProject(":client:forge_1_16_5") +includeProject(":client:forge_1_20_1") + +includeProject(":client:neoforge_1_21") +includeProject(":client:neoforge_1_21_3") +includeProject(":client:neoforge_1_21_4") +includeProject(":client:neoforge_1_21_6") +includeProject(":client:neoforge_1_21_11") +includeProject(":client:neoforge_26_1") +includeProject(":client:neoforge_26_2") +includeProject(":client:neoforge_26_3") + +includeProject(":server") + +includeProject(":server:fabric_1_16_5") +includeProject(":server:fabric_1_20_1") +includeProject(":server:fabric_1_21") +includeProject(":server:fabric_1_21_4") +includeProject(":server:fabric_1_21_6") +includeProject(":server:fabric_1_21_11") +includeProject(":server:fabric_26_1") +includeProject(":server:fabric_26_2") + +includeProject(":server:forge_1_7_10") +includeProject(":server:forge_1_12_2") +includeProject(":server:forge_1_16_5") +includeProject(":server:forge_1_20_1") + +includeProject(":server:neoforge_1_21") +includeProject(":server:neoforge_1_21_4") +includeProject(":server:neoforge_1_21_6") +includeProject(":server:neoforge_1_21_11") +includeProject(":server:neoforge_26_1") +includeProject(":server:neoforge_26_2") +includeProject(":server:fabric_26_3") +includeProject(":server:neoforge_26_3") + +includeProject(":server:spigot") +includeProject(":server:paper") +includeProject(":server:folia") +includeProject(":server:paper") +includeProject(":server:bungeecord") +includeProject(":server:velocity") // Minecraft 26.3 Bukkit-family adapters; legacy server modules above remain unchanged. -include(":server:paper_26_3") -include(":server:folia_26_3") -include(":server:spigot_26_3") -include(":server:velocity_26_3") - -include(":onejar") - -include(":onejar:fabric_1_16_5") -include(":onejar:fabric_1_20_1") -include(":onejar:fabric_1_21") -include(":onejar:fabric_1_21_4") -include(":onejar:fabric_1_21_6") -include(":onejar:fabric_1_21_11") -include(":onejar:fabric_26_1") -include(":onejar:fabric_26_2") - -include(":onejar:neoforge_1_21") -include(":onejar:neoforge_1_21_4") -include(":onejar:neoforge_1_21_6") -include(":onejar:neoforge_1_21_11") -include(":onejar:neoforge_26_1") -include(":onejar:neoforge_26_2") -include(":onejar:fabric_26_3") -include(":onejar:neoforge_26_3") +includeProject(":server:paper_26_3") +includeProject(":server:folia_26_3") +includeProject(":server:spigot_26_3") +includeProject(":server:velocity_26_3") + +includeProject(":onejar") + +includeProject(":onejar:fabric_1_16_5") +includeProject(":onejar:fabric_1_20_1") +includeProject(":onejar:fabric_1_21") +includeProject(":onejar:fabric_1_21_4") +includeProject(":onejar:fabric_1_21_6") +includeProject(":onejar:fabric_1_21_11") +includeProject(":onejar:fabric_26_1") +includeProject(":onejar:fabric_26_2") + +includeProject(":onejar:neoforge_1_21") +includeProject(":onejar:neoforge_1_21_4") +includeProject(":onejar:neoforge_1_21_6") +includeProject(":onejar:neoforge_1_21_11") +includeProject(":onejar:neoforge_26_1") +includeProject(":onejar:neoforge_26_2") +includeProject(":onejar:fabric_26_3") +includeProject(":onejar:neoforge_26_3") pluginManagement { repositories { From 08dda474d369e9cde3f82e2f32e0f58d94ae01b3 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 21:16:44 +0800 Subject: [PATCH 40/46] ci: use scoped compatibility builds for release jobs --- .github/workflows/client.yml | 2 +- .github/workflows/server.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index 947571ef..66b9329d 100644 --- a/.github/workflows/client.yml +++ b/.github/workflows/client.yml @@ -50,7 +50,7 @@ jobs: run: | set "JAVA_HOME=%JAVA_HOME_21%" set "PATH=%JAVA_HOME_21%\bin;%PATH%" - gradlew.bat -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :client:fabric_26_2:build --configure-on-demand --parallel --max-workers=4 + gradlew.bat -Pallmusic.scope=client-26.2 -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :client:fabric_26_2:build --configure-on-demand --parallel --max-workers=4 - name: update uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index b2aad8e8..c523f352 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -49,7 +49,7 @@ jobs: run: | set "JAVA_HOME=%JAVA_HOME_21%" set "PATH=%JAVA_HOME_21%\bin;%PATH%" - gradlew.bat -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :server:buildServer + gradlew.bat -Pallmusic.scope=server-all -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :server:buildServer - name: update uses: actions/upload-artifact@v4 with: From ff2b4590bb334578f156f8024188883fcf7c6057 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 21:27:41 +0800 Subject: [PATCH 41/46] ci: invoke non-executable Gradle wrapper through Bash --- .github/workflows/compatibility.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index cba8d4e2..cbd3caee 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -39,7 +39,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Pallmusic.scope=legacy-client -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :client:fabric_1_20_1:build + bash ./gradlew -Pallmusic.scope=legacy-client -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :client:fabric_1_20_1:build modern-clients: name: Minecraft 26.3 clients @@ -73,7 +73,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Pallmusic.scope=modern-client-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + bash ./gradlew -Pallmusic.scope=modern-client-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" legacy-server: name: Legacy Paper server @@ -100,7 +100,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Pallmusic.scope=legacy-server -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :server:paper:build + bash ./gradlew -Pallmusic.scope=legacy-server -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand :server:paper:build modern-servers: name: Minecraft 26.3 servers @@ -138,7 +138,7 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Pallmusic.scope=modern-server-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + bash ./gradlew -Pallmusic.scope=modern-server-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" onejar: name: Minecraft 26.3 one-jars @@ -172,4 +172,4 @@ jobs: run: | export JAVA_HOME="$JAVA_HOME_21" export PATH="$JAVA_HOME/bin:$PATH" - ./gradlew -Pallmusic.scope=onejar-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" + bash ./gradlew -Pallmusic.scope=onejar-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" From 997ab1157b9b8deb335d4a3efedbaa286fc1c434 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 21:40:44 +0800 Subject: [PATCH 42/46] fix: compile shared OpenAL player and use Java25 for NeoForge --- .github/workflows/compatibility.yml | 45 ++++++++++++------- .../allmusic/client/core/AllMusicPlayer.java | 1 - 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/.github/workflows/compatibility.yml b/.github/workflows/compatibility.yml index cbd3caee..640e358d 100644 --- a/.github/workflows/compatibility.yml +++ b/.github/workflows/compatibility.yml @@ -47,9 +47,11 @@ jobs: strategy: fail-fast: false matrix: - task: - - :client:fabric_26_3:build - - :client:neoforge_26_3:build + include: + - task: :client:fabric_26_3:build + launcher: '21' + - task: :client:neoforge_26_3:build + launcher: '25' steps: - uses: actions/checkout@v4 - name: Set up JDK 21 (Gradle launcher) @@ -70,8 +72,9 @@ jobs: JAVA_HOME_21: ${{ steps.java21.outputs.path }} JAVA_HOME_25: ${{ steps.java25.outputs.path }} TASK: ${{ matrix.task }} + LAUNCHER: ${{ matrix.launcher }} run: | - export JAVA_HOME="$JAVA_HOME_21" + if [ "$LAUNCHER" = "25" ]; then export JAVA_HOME="$JAVA_HOME_25"; else export JAVA_HOME="$JAVA_HOME_21"; fi export PATH="$JAVA_HOME/bin:$PATH" bash ./gradlew -Pallmusic.scope=modern-client-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" @@ -108,13 +111,19 @@ jobs: strategy: fail-fast: false matrix: - task: - - :server:fabric_26_3:build - - :server:neoforge_26_3:build - - :server:paper_26_3:build - - :server:folia_26_3:build - - :server:spigot_26_3:build - - :server:velocity_26_3:build + include: + - task: :server:fabric_26_3:build + launcher: '21' + - task: :server:neoforge_26_3:build + launcher: '25' + - task: :server:paper_26_3:build + launcher: '21' + - task: :server:folia_26_3:build + launcher: '21' + - task: :server:spigot_26_3:build + launcher: '21' + - task: :server:velocity_26_3:build + launcher: '21' steps: - uses: actions/checkout@v4 - name: Set up JDK 21 (Gradle launcher) @@ -135,8 +144,9 @@ jobs: JAVA_HOME_21: ${{ steps.java21.outputs.path }} JAVA_HOME_25: ${{ steps.java25.outputs.path }} TASK: ${{ matrix.task }} + LAUNCHER: ${{ matrix.launcher }} run: | - export JAVA_HOME="$JAVA_HOME_21" + if [ "$LAUNCHER" = "25" ]; then export JAVA_HOME="$JAVA_HOME_25"; else export JAVA_HOME="$JAVA_HOME_21"; fi export PATH="$JAVA_HOME/bin:$PATH" bash ./gradlew -Pallmusic.scope=modern-server-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" @@ -146,9 +156,11 @@ jobs: strategy: fail-fast: false matrix: - task: - - :onejar:fabric_26_3:build - - :onejar:neoforge_26_3:build + include: + - task: :onejar:fabric_26_3:build + launcher: '21' + - task: :onejar:neoforge_26_3:build + launcher: '25' steps: - uses: actions/checkout@v4 - name: Set up JDK 21 (Gradle launcher) @@ -169,7 +181,8 @@ jobs: JAVA_HOME_21: ${{ steps.java21.outputs.path }} JAVA_HOME_25: ${{ steps.java25.outputs.path }} TASK: ${{ matrix.task }} + LAUNCHER: ${{ matrix.launcher }} run: | - export JAVA_HOME="$JAVA_HOME_21" + if [ "$LAUNCHER" = "25" ]; then export JAVA_HOME="$JAVA_HOME_25"; else export JAVA_HOME="$JAVA_HOME_21"; fi export PATH="$JAVA_HOME/bin:$PATH" bash ./gradlew -Pallmusic.scope=onejar-26.3 -Dorg.gradle.java.installations.paths="$JAVA_HOME_25" --no-daemon --stacktrace --configure-on-demand "$TASK" diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 1699a1c3..471ff19b 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -293,7 +293,6 @@ private boolean resetSource() { } } } while (queued > 0); - } AL10.alSourcef(index, AL10.AL_GAIN, AllMusicCore.bridge.getVolume()); AL10.alSourcef(index, AL10.AL_PITCH, 1.0f); From 8728b01fb8cebc78511720d1d42187c680b1f0db Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 21:46:01 +0800 Subject: [PATCH 43/46] fix: keep playback reload branches inside run loop --- .../java/com/coloryr/allmusic/client/core/AllMusicPlayer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 471ff19b..2721378b 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -595,7 +595,6 @@ private void run() { continue; } } - } stopAndClearSource(); } else { From 1f144f4c44ec5e524d6141dcb21917f48106ef93 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 21:54:04 +0800 Subject: [PATCH 44/46] fix: restore shared player imports and task state --- .../coloryr/allmusic/client/core/AllMusicPlayer.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 2721378b..59acec50 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -1,6 +1,7 @@ package com.coloryr.allmusic.client.core; import com.coloryr.allmusic.client.core.objs.PlayTaskObj; +import com.coloryr.allmusic.client.core.player.AudioFormatDetector; import com.coloryr.allmusic.client.core.player.decoder.BuffPack; import com.coloryr.allmusic.client.core.player.decoder.IDecoder; import com.coloryr.allmusic.client.core.player.decoder.flac.FlacDecoder; @@ -26,6 +27,7 @@ import java.nio.IntBuffer; import java.util.Objects; import java.util.Stack; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -462,7 +464,8 @@ private void run() { continue; } - nowTask = tasks.pop(); + PlayTaskObj task = tasks.pop(); + nowTask = task; if (nowTask == null || nowTask.url == null || nowTask.url.isEmpty()) { continue; } @@ -837,10 +840,4 @@ public void setLocal(long local) throws IOException { seek(local); } - public void setReload() { - if (isPlay) { - reload = true; - isClose = true; - } - } } From 5b771c80efe528236818922443b641e419c1c670 Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 22:04:13 +0800 Subject: [PATCH 45/46] fix: expose format detection and use shared playback state --- .../com/coloryr/allmusic/client/core/AllMusicPlayer.java | 4 ---- .../allmusic/client/core/player/AudioFormatDetector.java | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java index 59acec50..72dfab6a 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicPlayer.java @@ -512,7 +512,6 @@ private void run() { if (task.time != 0) { decoder.set(task.time); } - int chatCount = 0; boolean decoderEnded = false; while (true) { @@ -569,9 +568,6 @@ private void run() { Thread.sleep(5); - checkChat(); - checkVolume(); - dequeue(); } catch (Exception e) { if (!isClose) { e.printStackTrace(); diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/player/AudioFormatDetector.java b/client/src/main/java/com/coloryr/allmusic/client/core/player/AudioFormatDetector.java index a8736961..037537fc 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/player/AudioFormatDetector.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/player/AudioFormatDetector.java @@ -1,7 +1,7 @@ package com.coloryr.allmusic.client.core.player; -final class AudioFormatDetector { - enum Format { +public final class AudioFormatDetector { + public enum Format { MP3, M4A, OGG, @@ -12,7 +12,7 @@ enum Format { private AudioFormatDetector() { } - static Format detect(byte[] head, int length) { + public static Format detect(byte[] head, int length) { int available = Math.min(length, head.length); if (available >= 4 && matches(head, 0, 'f', 'L', 'a', 'C')) { return Format.FLAC; @@ -32,7 +32,7 @@ static Format detect(byte[] head, int length) { return Format.UNKNOWN; } - static String hexPrefix(byte[] head, int length) { + public static String hexPrefix(byte[] head, int length) { int available = Math.min(Math.min(length, head.length), 12); StringBuilder result = new StringBuilder(available * 3); for (int index = 0; index < available; index++) { From e72c75bc2600353b11b3722fa07f43a419e3dcfa Mon Sep 17 00:00:00 2001 From: Arbousier1 Date: Tue, 6 Oct 2026 22:10:26 +0800 Subject: [PATCH 46/46] fix: compile shared HUD and resolve Velocity API from Paper --- .../java/com/coloryr/allmusic/client/core/AllMusicHud.java | 1 - server/velocity_26_3/build.gradle.kts | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java index 9ad3cd42..b53ce4c2 100644 --- a/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java +++ b/client/src/main/java/com/coloryr/allmusic/client/core/AllMusicHud.java @@ -100,7 +100,6 @@ public class AllMusicHud { private BufferedImage bg2; private volatile boolean isRun; private final Thread picThread; - private final ScheduledExecutorService service1; /** * 是否有图片 diff --git a/server/velocity_26_3/build.gradle.kts b/server/velocity_26_3/build.gradle.kts index 653839d8..607e1d58 100644 --- a/server/velocity_26_3/build.gradle.kts +++ b/server/velocity_26_3/build.gradle.kts @@ -2,7 +2,8 @@ java.sourceCompatibility = JavaVersion.VERSION_25 java.targetCompatibility = JavaVersion.VERSION_25 repositories { - maven("https://nexus.velocitypowered.com/repository/maven-public/") + // Velocity API artifacts are published to Paper's repository. The old + // nexus.velocitypowered.com endpoint intermittently returns HTTP 522. maven("https://repo.papermc.io/repository/maven-public/") }