diff --git a/.github/workflows/client.yml b/.github/workflows/client.yml index 37169be8..66b9329d 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 -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/compatibility.yml b/.github/workflows/compatibility.yml new file mode 100644 index 00000000..640e358d --- /dev/null +++ b/.github/workflows/compatibility.yml @@ -0,0 +1,188 @@ +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" + 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 + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + 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) + 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 }} + LAUNCHER: ${{ matrix.launcher }} + run: | + 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" + + 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" + 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 + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + 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) + 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 }} + LAUNCHER: ${{ matrix.launcher }} + run: | + 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" + + onejar: + name: Minecraft 26.3 one-jars + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + 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) + 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 }} + LAUNCHER: ${{ matrix.launcher }} + run: | + 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/.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..c523f352 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 -Pallmusic.scope=server-all -Dorg.gradle.java.installations.paths=%JAVA_HOME_25% --no-daemon :server:buildServer - name: update uses: actions/upload-artifact@v4 with: diff --git a/README.md b/README.md index 8f6ea137..7afad397 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的服务器或客户端** @@ -20,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 @@ -29,16 +36,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 +71,6 @@ AllMusic不会内置音乐api,你想要正常使用需要自己安装 > %allmusic_lyric% 歌词 > %allmusic_tlyric% 翻译的歌词 -## 更新日志 - -``` -4.0.0:全新字体渲染,拆分音乐API -``` - ## 配置文件说明 配置文件采用json格式,需要遵守json编写的格式规范 - maxPlayList 最大歌曲数 diff --git a/build.gradle.kts b/build.gradle.kts index d2c823db..cd3debab 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.2.5" // 设置项目JDK版本 java.sourceCompatibility = JavaVersion.VERSION_1_8 @@ -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_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_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/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_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/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/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/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_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/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_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/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_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/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_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/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_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/AllMusicClient.java b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index cb7dce0d..a45347ac 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 @@ -77,7 +77,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_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 00000000..b2b5fa79 Binary files /dev/null and b/client/fabric_26_3/src/main/resources/assets/allmusic_client/icon.png differ 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/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) } } 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..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 @@ -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; @@ -121,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_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(); 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; 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..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 @@ -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; @@ -123,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/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/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 0716ce7d..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 @@ -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); @@ -98,11 +93,12 @@ 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 - public TextFrameBuffer makeTextRender(String name) { + public TextFrameBuffer makeTextRender(String name) { return new CoreRenderTarget(name); } 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 1a61ff23..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 @@ -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); @@ -97,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 1a61ff23..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 @@ -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); @@ -97,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 443f400f..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 @@ -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; @@ -133,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 3ac78cae..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 @@ -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)) { @@ -140,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 3ac78cae..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 @@ -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)) { @@ -140,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 00000000..b2b5fa79 Binary files /dev/null and b/client/neoforge_26_3/src/main/resources/icon.png differ 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 709bff3d..97113a1d 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核心 @@ -53,6 +54,8 @@ public class AllMusicCore { */ private static AllMusicHud hud; + public static final ScheduledExecutorService service = Executors.newScheduledThreadPool(4); + /** * 更新音频缓存 */ @@ -142,6 +145,8 @@ public static synchronized void saveConfig() { } 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 5f294fca..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; /** * 是否有图片 @@ -134,10 +133,9 @@ public AllMusicHud(int size) { picThread = new Thread(this::run, "allmusic_pic"); picThread.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); modernRender = AllMusicCore.bridge.makeModernHudRender(size); @@ -173,7 +171,6 @@ public AllMusicHud(int size) { public void stop() { isRun = false; - service1.close(); semaphore.release(); picThread.interrupt(); if (modernRender != null) { 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 94% 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 d8f5d6f5..72dfab6a 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,7 +1,7 @@ -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.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; @@ -25,8 +25,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; @@ -64,14 +64,9 @@ public class AllMusicPlayer extends InputStream implements SeekableInput { private ScheduledExecutorService scheduler; 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() { @@ -79,7 +74,6 @@ public void stop() { isClose = true; semaphore.release(); semaphoreReload.release(); - scheduler.close(); } public void setChat() { @@ -291,16 +285,16 @@ private boolean resetSource() { 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); - } + 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); AL10.alSourcef(index, AL10.AL_GAIN, AllMusicCore.bridge.getVolume()); AL10.alSourcef(index, AL10.AL_PITCH, 1.0f); @@ -459,9 +453,6 @@ private void run() { isRun = true; while (true) { try { - if (!isRun) { - return; - } semaphore.acquire(); if (!isRun) { return; @@ -469,8 +460,15 @@ private void run() { prepareSource(); + if (tasks.isEmpty()) { + continue; + } + PlayTaskObj task = tasks.pop(); - if (task == null || task.url == null || task.url.isEmpty()) continue; + nowTask = task; + if (nowTask == null || nowTask.url == null || nowTask.url.isEmpty()) { + continue; + } tasks.clear(); nowTask = task; currentUrl = task.url; @@ -502,6 +500,7 @@ private void run() { } isPlay = true; + int frequency = decoder.getOutputFrequency(); int channels = decoder.getOutputChannels(); if (channels != 1 && channels != 2) { @@ -513,13 +512,19 @@ private void run() { if (task.time != 0) { decoder.set(task.time); } - int chatCount = 0; boolean decoderEnded = false; while (true) { if (!isRun) { return; } + if (isClose) { + break; + } + if (!AL10.alIsSource(index)) { + setReload(); + break; + } try { if (!isCurrentSource()) { reload = true; @@ -531,7 +536,9 @@ private void run() { if (!isRun) { return; } - if (isClose) break; + if (isClose) { + break; + } BuffPack output = decoder.decodeFrame(); if (output == null) { decoderEnded = true; @@ -561,13 +568,6 @@ private void run() { Thread.sleep(5); - if (isChat) { - chatCount++; - if (chatCount >= 200) { - isChat = false; - chatCount = 0; - } - } } catch (Exception e) { if (!isClose) { e.printStackTrace(); @@ -584,12 +584,9 @@ private void run() { Thread.sleep(50); } - if (!reload) { + if (reload) { wait = true; - if (semaphoreReload.tryAcquire(500, TimeUnit.MILLISECONDS)) { - if (!isRun) { - break; - } + if (semaphoreReload.tryAcquire(1, TimeUnit.SECONDS)) { if (reload) { nowTask = null; tasks.push(task); @@ -597,7 +594,6 @@ private void run() { continue; } } - isPlay = false; stopAndClearSource(); } else { @@ -680,9 +676,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; @@ -715,6 +721,13 @@ private void decodeClose() throws Exception { } } + public void setReload() { + if (isPlay) { + reload = true; + isClose = true; + } + } + @Override public synchronized int read() throws IOException { for (int retries = 0; ; retries++) { @@ -823,10 +836,4 @@ public void setLocal(long local) throws IOException { seek(local); } - public void setReload() { - if (isPlay) { - reload = true; - isClose = true; - } - } } 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++) { 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 475c7854..e5de9afb 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/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/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 00000000..b2b5fa79 Binary files /dev/null and b/onejar/fabric_1_21_4/src/main/resources/assets/allmusic/icon.png differ 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/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/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 00000000..b2b5fa79 Binary files /dev/null and b/onejar/fabric_26_3/src/main/resources/assets/allmusic/icon.png differ 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_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 00000000..b2b5fa79 Binary files /dev/null and b/onejar/neoforge_1_21_4/src/main/resources/icon.png differ 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/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 00000000..b2b5fa79 Binary files /dev/null and b/onejar/neoforge_26_2/src/main/resources/icon.png differ 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 00000000..b2b5fa79 Binary files /dev/null and b/onejar/neoforge_26_3/src/main/resources/icon.png differ diff --git a/server/bungeecord/build.gradle.kts b/server/bungeecord/build.gradle.kts new file mode 100644 index 00000000..f38808fa --- /dev/null +++ b/server/bungeecord/build.gradle.kts @@ -0,0 +1,34 @@ +repositories { + maven("https://libraries.minecraft.net/") +} + +dependencies { + 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") + } + 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/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 00000000..b2b5fa79 Binary files /dev/null and b/server/fabric_1_21_4/src/main/resources/assets/allmusic_server/icon.png differ 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/fabric_26_1/build.gradle.kts b/server/fabric_26_1/build.gradle.kts index d9659b92..e844f31a 100644 --- a/server/fabric_26_1/build.gradle.kts +++ b/server/fabric_26_1/build.gradle.kts @@ -23,12 +23,12 @@ 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")) - -// 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..e6a7073d 100644 --- a/server/fabric_26_2/build.gradle.kts +++ b/server/fabric_26_2/build.gradle.kts @@ -23,12 +23,12 @@ 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")) - -// 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_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 00000000..b2b5fa79 Binary files /dev/null and b/server/fabric_26_3/src/main/resources/assets/allmusic_server/icon.png differ 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/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/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/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 00000000..b2b5fa79 Binary files /dev/null and b/server/neoforge_1_21_4/src/main/resources/icon.png differ 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")) 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 00000000..b2b5fa79 Binary files /dev/null and b/server/neoforge_26_3/src/main/resources/icon.png differ 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/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java b/server/src/main/java/com/coloryr/allmusic/server/core/AllMusic.java index eced95b0..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 @@ -40,6 +40,8 @@ public class AllMusic { public static final String SERVER_DIR = "allmusic_server/"; + public static File basePath; + /** * 客户端插件信道名 */ @@ -55,11 +57,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"; /** * 日志 */ @@ -151,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; } @@ -177,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; } @@ -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); } @@ -384,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; @@ -405,11 +414,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 +431,14 @@ public static void init(File file) { cookieFile.createNewFile(); } - BanSave.init(file); - HudSave.init(file); - MusicListSave.init(file); - - loadConfig(); + BanSave.init(basePath); + HudSave.init(basePath); + MusicListSave.init(basePath); - 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 b433d781..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 @@ -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()); @@ -72,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; @@ -87,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)) { @@ -104,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,11 +117,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.isDefault = isDefault; + obj.args = newArgs; obj.api = apiname; if (AllMusic.side.onMusicAdd(sender, obj)) { @@ -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().maxPlayList) { - 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); @@ -221,7 +224,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)) { @@ -279,7 +281,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 new file mode 100644 index 00000000..39ff05a0 --- /dev/null +++ b/server/src/main/java/com/coloryr/allmusic/server/core/command/sub/CommandAgree.java @@ -0,0 +1,42 @@ +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; + +import java.util.Locale; + +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(sender, AllMusic.getMessage().vote.err4); + return; + } + if (vote.votePlayer.contains(name.toLowerCase(Locale.ROOT))) { + AllMusic.side.sendMessage(sender, AllMusic.getMessage().vote.err5); + return; + } + + 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())) + .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/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/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..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 @@ -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; @@ -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) { @@ -32,22 +36,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.err5); + } + + 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 +70,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.err5); + } + + 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/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 e3da747f..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 @@ -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) { @@ -15,21 +16,25 @@ 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) { 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(sender, AllMusic.getMessage().push.cancel1); } else { AllMusic.side.sendMessage(sender, AllMusic.getMessage().command.error); } @@ -37,27 +42,14 @@ 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); + 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); } 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/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 dd62dfde..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 @@ -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,157 @@ 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 用户名 + * @param vote 投票内容 */ - public static void addPush(String player) { - player = player.toLowerCase(); - pushPlayer.add(player); - } + public static boolean startVote(VoteItem vote) { + String id = vote.getId(); + String api = vote.getApi(); - public static void startPush(String player, SongInfoObj music) { - player = player.toLowerCase(); - push = music; - pushSender = player; - pushPlayer.add(player); - pushTime = AllMusic.getConfig().voteTime; + 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; + } + } + + voteList.add(vote); + voteTime = AllMusic.getConfig().vote.voteTime; + + return true; } - public static void pushTick() { - pushTime--; + public static SongInfoObj getMusic(String id, String api) { + for (SongInfoObj item : playList) { + if (item.getId().equalsIgnoreCase(id) && item.getApi().equalsIgnoreCase(api)) { + return item; + } + } + + return null; } - public static void voteTick() { - voteTime--; + 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 SongInfoObj getPush() { - return push; + public static List cloneVoteList() { + return new ArrayList<>(voteList); } - public static int getPushTime() { - return pushTime; + public static void removeVote(VoteItem vote) { + voteList.remove(vote); } - public static String getPushSender() { - return pushSender; + public static boolean fullVoteList() { return voteList.size() >= AllMusic.getConfig().vote.voteListSize; } + + 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; } /** @@ -221,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); @@ -231,11 +258,8 @@ private static void task() { } } nowPlayPlayer.clear(); - votePlayer.clear(); - pushPlayer.clear(); playList.clear(); - clearVote(); - clearPush(); + voteList.clear(); AllMusic.log.data("歌曲处理线程关闭"); } @@ -310,17 +334,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 +433,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 +451,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; } @@ -469,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; } } @@ -557,7 +570,7 @@ public static Set getNowPlayPlayer() { */ public static boolean containNowPlay(String player) { player = player.toLowerCase(); - return !nowPlayPlayer.contains(player); + return nowPlayPlayer.contains(player); } /** @@ -586,5 +599,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 365707ea..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 @@ -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,9 @@ public static void stop() { */ private static void clear() { isPlay = false; + + checkVoteNext(); + PlayMusic.musicNowTime = 0; PlayMusic.musicAllTime = 0; PlayMusic.musicLessTime = 0; @@ -72,6 +74,38 @@ private static void clear() { HudUtils.sendHudLyricData(); } + private static void checkVoteNext() { + VoteItem vote = PlayMusic.getVote(); + 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 (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); + } + } + } + } /** * 歌曲时间定时器 @@ -108,32 +142,23 @@ 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; - } - } - - return false; + public static int getMiniVote() { + return Math.min(AllMusic.getConfig().vote.minVote, AllMusic.side.getPlayers().size()); } - 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 +170,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 +223,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 +235,6 @@ private static void musicPlayTask() { } } } - Thread.sleep(1000); } else { HudUtils.sendClearHud(); HudUtils.sendHudNowData(); @@ -211,20 +247,22 @@ private static void musicPlayTask() { continue; } + checkVotePush(); + 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 +312,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..e4fe1ffa --- /dev/null +++ b/server/src/main/java/com/coloryr/allmusic/server/core/music/VoteItem.java @@ -0,0 +1,47 @@ +package com.coloryr.allmusic.server.core.music; + +import com.coloryr.allmusic.server.core.objs.music.SongInfoObj; + +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; + } + + 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/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 801f3eb8..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 @@ -11,22 +11,6 @@ * 配置文件对象 */ public class ConfigObj { - /** - * 最大歌曲数 - */ - public int maxPlayList; - /** - * 一个玩家最大可点数量 - */ - public int maxPlayerList; - /** - * 最小通过投票数 - */ - public int minVote; - /** - * 投票时间 - */ - public int voteTime; /** * 歌曲延迟 */ @@ -83,10 +67,6 @@ public class ConfigObj { * K歌歌词 */ public boolean ktvMode; - /** - * 歌曲音质 - */ - public String musicBR; /** * 配置文件版本号 */ @@ -107,6 +87,10 @@ public class ConfigObj { * 花费点歌 */ public CostObj cost; + /** + * 投票配置 + */ + public VoteObj vote; /** * 默认音乐API */ @@ -141,7 +125,7 @@ public boolean check() { saveConfig = true; adminList = new HashSet<>(); } - if (economy == null || economy.check()) { + if (economy == null) { saveConfig = true; economy = EconomyObj.make(); } @@ -157,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<>(); @@ -166,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; @@ -188,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/EconomyObj.java b/server/src/main/java/com/coloryr/allmusic/server/core/objs/config/EconomyObj.java index 98c0b7c6..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 @@ -1,26 +1,13 @@ package com.coloryr.allmusic.server.core.objs.config; public class EconomyObj { - // public String mysqlUrl; public String backend; public boolean vault; public static EconomyObj make() { EconomyObj obj = new EconomyObj(); - obj.init(); - + obj.backend = "server1"; + obj.vault = true; return obj; } - - 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; - } } 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/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/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/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..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 @@ -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) + agree = "[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..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 @@ -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 cancel2; public String err1; - public String err2; - public String err3; + public String err4; + public String err5; public static PushObj make() { PushObj obj = new PushObj(); @@ -38,29 +35,23 @@ 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) + if (err4 == null) + return true; + if (err5 == null) + return true; + if (cancel1 == null) return true; - if (err3 == null) + if (cancel2 == null) return true; return doPush == null; } @@ -76,31 +67,25 @@ 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]已删除你发起的投票"; + if (cancel2 == null) + cancel2 = "[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..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,13 +8,16 @@ public class VoteObj { public String bq2; public String agree; 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 err6; public String cancel; + public String cancel1; + public String list; public static VoteObj make() { VoteObj obj = new VoteObj(); @@ -38,19 +41,25 @@ public boolean check() { return true; if (bqAgree == null) return true; - if (arAgree == null) - return true; if (timeOut == 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 (err6 == 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 + "名玩家同意才会切歌。"; - if (arAgree == null) - arAgree = "[AllMusic]你已申请切歌"; + bqAgree = "[AllMusic]" + ARG.player + "同意投票,共有" + ARG.count + "名玩家同意投票,需要至少" + ARG.countAll + "名玩家同意才会通过。"; 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 (err6 == null) + err6 = "[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/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/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; } 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/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<>(); diff --git a/server/velocity_26_3/build.gradle.kts b/server/velocity_26_3/build.gradle.kts new file mode 100644 index 00000000..607e1d58 --- /dev/null +++ b/server/velocity_26_3/build.gradle.kts @@ -0,0 +1,34 @@ +java.sourceCompatibility = JavaVersion.VERSION_25 +java.targetCompatibility = JavaVersion.VERSION_25 + +repositories { + // 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/") +} + +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 27136c01..5e183301 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,73 +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: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(":server") - -include(":server:fabric_1_16_5") -include(":server:fabric_1_20_1") -include(":server:fabric_1_21") -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_6") -include(":server:neoforge_1_21_11") -include(":server:neoforge_26_1") -include(":server:neoforge_26_2") - -include(":server:spigot") -include(":server:paper") -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") +// 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. +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 {