Compare commits

..

No commits in common. "main" and "velocity" have entirely different histories.

31 changed files with 175 additions and 1062 deletions

View File

@ -1,40 +0,0 @@
# Automatically build the project and run any configured tests for every push
# and submitted pull request. This can help catch issues that only occur on
# certain platforms or Java versions, and provides a first line of defence
# against bad commits.
name: build
on: [ pull_request, push ]
jobs:
build:
strategy:
matrix:
# Use these Java versions
java: [
17, # Current Java LTS & minimum supported by Minecraft
]
# and run on both Linux and Windows
os: [ ubuntu-22.04, windows-2022 ]
runs-on: ${{ matrix.os }}
steps:
- name: checkout repository
uses: actions/checkout@v3
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: 'microsoft'
- name: make gradle wrapper executable
if: ${{ runner.os != 'Windows' }}
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS
uses: actions/upload-artifact@v3
with:
name: Artifacts
path: build/libs/

134
.gitignore vendored
View File

@ -1,40 +1,118 @@
# gradle
.gradle/
build/
out/
classes/
# eclipse
*.launch
# idea
# User-specific stuff
.idea/
*.iml
*.ipr
*.iws
# vscode
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
.settings/
.vscode/
bin/
.classpath
.project
# JIRA plugin
atlassian-ide-plugin.xml
# macos
# Compiled class file
*.class
*.DS_Store
# Log file
*.log
# fabric
# BlueJ files
*.ctxt
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db
# Dump file
*.stackdump
# Folder config file
[Dd]esktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp
# Windows shortcuts
*.lnk
.gradle
build/
# Ignore Gradle GUI config
gradle-app.setting
# Cache of project
.gradletasknamecache
**/build/
# Common working directory
run/
# java
hs_err_*.log
replay_*.log
*.hprof
*.jfr
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

121
LICENSE
View File

@ -1,121 +0,0 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -1 +1 @@
### DreamRunner Carpet Addition
### DreamRunner VelocityAddition

View File

@ -1,100 +1,55 @@
plugins {
id 'fabric-loom' version '1.3-SNAPSHOT'
id 'maven-publish'
id 'java'
id 'eclipse'
id "org.jetbrains.gradle.plugin.idea-ext" version "1.0.1"
}
version = project.mod_version
group = project.maven_group
base {
archivesName = project.archives_base_name
}
group = 'cn.wzpmc'
version = '1.0-SNAPSHOT'
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
mavenCentral()
maven {
url 'https://masa.dy.fi/maven'
name = "papermc-repo"
url = "https://repo.papermc.io/repository/maven-public/"
}
maven {
name = "CottonMC"
url = "https://server.bbkr.space/artifactory/libs-release"
}
}
loom {
splitEnvironmentSourceSets()
mods {
"music" {
sourceSet sourceSets.main
sourceSet sourceSets.client
}
}
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "carpet:fabric-carpet:1.20-${project.carpet_core_version}"
modClientImplementation "io.github.cottonmc:LibGui:8.0.2+1.20"
// Uncomment the following line to enable the deprecated Fabric API modules.
// These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time.
// https://mvnrepository.com/artifact/javazoom/jlayer
implementation 'javazoom:jlayer:1.0.1'
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
compileOnly "com.velocitypowered:velocity-api:3.2.0-SNAPSHOT"
annotationProcessor "com.velocitypowered:velocity-api:3.2.0-SNAPSHOT"
}
processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": project.version
def targetJavaVersion = 11
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 17
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
jar {
from("LICENSE") {
rename { "${it}_${project.base.archivesName.get()}" }
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
options.release = targetJavaVersion
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
def templateSource = file('src/main/templates')
def templateDest = layout.buildDirectory.dir('generated/sources/templates')
def generateTemplates = tasks.register('generateTemplates', Copy) { task ->
def props = [
'version': project.version
]
task.inputs.properties props
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
task.from templateSource
task.into templateDest
task.expand props
}
sourceSets.main.java.srcDir(generateTemplates.map { it.outputs })
rootProject.idea.project.settings.taskTriggers.afterSync generateTemplates
project.eclipse.synchronizationTasks(generateTemplates)

View File

@ -1,15 +1 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10
loader_version=0.14.21
carpet_core_version=1.4.112+v230608
# Mod Properties
mod_version=1.0.0
maven_group=cn.wzpmc
archives_base_name=der
# Dependencies
fabric_version=0.86.1+1.20.1
javaVersion=11

View File

@ -1,10 +1 @@
pluginManagement {
repositories {
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
}
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = 'DreamRunnerVelocityAddition'

View File

@ -1,83 +0,0 @@
package cn.wzpmc;
import cn.wzpmc.entities.SongObject;
import cn.wzpmc.gui.OnWorldTickStart;
import io.netty.buffer.ByteBuf;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Environment(EnvType.CLIENT)
public class DreamRunnerModClient implements ClientModInitializer {
public static final Identifier packageChannelId = Objects.requireNonNull(Identifier.of("der", "music_channel"));
public static OnWorldTickStart worldTickStart = new OnWorldTickStart();
public static List<SongObject> musics = new ArrayList<>();
@Override
public void onInitializeClient() {
ClientPlayNetworking.registerGlobalReceiver(packageChannelId, (client, handler, buf, responseSender) -> packetHandler(client, buf));
ClientTickEvents.START_CLIENT_TICK.register(worldTickStart);
}
// Packet Struct:
// SongName Length
// SongName
// Artist List Length
// n Artist
// Artist Name Length
// Artist Name
// Pic Length
// Pic Byte Array
// Song Length
// Song Byte Array
public void packetHandler(MinecraftClient client, ByteBuf buf) {
int duringTime = buf.readInt();
int songNameLength = buf.readInt();
byte[] data = new byte[songNameLength];
buf.readBytes(data);
String songName = new String(data, StandardCharsets.UTF_8);
StringBuilder artistsBuilder = new StringBuilder();
int artistListLength = buf.readInt();
for (int i = 0; i < artistListLength; i++) {
int artistNameLength = buf.readInt();
data = new byte[artistNameLength];
buf.readBytes(data);
String artistName = new String(data, StandardCharsets.UTF_8);
artistsBuilder.append(artistName).append(',');
}
artistsBuilder.deleteCharAt(artistsBuilder.lastIndexOf(","));
String artistNames = artistsBuilder.toString();
int lrcLength = buf.readInt();
data = new byte[lrcLength];
buf.readBytes(data);
String lrc = new String(data, StandardCharsets.UTF_8);
int picByteArrayLength = buf.readInt();
byte[] picByteArray = new byte[picByteArrayLength];
buf.readBytes(picByteArray);
int songByteArrayLength = buf.readInt();
byte[] songByteArray = new byte[songByteArrayLength];
buf.readBytes(songByteArray);
SongObject songObject;
try {
songObject = new SongObject(client, songName, artistNames, duringTime, picByteArray, songByteArray, lrc);
} catch (IOException e) {
DreamRunnerMod.LOGGER.error("读取图片时出现错误", e);
throw new RuntimeException(e);
}
if (musics.isEmpty()) {
songObject.playSong();
}
musics.add(songObject);
}
}

View File

@ -1,13 +0,0 @@
package cn.wzpmc.configuration;
public class GuiConfiguration {
public static final int coverWidth = 64;
public static final int coverHeight = 64;
public static final int baseX = 8;
public static final int baseY = 8;
public static final int backgroundColor = 0x32_FFFFFF;
public static final int listBackgroundColor = 0x32_FFFFFF;
public static final int textColor = 0xFF_FFFFFF;
public static final int progressBackgroundColor = 0x48_6754E6;
public static final int progressColor = 0x5F_6754E6;
}

View File

@ -1,121 +0,0 @@
package cn.wzpmc.entities;
import cn.wzpmc.configuration.GuiConfiguration;
import cn.wzpmc.mixin.SoundManagerMixin;
import cn.wzpmc.mixin.SoundSystemMixin;
import cn.wzpmc.sounds.MusicSoundInstance;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.sound.SoundManager;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.NativeImageBackedTexture;
import net.minecraft.util.Identifier;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.*;
@Environment(EnvType.CLIENT)
public class SongObject {
private final String songName;
private final MusicSoundInstance soundInstance;
private final String artists;
private final Identifier imageId;
private final LinkedHashMap<Long, String> lyrics = new LinkedHashMap<>();
private final long totalDuringTime;
private final MinecraftClient minecraftClient;
private long nowSongDuringTime;
private int startTick = -1;
public SongObject(MinecraftClient minecraftClient, String songName, String artists, long totalDuringTime, byte[] picByteArray, byte[] songByteArray, String lyrics) throws IOException {
this.minecraftClient = minecraftClient;
this.songName = songName;
this.soundInstance = new MusicSoundInstance(minecraftClient.player, new ByteArrayInputStream(songByteArray), this);
this.artists = artists;
this.nowSongDuringTime = 0;
this.totalDuringTime = totalDuringTime;
// 图片处理部分
BufferedImage originalImage = ImageIO.read(new ByteArrayInputStream(picByteArray));
BufferedImage resizedImage = new BufferedImage(GuiConfiguration.coverWidth, GuiConfiguration.coverHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = resizedImage.createGraphics();
g2d.drawImage(originalImage, 0, 0, GuiConfiguration.coverWidth, GuiConfiguration.coverHeight, null);
g2d.dispose();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(resizedImage, "png", byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
NativeImage image = NativeImage.read(new ByteArrayInputStream(byteArray));
NativeImageBackedTexture backedTexture = new NativeImageBackedTexture(image);
this.imageId = minecraftClient.getTextureManager().registerDynamicTexture("der", backedTexture);
// 歌词处理部分
String[] lineSplit = lyrics.split("\n");
for (String fileLine : lineSplit) {
String[] timeLrcSplit = fileLine.split("]");
String time = timeLrcSplit[0].replace("[", "");
String[] msSplit = time.split("\\.");
String[] minSecSplit = msSplit[0].split(":");
long min = Long.parseLong(minSecSplit[0]);
long sec = Long.parseLong(minSecSplit[1]);
long ms = Long.parseLong(msSplit[1]);
StringBuilder line = new StringBuilder();
for (int i = 1; i < timeLrcSplit.length; i++) {
line.append(timeLrcSplit[i]);
}
long lrcTime = (min * 60 + sec) * 1000 + ms;
this.lyrics.put(lrcTime, line.toString());
}
}
public void playSong() {
SoundManager soundManager = this.minecraftClient.getSoundManager();
soundManager.playNextTick(this.soundInstance);
}
public Identifier getImageId() {
return this.imageId;
}
public String getName() {
return this.songName;
}
public String getArtists() {
return this.artists;
}
public Long getNowDuringTime() {
SoundManagerMixin soundManager = (SoundManagerMixin) this.minecraftClient.getSoundManager();
SoundSystemMixin soundSystem = (SoundSystemMixin) soundManager.getSoundSystem();
if (startTick == -1 && soundSystem.getSoundEndTick().containsKey(this.soundInstance)) {
startTick = soundSystem.getTicks();
}
int ticks = soundSystem.getTicks() - startTick;
this.nowSongDuringTime = ticks * 50L;
return this.nowSongDuringTime;
}
public Long getTotalDuringTime() {
return this.totalDuringTime;
}
public String getLrc() {
List<Map.Entry<Long, String>> entries = new ArrayList<>(this.lyrics.entrySet());
entries.sort(Map.Entry.comparingByKey(Comparator.reverseOrder()));
for (Map.Entry<Long, String> timeLyrics : entries) {
Long time = timeLyrics.getKey();
if (time <= this.nowSongDuringTime) {
return timeLyrics.getValue();
}
}
return "";
}
public boolean isDone() {
return this.nowSongDuringTime >= this.totalDuringTime;
}
}

View File

@ -1,61 +0,0 @@
package cn.wzpmc.gui;
import cn.wzpmc.DreamRunnerModClient;
import cn.wzpmc.configuration.GuiConfiguration;
import cn.wzpmc.entities.SongObject;
import cn.wzpmc.utils.TimeUtils;
import io.github.cottonmc.cotton.gui.client.ScreenDrawing;
import io.github.cottonmc.cotton.gui.widget.WWidget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
@Environment(EnvType.CLIENT)
public class NowSongDetailWeight extends WWidget {
private static int targetWidth = 0;
private static double nowWidth = 0d;
private static double speed = 0d;
private final MinecraftClient client;
public NowSongDetailWeight() {
this.client = MinecraftClient.getInstance();
}
public static void reset() {
targetWidth = 0;
nowWidth = 0d;
speed = 0d;
}
private static final int textBaseX = GuiConfiguration.coverWidth + 2;
@Override
public void paint(DrawContext context, int x, int y, int mouseX, int mouseY) {
if (!DreamRunnerModClient.musics.isEmpty()) {
SongObject musicObject = DreamRunnerModClient.musics.get(0);
if ((int) nowWidth != targetWidth) {
nowWidth += speed;
}
speed = (targetWidth - nowWidth) / 60;
ScreenDrawing.coloredRect(context, x - 2, y - 2, (int) nowWidth, GuiConfiguration.coverHeight + 4, GuiConfiguration.backgroundColor);
context.drawTexture(musicObject.getImageId(), x, y, 0, 0, 0, GuiConfiguration.coverWidth, GuiConfiguration.coverHeight, GuiConfiguration.coverWidth, GuiConfiguration.coverHeight);
String musicName = musicObject.getName();
String artists = musicObject.getArtists();
int musicNameWidth = client.textRenderer.getWidth(musicName);
int artistsNameWidth = client.textRenderer.getWidth(artists);
Long nowDuringTime = musicObject.getNowDuringTime();
Long totalDuringTime = musicObject.getTotalDuringTime();
context.drawText(client.textRenderer, musicName, textBaseX + x, y, GuiConfiguration.textColor, false);
context.drawText(client.textRenderer, artists, textBaseX + x, client.textRenderer.fontHeight + y, GuiConfiguration.textColor, false);
ScreenDrawing.coloredRect(context, GuiConfiguration.coverWidth + 2 + x, client.textRenderer.fontHeight * 2 + y, (int) (((double) nowDuringTime / totalDuringTime) * 100), client.textRenderer.fontHeight + 4, GuiConfiguration.progressColor);
ScreenDrawing.coloredRect(context, GuiConfiguration.coverWidth + 2 + x, client.textRenderer.fontHeight * 2 + y, 100, client.textRenderer.fontHeight + 4, GuiConfiguration.progressBackgroundColor);
context.drawText(client.textRenderer, TimeUtils.getTimeString(nowDuringTime) + " / " + TimeUtils.getTimeString(totalDuringTime), GuiConfiguration.coverWidth + 4 + x, client.textRenderer.fontHeight * 2 + 2 + y, GuiConfiguration.textColor, false);
String lrc = musicObject.getLrc();
context.drawText(client.textRenderer, lrc, textBaseX + x, client.textRenderer.fontHeight * 3 + 4 + y, GuiConfiguration.textColor, false);
int lrcWidth = client.textRenderer.getWidth(lrc);
targetWidth = Math.max(Math.max(Math.max(musicNameWidth, artistsNameWidth), lrcWidth), 100) + GuiConfiguration.coverWidth + 6;
} else {
reset();
}
}
}

View File

@ -1,53 +0,0 @@
package cn.wzpmc.gui;
import cn.wzpmc.DreamRunnerModClient;
import cn.wzpmc.configuration.GuiConfiguration;
import cn.wzpmc.entities.SongObject;
import io.github.cottonmc.cotton.gui.client.CottonHud;
import io.github.cottonmc.cotton.gui.widget.WWidget;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.MinecraftClient;
import java.util.ArrayList;
import java.util.List;
@Environment(EnvType.CLIENT)
public class OnWorldTickStart implements ClientTickEvents.StartTick {
private final List<WWidget> songList = new ArrayList<>();
private WWidget nowSongDetail;
@Override
public void onStartTick(MinecraftClient client) {
if (nowSongDetail != null) {
CottonHud.remove(nowSongDetail);
}
for (WWidget wWidget : songList) {
CottonHud.remove(wWidget);
}
songList.clear();
int i = 0;
for (SongObject music : DreamRunnerModClient.musics) {
if (i != 0) {
SongDetailWeight songDetailWeight = new SongDetailWeight(music);
CottonHud.add(songDetailWeight, GuiConfiguration.baseX, (i * 2) + ((i - 1) * (MinecraftClient.getInstance().textRenderer.fontHeight + 4)) + GuiConfiguration.coverHeight + GuiConfiguration.baseY + 4);
songList.add(songDetailWeight);
}
i++;
}
nowSongDetail = new NowSongDetailWeight();
CottonHud.add(nowSongDetail, GuiConfiguration.baseX + 2, GuiConfiguration.baseY + 2);
}
public void remove() {
if (nowSongDetail != null) {
CottonHud.remove(nowSongDetail);
}
nowSongDetail = null;
for (WWidget wWidget : songList) {
CottonHud.remove(wWidget);
}
songList.clear();
}
}

View File

@ -1,25 +0,0 @@
package cn.wzpmc.gui;
import cn.wzpmc.configuration.GuiConfiguration;
import cn.wzpmc.entities.SongObject;
import io.github.cottonmc.cotton.gui.client.ScreenDrawing;
import io.github.cottonmc.cotton.gui.widget.WWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
public class SongDetailWeight extends WWidget {
private final SongObject songObject;
private final MinecraftClient minecraftClient;
public SongDetailWeight(SongObject songObject) {
this.songObject = songObject;
this.minecraftClient = MinecraftClient.getInstance();
}
@Override
public void paint(DrawContext context, int x, int y, int mouseX, int mouseY) {
int fontHeight = this.minecraftClient.textRenderer.fontHeight;
ScreenDrawing.coloredRect(context, x, y, GuiConfiguration.coverWidth + 106, fontHeight + 4, GuiConfiguration.listBackgroundColor);
context.drawText(this.minecraftClient.textRenderer, this.songObject.getName() + '-' + this.songObject.getArtists(), x + 2, y + 2, GuiConfiguration.textColor, false);
}
}

View File

@ -1,17 +0,0 @@
package cn.wzpmc.mixin;
import cn.wzpmc.DreamRunnerModClient;
import net.minecraft.client.gui.screen.GameMenuScreen;
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(GameMenuScreen.class)
public class GameMenuScreenMixin {
@Inject(method = "disconnect", at = @At("HEAD"))
private void disconnect(CallbackInfo ci) {
DreamRunnerModClient.worldTickStart.remove();
DreamRunnerModClient.musics.clear();
}
}

View File

@ -1,12 +0,0 @@
package cn.wzpmc.mixin;
import net.minecraft.client.sound.SoundManager;
import net.minecraft.client.sound.SoundSystem;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(SoundManager.class)
public interface SoundManagerMixin {
@Accessor("soundSystem")
SoundSystem getSoundSystem();
}

View File

@ -1,17 +0,0 @@
package cn.wzpmc.mixin;
import net.minecraft.client.sound.SoundInstance;
import net.minecraft.client.sound.SoundSystem;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(SoundSystem.class)
public interface SoundSystemMixin {
@Accessor("soundEndTicks")
Map<SoundInstance, Integer> getSoundEndTick();
@Accessor("ticks")
int getTicks();
}

View File

@ -1,67 +0,0 @@
package cn.wzpmc.sounds;
import cn.wzpmc.DreamRunnerMod;
import cn.wzpmc.entities.SongObject;
import javazoom.jl.decoder.BitstreamException;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.sound.*;
import net.minecraft.sound.SoundCategory;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.CompletableFuture;
@Environment(EnvType.CLIENT)
public class MusicSoundInstance extends AbstractSoundInstance implements TickableSoundInstance {
private final ClientPlayerEntity entity;
private final InputStream inputStream;
private final SongObject songObject;
public MusicSoundInstance(ClientPlayerEntity entity, InputStream inputStream, SongObject songObject) {
super(new Identifier("der", "music"), SoundCategory.RECORDS, SoundInstance.createRandom());
this.entity = entity;
this.inputStream = inputStream;
this.songObject = songObject;
}
@Override
public double getX() {
this.x = this.entity.getX();
return this.x;
}
@Override
public double getY() {
this.y = this.entity.getY();
return this.y;
}
@Override
public double getZ() {
this.z = this.entity.getZ();
return this.z;
}
@Override
public CompletableFuture<AudioStream> getAudioStream(SoundLoader loader, Identifier id, boolean repeatInstantly) {
try {
MyAudioStream myAudioStream = new MyAudioStream(inputStream, songObject);
return CompletableFuture.completedFuture(myAudioStream);
} catch (IOException | BitstreamException e) {
DreamRunnerMod.LOGGER.error("读取音频文件出错!", e);
}
return null;
}
@Override
public boolean isDone() {
return this.songObject.isDone();
}
@Override
public void tick() {
}
}

View File

@ -1,81 +0,0 @@
package cn.wzpmc.sounds;
import cn.wzpmc.DreamRunnerMod;
import cn.wzpmc.DreamRunnerModClient;
import cn.wzpmc.entities.SongObject;
import javazoom.jl.decoder.*;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.sound.AudioStream;
import javax.sound.sampled.AudioFormat;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@Environment(EnvType.CLIENT)
public class MyAudioStream implements AudioStream {
private final Bitstream bitstream;
private final Decoder decoder;
private final AudioFormat format;
private final SongObject songObject;
public MyAudioStream(InputStream inputStream, SongObject songObject) throws BitstreamException, IOException {
this.bitstream = new Bitstream(inputStream);
this.decoder = new Decoder();
this.songObject = songObject;
Header header = bitstream.readFrame();
if (header == null) {
throw new IOException("读取MP3文件头失败");
}
this.format = new AudioFormat(header.frequency(), 16, header.mode() == Header.SINGLE_CHANNEL ? 1 : 2, true, false);
bitstream.unreadFrame();
}
@Override
public void close() {
try {
this.bitstream.close();
} catch (Exception e) {
DreamRunnerMod.LOGGER.error("关闭资源失败!", e);
}
DreamRunnerModClient.musics.remove(this.songObject);
if (DreamRunnerModClient.musics.isEmpty()) {
return;
}
SongObject nextSong = DreamRunnerModClient.musics.get(0);
nextSong.playSong();
}
@Override
public AudioFormat getFormat() {
return this.format;
}
@Override
public ByteBuffer getBuffer(int size) {
ByteBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder());
try {
while (buffer.hasRemaining()) {
Header frameHeader = bitstream.readFrame();
if (frameHeader == null) {
break;
}
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(frameHeader, bitstream);
int bufferLength = output.getBufferLength();
short[] pcm = output.getBuffer();
for (int i = 0; i < bufferLength; i++) {
if (!buffer.hasRemaining()) {
break;
}
buffer.putShort(pcm[i]);
}
bitstream.closeFrame();
}
} catch (Exception e) {
DreamRunnerMod.LOGGER.error("读取音频文件失败!", e);
return null;
}
buffer.flip();
return buffer;
}
}

View File

@ -1,10 +0,0 @@
package cn.wzpmc.utils;
public class TimeUtils {
public static String getTimeString(Long time) {
long secDuringTime = time / 1000;
long sec = secDuringTime % 60;
long min = secDuringTime / 60;
return String.format("%d:%02d", min, sec);
}
}

View File

@ -1,15 +0,0 @@
{
"required": true,
"package": "cn.wzpmc.mixin",
"compatibilityLevel": "JAVA_17",
"client": [
"SoundManagerMixin",
"SoundSystemMixin"
],
"injectors": {
"defaultRequire": 1
},
"mixins": [
"GameMenuScreenMixin"
]
}

View File

@ -1,14 +0,0 @@
package cn.wzpmc;
import carpet.api.settings.Rule;
import carpet.api.settings.RuleCategory;
public class CarpetSettings {
/**
* Disable Out of order detect
*/
@Rule(categories = {RuleCategory.BUGFIX})
public static boolean fuckOutOfOrderChat = false;
@Rule(categories = {RuleCategory.BUGFIX})
public static boolean clearBotScoreBoard = true;
}

View File

@ -1,82 +0,0 @@
package cn.wzpmc;
import carpet.CarpetExtension;
import carpet.CarpetServer;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.fabricmc.api.ModInitializer;
import net.minecraft.scoreboard.ScoreboardObjective;
import net.minecraft.scoreboard.ScoreboardPlayerScore;
import net.minecraft.scoreboard.ServerScoreboard;
import net.minecraft.server.MinecraftServer;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
public class DreamRunnerMod implements ModInitializer, CarpetExtension {
// This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name.
// That way, it's clear which mod wrote info, warnings, and errors.
public static final Logger LOGGER = LoggerFactory.getLogger("DreamRunnerCarpetAddition");
static {
System.out.println();
CarpetServer.manageExtension(new DreamRunnerMod());
}
@Override
public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
LOGGER.info("Fuck you out of order chat!!!");
}
@Override
public void onGameStarted() {
// let's /carpet handle our few simple settings
CarpetServer.settingsManager.parseSettingsClass(CarpetSettings.class);
}
public void onTick(MinecraftServer server) {
if (CarpetSettings.clearBotScoreBoard) {
ServerScoreboard scoreboard = server.getScoreboard();
Collection<ScoreboardObjective> objectives = scoreboard.getObjectives();
for (ScoreboardObjective objective : objectives) {
Collection<ScoreboardPlayerScore> allPlayerScores = scoreboard.getAllPlayerScores(objective);
for (ScoreboardPlayerScore allPlayerScore : allPlayerScores) {
String playerName = allPlayerScore.getPlayerName();
if (playerName.startsWith("bot_")) {
allPlayerScore.clearScore();
}
}
}
}
}
@Override
public Map<String, String> canHasTranslations(String lang) {
InputStream langFile = DreamRunnerMod.class.getClassLoader().getResourceAsStream("assets/der/lang/%s.json".formatted(lang));
if (langFile == null) {
// we don't have that language
return Collections.emptyMap();
}
String jsonData;
try {
jsonData = IOUtils.toString(langFile, StandardCharsets.UTF_8);
} catch (IOException e) {
return Collections.emptyMap();
}
Gson gson = new GsonBuilder().setLenient().create(); // lenient allows for comments
return gson.fromJson(jsonData, new TypeToken<Map<String, String>>() {
}.getType());
}
}

View File

@ -0,0 +1,24 @@
package cn.wzpmc.der;
import com.google.inject.Inject;
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.plugin.Plugin;
import org.slf4j.Logger;
@Plugin(
id = "der",
name = "DreamRunnerVelocityAddition",
version = BuildConstants.VERSION,
description = "A plugin for dreamrunner server's velocity",
authors = {"wzp"}
)
public class DreamRunnerVelocityAddition {
@Inject
private Logger logger;
@Subscribe
public void onProxyInitialization(ProxyInitializeEvent event) {
}
}

View File

@ -1,21 +0,0 @@
package cn.wzpmc.mixin;
import cn.wzpmc.CarpetSettings;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import org.jetbrains.annotations.NotNull;
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.CallbackInfoReturnable;
import java.time.Instant;
@Mixin(ServerPlayNetworkHandler.class)
public class OutOfOrderMixin {
@Inject(method = "isInProperOrder", at = @At("HEAD"), cancellable = true)
public void isInProperOrder(Instant timestamp, @NotNull CallbackInfoReturnable<Boolean> cir) {
if (CarpetSettings.fuckOutOfOrderChat) {
cir.setReturnValue(true);
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View File

@ -1,6 +0,0 @@
{
"carpet.rule.fuckOutOfOrderChat.desc": "disable out of order chat check",
"carpet.rule.fuckOutOfOrderChat.name": "Disable out of order chat check",
"carpet.rule.clearBotScoreBoard.desc": "Clear bot's scoreboard score every tick",
"carpet.rule.clearBotScoreBoard.name": "Clear bot's scoreboard"
}

View File

@ -1,6 +0,0 @@
{
"carpet.rule.fuckOutOfOrderChat.desc": "禁用消息乱序检查",
"carpet.rule.fuckOutOfOrderChat.name": "禁用消息乱序检查",
"carpet.rule.clearBotScoreBoard.desc": "每Tick清除bot计分板",
"carpet.rule.clearBotScoreBoard.name": "清除bot计分板"
}

View File

@ -1,10 +0,0 @@
{
"music": {
"sounds": [
{
"name": "fabric-sound-api-v1:empty",
"stream": true
}
]
}
}

View File

@ -1,11 +0,0 @@
{
"required": true,
"package": "cn.wzpmc.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"OutOfOrderMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -1,42 +0,0 @@
{
"schemaVersion": 1,
"id": "der",
"version": "${version}",
"name": "DreamRunnerCarpetAddition",
"description": "An Carpet Addition Mod For DreamRunner Server",
"authors": [
"wzpMC"
],
"contact": {
"homepage": "https://fabricmc.net/",
"sources": "https://github.com/FabricMC/fabric-example-mod"
},
"license": "CC0-1.0",
"icon": "assets/der/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"cn.wzpmc.DreamRunnerMod"
],
"client": [
"cn.wzpmc.DreamRunnerModClient"
]
},
"mixins": [
"der.mixins.json",
{
"config": "der.client.mixins.json",
"environment": "client"
}
],
"depends": {
"fabricloader": ">=0.14.21",
"minecraft": "~1.20.1",
"java": ">=17",
"fabric-api": "*",
"carpet": ">=1.4.45+v210811"
},
"suggests": {
"another-mod": "*"
}
}

View File

@ -0,0 +1,7 @@
package cn.wzpmc.der;
// The constants are replaced before compilation
public class BuildConstants {
public static final String VERSION = "${version}";
}