mirror of
https://github.com/Coloryr/AllMusic.git
synced 2026-11-08 18:54:44 +00:00
fixed #117
This commit is contained in:
1 parent
2a410060b9
commit
d61354991e
18 files changed
+1695
No files matched your search
@@ -0,0 +1,35 @@
|
||||
plugins {
|
||||
id("fabric-loom") version Versions.fabricLoom
|
||||
}
|
||||
|
||||
java.sourceCompatibility = JavaVersion.VERSION_21
|
||||
java.targetCompatibility = JavaVersion.VERSION_21
|
||||
|
||||
dependencies {
|
||||
minecraft("com.mojang:minecraft:1.21.8")
|
||||
mappings(loom.officialMojangMappings())
|
||||
modImplementation("net.fabricmc:fabric-loader:0.19.3")
|
||||
|
||||
modImplementation("net.fabricmc.fabric-api:fabric-api:0.136.1+1.21.8")
|
||||
}
|
||||
|
||||
tasks {
|
||||
processResources {
|
||||
filesMatching("fabric.mod.json") {
|
||||
expand(
|
||||
"version" to project.version
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
remapJar {
|
||||
inputFile.set(shadowJar.get().archiveFile)
|
||||
|
||||
archiveFileName.set("[fabric-1.21.6]AllMusic_Client-${project.version}.jar")
|
||||
destinationDirectory.set(file("${parent!!.projectDir}/../build"))
|
||||
}
|
||||
|
||||
build {
|
||||
dependsOn(remapJar)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
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.networking.v1.ClientPlayNetworking;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
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 ResourceLocation ID = ResourceLocation.fromNamespaceAndPath("allmusic", "channel");
|
||||
public static final Logger LOGGER = LogManager.getLogger("AllMusic Client");
|
||||
public static GuiGraphics context;
|
||||
|
||||
public static void update(GuiGraphics 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.displayClientMessage(Component.literal(finalData), false);
|
||||
});
|
||||
}
|
||||
|
||||
public float getVolume() {
|
||||
return Minecraft.getInstance().options.getSoundSourceVolume(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 {
|
||||
Resource resource = Minecraft.getInstance().getResourceManager().getResource(ResourceLocation.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 {
|
||||
Resource resource = Minecraft.getInstance().getResourceManager().getResource(ResourceLocation.fromNamespaceAndPath(MODID, file)).orElseThrow();
|
||||
return resource.open();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
ClientPlayNetworking.registerGlobalReceiver(MusicCodec.ID, (pack, handler) -> {
|
||||
AllMusicCore.packDo(pack.pack());
|
||||
});
|
||||
|
||||
AllMusicCore.init(FabricLoader.getInstance().getConfigDir(), this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
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 com.mojang.blaze3d.ProjectionType;
|
||||
import com.mojang.blaze3d.buffers.GpuBuffer;
|
||||
import com.mojang.blaze3d.buffers.GpuBufferSlice;
|
||||
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.pipeline.TextureTarget;
|
||||
import com.mojang.blaze3d.platform.Window;
|
||||
import com.mojang.blaze3d.systems.RenderPass;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.ByteBufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.MeshData;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.gui.font.glyphs.BakedGlyph;
|
||||
import net.minecraft.client.gui.navigation.ScreenRectangle;
|
||||
import net.minecraft.client.gui.render.TextureSetup;
|
||||
import net.minecraft.client.gui.render.state.GlyphEffectRenderState;
|
||||
import net.minecraft.client.gui.render.state.GlyphRenderState;
|
||||
import net.minecraft.client.gui.render.state.GuiElementRenderState;
|
||||
import net.minecraft.client.gui.render.state.GuiRenderState;
|
||||
import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer;
|
||||
import net.minecraft.client.renderer.MappableRingBuffer;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
import net.minecraft.client.renderer.fog.FogRenderer;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.joml.Matrix3x2f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CoreRenderTarget extends TextFrameBuffer {
|
||||
private final RenderTarget target;
|
||||
private final String name;
|
||||
|
||||
private final GuiRenderer renderer;
|
||||
private final FogRenderer fogRenderer = new FogRenderer();
|
||||
private final GuiRenderState renderState = new GuiRenderState();
|
||||
|
||||
public CoreRenderTarget(String name) {
|
||||
this.name = name;
|
||||
target = new TextureTarget(name, 800, 200, false);
|
||||
renderer = new GuiRenderer(renderState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
Window window = Minecraft.getInstance().getWindow();
|
||||
|
||||
nowWidth = width * window.getGuiScale();
|
||||
nowHeight = height * window.getGuiScale();
|
||||
|
||||
if (nowWidth > target.width || nowHeight > target.height) {
|
||||
target.resize(nowWidth, nowHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void use() {
|
||||
isDraw = true;
|
||||
|
||||
clear();
|
||||
RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unUse() {
|
||||
renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this);
|
||||
isDraw = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawText(String text, int y, int color, boolean shadow) {
|
||||
Window window = Minecraft.getInstance().getWindow();
|
||||
|
||||
var font = Minecraft.getInstance().font;
|
||||
Component component = MiniMessage.parse(text);
|
||||
int width = font.width(component.getVisualOrderText());
|
||||
|
||||
if (width == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GuiGraphics graphics = new GuiGraphics(Minecraft.getInstance(), renderState);
|
||||
color = color | 0xFF000000;
|
||||
graphics.drawString(font, component, 0, y, color, shadow);
|
||||
|
||||
TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale());
|
||||
texts.add(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染贴图的一部分到屏幕指定位置
|
||||
*
|
||||
* @param alpha 透明度
|
||||
* @param x 屏幕X坐标(左上角)
|
||||
* @param y 屏幕Y坐标(左上角)
|
||||
* @param width 需要渲染的宽度
|
||||
* @param height 需要渲染的高度
|
||||
* @param texX 贴图左上角X坐标
|
||||
* @param texY 贴图左上角Y坐标
|
||||
* @param scale 贴图缩放
|
||||
*/
|
||||
private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) {
|
||||
float w = width / 2;
|
||||
float h = height / 2;
|
||||
|
||||
Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h);
|
||||
|
||||
float x0 = -w;
|
||||
float x1 = w;
|
||||
float y0 = -h;
|
||||
float y1 = h;
|
||||
|
||||
// 计算贴图区域UV
|
||||
float u0 = texX * scale / target.width;
|
||||
float v0 = 1 - (texY * scale / target.height);
|
||||
float u1 = (texX + width) * scale / target.width;
|
||||
float v1 = 1 - ((texY + height) * scale / target.height);
|
||||
|
||||
int color = 0xFFFFFF00 + (int) (255 * alpha);
|
||||
|
||||
AllMusicClient.context.guiRenderState.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED,
|
||||
TextureSetup.singleTexture(target.getColorTextureView()), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek()));
|
||||
}
|
||||
|
||||
public void drawLoop(float alpha, float x, float y,
|
||||
float texX, float texY,
|
||||
float textWidth, float textHeight,
|
||||
int maxWidth, float offsetX, float scale) {
|
||||
|
||||
// 如果宽度不大于最大宽度,直接全部渲染
|
||||
if (maxWidth == -1 || textWidth <= maxWidth) {
|
||||
draw(alpha, x, y, textWidth, textHeight, texX, texY, scale);
|
||||
return;
|
||||
}
|
||||
|
||||
if (textWidth - offsetX < maxWidth) {
|
||||
float nowWith = textWidth - offsetX;
|
||||
draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale);
|
||||
draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale);
|
||||
} else {
|
||||
draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 居中百分比显示(根据百分比选择显示贴图的中间部分)
|
||||
*
|
||||
* @param alpha 透明度
|
||||
* @param startX 起始X坐标(屏幕左上角)
|
||||
* @param startY 起始Y坐标(屏幕左上角)
|
||||
* @param texX 贴图左上角X
|
||||
* @param texY 贴图左上角Y
|
||||
* @param texWidth 贴图总宽度
|
||||
* @param texHeight 贴图高度
|
||||
* @param maxWidth 最大渲染宽度
|
||||
* @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边
|
||||
*/
|
||||
public void drawByPercent(float alpha, float startX, float startY,
|
||||
float texX, float texY,
|
||||
float texWidth, float texHeight,
|
||||
int maxWidth, float percent, float scale) {
|
||||
|
||||
// 限制百分比范围
|
||||
percent = Math.clamp(percent, 0.0f, 1.0f);
|
||||
|
||||
// 如果贴图宽度小于等于最大宽度,直接全部显示
|
||||
if (texWidth <= maxWidth) {
|
||||
draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算贴图的起始位置(根据百分比)
|
||||
float maxOffset = texWidth - maxWidth;
|
||||
float texOffset = maxOffset * percent;
|
||||
|
||||
// 渲染
|
||||
draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) {
|
||||
for (var item : texts) {
|
||||
Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir);
|
||||
|
||||
drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(float x, float y, float alpha, int line) {
|
||||
if (line >= texts.size()) {
|
||||
return;
|
||||
}
|
||||
TextItem item = texts.get(line);
|
||||
draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2f getLine(int line) {
|
||||
if (line >= texts.size()) {
|
||||
return new Point2f(0, 0);
|
||||
}
|
||||
TextItem item = texts.get(line);
|
||||
return new Point2f(item.textWidth, item.textHeight);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) {
|
||||
for (var item : texts) {
|
||||
Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir);
|
||||
|
||||
drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GuiRenderer implements AutoCloseable {
|
||||
private static final Comparator<ScreenRectangle> SCISSOR_COMPARATOR = Comparator.nullsFirst(
|
||||
Comparator.comparing(ScreenRectangle::top)
|
||||
.thenComparing(ScreenRectangle::bottom)
|
||||
.thenComparing(ScreenRectangle::left)
|
||||
.thenComparing(ScreenRectangle::right)
|
||||
);
|
||||
private static final Comparator<TextureSetup> TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey));
|
||||
private static final Comparator<GuiElementRenderState> ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR)
|
||||
.thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey))
|
||||
.thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR);
|
||||
private final GuiRenderState renderState;
|
||||
private final List<Draw> draws = new ArrayList<>();
|
||||
private final List<MeshToDraw> meshesToDraw = new ArrayList<>();
|
||||
private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432);
|
||||
private final Map<VertexFormat, MappableRingBuffer> vertexBuffers = new Object2ObjectOpenHashMap<>();
|
||||
private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0F, 11000.0F, true);
|
||||
private ScreenRectangle previousScissorArea;
|
||||
private RenderPipeline previousPipeline;
|
||||
private TextureSetup previousTextureSetup;
|
||||
private BufferBuilder bufferBuilder;
|
||||
|
||||
public GuiRenderer(GuiRenderState state) {
|
||||
renderState = state;
|
||||
}
|
||||
|
||||
public void render(GpuBufferSlice gpu, CoreRenderTarget target) {
|
||||
renderState.forEachText(state -> {
|
||||
var matrix3x2f = state.pose;
|
||||
var screenrectangle = state.scissor;
|
||||
state.ensurePrepared().visit(new Font.GlyphVisitor() {
|
||||
@Override
|
||||
public void acceptGlyph(BakedGlyph.GlyphInstance glyph) {
|
||||
if (glyph.glyph().textureView() != null) {
|
||||
renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2f, glyph, screenrectangle));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptEffect(BakedGlyph glyph, BakedGlyph.Effect effect) {
|
||||
if (glyph.textureView() != null) {
|
||||
renderState.submitGlyphToCurrentLayer(new GlyphEffectRenderState(matrix3x2f, glyph, effect, screenrectangle));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
renderState.sortElements(ELEMENT_SORT_COMPARATOR);
|
||||
|
||||
previousScissorArea = null;
|
||||
previousPipeline = null;
|
||||
previousTextureSetup = null;
|
||||
bufferBuilder = null;
|
||||
renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR);
|
||||
if (bufferBuilder != null) {
|
||||
recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea);
|
||||
}
|
||||
|
||||
recordDraws();
|
||||
|
||||
if (!draws.isEmpty()) {
|
||||
Minecraft minecraft = Minecraft.getInstance();
|
||||
Window window = minecraft.getWindow();
|
||||
RenderSystem.setProjectionMatrix(
|
||||
guiProjectionMatrixBuffer.getBuffer((float) target.target.width / window.getGuiScale(),
|
||||
(float) target.target.height / window.getGuiScale()),
|
||||
ProjectionType.ORTHOGRAPHIC
|
||||
);
|
||||
var rendertarget = target.target;
|
||||
int i = 0;
|
||||
|
||||
for (Draw guirenderer$draw : draws) {
|
||||
if (guirenderer$draw.indexCount > i) {
|
||||
i = guirenderer$draw.indexCount;
|
||||
}
|
||||
}
|
||||
|
||||
var buffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS);
|
||||
var gpubuffer = buffer.getBuffer(i);
|
||||
var index = buffer.type();
|
||||
var gpubufferslice = RenderSystem.getDynamicUniforms().writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F),
|
||||
new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f(), 0.0F);
|
||||
if (!meshesToDraw.isEmpty()) {
|
||||
executeDrawRange(() -> "before blur", rendertarget, gpu, gpubufferslice, gpubuffer,
|
||||
index, Math.min(meshesToDraw.size(), draws.size()), target);
|
||||
}
|
||||
}
|
||||
|
||||
for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) {
|
||||
mappableringbuffer.rotate();
|
||||
}
|
||||
|
||||
draws.clear();
|
||||
meshesToDraw.clear();
|
||||
renderState.reset();
|
||||
}
|
||||
|
||||
private void executeDrawRange(Supplier<String> name, RenderTarget target, GpuBufferSlice buffer1, GpuBufferSlice buffer2,
|
||||
GpuBuffer buffer3, VertexFormat.IndexType index, int count, CoreRenderTarget target1) {
|
||||
try (RenderPass renderpass = RenderSystem.getDevice()
|
||||
.createCommandEncoder()
|
||||
.createRenderPass(name, target.getColorTextureView(), OptionalInt.empty(),
|
||||
target.useDepth ? target.getDepthTextureView() : null, OptionalDouble.empty())) {
|
||||
RenderSystem.bindDefaultUniforms(renderpass);
|
||||
renderpass.setUniform("Fog", buffer1);
|
||||
renderpass.setUniform("DynamicTransforms", buffer2);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
var item = draws.get(i);
|
||||
executeDraw(item, renderpass, target1, buffer3, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addElementToMesh(GuiElementRenderState state, int range) {
|
||||
var pipeline = state.pipeline();
|
||||
var setup = state.textureSetup();
|
||||
var screen = state.scissorArea();
|
||||
if (pipeline != previousPipeline || scissorChanged(screen, previousScissorArea) || !setup.equals(previousTextureSetup)) {
|
||||
if (bufferBuilder != null) {
|
||||
recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea);
|
||||
}
|
||||
|
||||
bufferBuilder = new BufferBuilder(byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat());
|
||||
previousPipeline = pipeline;
|
||||
previousTextureSetup = setup;
|
||||
previousScissorArea = screen;
|
||||
}
|
||||
|
||||
state.buildVertices(bufferBuilder, 0.0F + range * 0.01F);
|
||||
}
|
||||
|
||||
private void recordMesh(BufferBuilder builder, RenderPipeline pipeline, TextureSetup texture, ScreenRectangle rectangle) {
|
||||
var meshdata = builder.buildOrThrow();
|
||||
meshesToDraw.add(new MeshToDraw(meshdata, pipeline, texture, rectangle));
|
||||
}
|
||||
|
||||
private void recordDraws() {
|
||||
var map = new Object2IntOpenHashMap<VertexFormat>();
|
||||
|
||||
for (MeshToDraw item : meshesToDraw) {
|
||||
var state = item.mesh.drawState();
|
||||
var vertexformat = state.format();
|
||||
if (!map.containsKey(vertexformat)) {
|
||||
map.put(vertexformat, 0);
|
||||
}
|
||||
|
||||
map.put(vertexformat, map.getInt(vertexformat) + state.vertexCount() * vertexformat.getVertexSize());
|
||||
}
|
||||
|
||||
for (var entry : map.object2IntEntrySet()) {
|
||||
var vertexformat = entry.getKey();
|
||||
int i = entry.getIntValue();
|
||||
var buffer = vertexBuffers.get(vertexformat);
|
||||
if (buffer == null || buffer.size() < i) {
|
||||
if (buffer != null) {
|
||||
buffer.close();
|
||||
}
|
||||
|
||||
vertexBuffers.put(vertexformat, new MappableRingBuffer(() -> "vertex buffer for " + vertexformat, 34, i));
|
||||
}
|
||||
}
|
||||
|
||||
var commandencoder = RenderSystem.getDevice().createCommandEncoder();
|
||||
map = new Object2IntOpenHashMap<>();
|
||||
|
||||
for (MeshToDraw item : meshesToDraw) {
|
||||
var meshdata = item.mesh;
|
||||
var state = meshdata.drawState();
|
||||
var vertexformat = state.format();
|
||||
var mappableringbuffer = vertexBuffers.get(vertexformat);
|
||||
if (!map.containsKey(vertexformat)) {
|
||||
map.put(vertexformat, 0);
|
||||
}
|
||||
|
||||
ByteBuffer bytebuffer = meshdata.vertexBuffer();
|
||||
int i = bytebuffer.remaining();
|
||||
int j = map.getInt(vertexformat);
|
||||
|
||||
try (var item1 = commandencoder.mapBuffer(mappableringbuffer.currentBuffer().slice(j, i), false, true)) {
|
||||
MemoryUtil.memCopy(bytebuffer, item1.data());
|
||||
}
|
||||
|
||||
map.put(vertexformat, j + i);
|
||||
draws.add(new Draw(mappableringbuffer.currentBuffer(), j / vertexformat.getVertexSize(), state.mode(),
|
||||
state.indexCount(), item.pipeline, item.textureSetup, item.scissorArea));
|
||||
item.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void executeDraw(Draw draw, RenderPass pass, CoreRenderTarget target, GpuBuffer buffer, VertexFormat.IndexType index) {
|
||||
var renderpipeline = draw.pipeline();
|
||||
pass.setPipeline(renderpipeline);
|
||||
pass.setVertexBuffer(0, draw.vertexBuffer);
|
||||
var area = draw.scissorArea();
|
||||
if (area != null) {
|
||||
Window window = Minecraft.getInstance().getWindow();
|
||||
|
||||
int i = target.target.height;
|
||||
int j = window.getGuiScale();
|
||||
double d0 = area.left() * j;
|
||||
double d1 = i - area.bottom() * j;
|
||||
double d2 = area.width() * j;
|
||||
double d3 = area.height() * j;
|
||||
pass.enableScissor((int) d0, (int) d1, Math.max(0, (int) d2), Math.max(0, (int) d3));
|
||||
} else {
|
||||
pass.disableScissor();
|
||||
}
|
||||
|
||||
if (draw.textureSetup.texure0() != null) {
|
||||
pass.bindSampler("Sampler0", draw.textureSetup.texure0());
|
||||
}
|
||||
|
||||
if (draw.textureSetup.texure1() != null) {
|
||||
pass.bindSampler("Sampler1", draw.textureSetup.texure1());
|
||||
}
|
||||
|
||||
if (draw.textureSetup.texure2() != null) {
|
||||
pass.bindSampler("Sampler2", draw.textureSetup.texure2());
|
||||
}
|
||||
|
||||
pass.setIndexBuffer(buffer, index);
|
||||
pass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1);
|
||||
}
|
||||
|
||||
private boolean scissorChanged(ScreenRectangle screen1, ScreenRectangle screen2) {
|
||||
if (screen1 == screen2) {
|
||||
return false;
|
||||
} else {
|
||||
return screen1 == null || !screen1.equals(screen2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
byteBufferBuilder.close();
|
||||
|
||||
guiProjectionMatrixBuffer.close();
|
||||
|
||||
for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) {
|
||||
mappableringbuffer.close();
|
||||
}
|
||||
}
|
||||
|
||||
record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount,
|
||||
RenderPipeline pipeline, TextureSetup textureSetup, ScreenRectangle scissorArea) {
|
||||
}
|
||||
|
||||
record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup,
|
||||
ScreenRectangle scissorArea) implements AutoCloseable {
|
||||
@Override
|
||||
public void close() {
|
||||
mesh.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.coloryr.allmusic.client;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderPipeline;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.gui.navigation.ScreenRectangle;
|
||||
import net.minecraft.client.gui.render.TextureSetup;
|
||||
import net.minecraft.client.gui.render.state.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, float f) {
|
||||
vertexConsumer.addVertexWith2DPose(pose(), x0(), y0(), f).setUv(u0(), v0()).setColor(color());
|
||||
vertexConsumer.addVertexWith2DPose(pose(), x0(), y1(), f).setUv(u0(), v1()).setColor(color());
|
||||
vertexConsumer.addVertexWith2DPose(pose(), x1(), y1(), f).setUv(u1(), v1()).setColor(color());
|
||||
vertexConsumer.addVertexWith2DPose(pose(), x1(), y0(), f).setUv(u1(), v0()).setColor(color());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,648 @@
|
||||
package com.coloryr.allmusic.client;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.MutableComponent;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.network.chat.TextColor;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
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<String, ChatFormatting> COLOR_MAP = new HashMap<>();
|
||||
// 装饰器映射
|
||||
private static final Map<String, ChatFormatting> 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<StyleContext> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理颜色标签
|
||||
* 支持格式: <color:red>, <color:#FF0000>, <color(rgb):255,0,0>
|
||||
*/
|
||||
private static void handleColorTag(StyleContext style, String params, String value) {
|
||||
if (value != null) {
|
||||
// 格式: <color:red> 或 <color:#FF0000>
|
||||
TextColor textColor = parseColor(value);
|
||||
if (textColor != null) {
|
||||
style.color = textColor;
|
||||
// 清除渐变、过渡和彩虹效果
|
||||
style.gradient = false;
|
||||
style.transition = false;
|
||||
style.rainbow = false;
|
||||
}
|
||||
} else if (params != null) {
|
||||
// 格式: <color(rgb):255,0,0>
|
||||
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) {
|
||||
// 忽略解析错误
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理阴影标签
|
||||
* 支持格式: <shadow>, <shadow:true>, <shadow:false>, <shadow:#FF0000>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理装饰标签
|
||||
* 支持格式: <decoration:bold>, <decoration:bold:true>, <decoration:bold:false>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理彩虹色标签
|
||||
* 支持格式: <rainbow>, <rainbow:!>, <rainbow(saturation):0.8>, <rainbow(brightness):0.5>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理渐变色标签
|
||||
* 支持格式: <gradient:#FF0000:#00FF00>, <gradient(rgb):255,0,0:0,255,0>
|
||||
* 渐变是逐字符过渡
|
||||
*/
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理过渡色标签
|
||||
* 支持格式: <transition:#FF0000:#00FF00>, <transition(rgb):255,0,0:0,255,0>
|
||||
* 过渡是整个文本块的颜色过渡(类似整体渐变效果)
|
||||
*/
|
||||
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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理字体标签
|
||||
* 支持格式: <font:minecraft:default>, <font:minecraft:uniform>
|
||||
*/
|
||||
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<ChatFormatting, Boolean> 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<ChatFormatting, Boolean> entry : decorations.entrySet()) {
|
||||
if (entry.getValue()) {
|
||||
style = style.applyFormat(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
if (font != null) {
|
||||
try {
|
||||
style = style.withFont(ResourceLocation.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
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.guiRenderState.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, TextureSetup.singleTexture(rotate ? rotateTexture.getTextureView() : sourceTexture.getTextureView()), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
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.ResourceLocation;
|
||||
import org.joml.Matrix3x2f;
|
||||
|
||||
public class TexRender extends TextureRender {
|
||||
private final SimpleTexture sourceTexture;
|
||||
|
||||
public TexRender(String texture) {
|
||||
super(texture);
|
||||
ResourceLocation location = ResourceLocation.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.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, TextureSetup.singleTexture(sourceTexture.getTextureView()), 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.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, TextureSetup.singleTexture(sourceTexture.getTextureView()), 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.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, TextureSetup.singleTexture(sourceTexture.getTextureView()), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek()));
|
||||
}
|
||||
}
|
||||
@@ -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.Gui;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
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(Gui.class)
|
||||
public class GuiShow {
|
||||
@Inject(method = {"renderChat"}, at = {@At(value = "RETURN")})
|
||||
public void guiShow(GuiGraphics context, DeltaTracker tickCounter, CallbackInfo ci) {
|
||||
AllMusicClient.update(context);
|
||||
}
|
||||
}
|
||||
@@ -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;Z)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GameRenderer;resetData()V"))
|
||||
public void quit(CallbackInfo info) {
|
||||
AllMusicCore.onServerQuit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.coloryr.allmusic.client.mixins;
|
||||
|
||||
import com.coloryr.allmusic.client.core.AllMusicCore;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
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 = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screens/TitleScreen;registerTextures(Lnet/minecraft/client/renderer/texture/TextureManager;)V"))
|
||||
public void register(CallbackInfo info) {
|
||||
RenderSystem.queueFencedTask(AllMusicCore::renderInit);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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 soundInstance, CallbackInfoReturnable<SoundEngine.PlayResult> cir) {
|
||||
if (AllMusicCore.isPlay()) {
|
||||
SoundSource data = soundInstance.getSource();
|
||||
switch (data) {
|
||||
case RECORDS, MUSIC -> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "reload", at = @At("RETURN"))
|
||||
public void reload(CallbackInfo info) {
|
||||
AllMusicCore.reload();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
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("TAIL"))
|
||||
public void tick(CallbackInfo info) {
|
||||
AllMusicCore.tick();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<MusicCodec> ID = new Type<>(ChannelID);
|
||||
public static final StreamCodec<FriendlyByteBuf, MusicCodec> CODEC =
|
||||
StreamCodec.of((buf, value) -> MusicPacketCodec.pack(buf, value.pack),
|
||||
buf -> new MusicCodec(MusicPacketCodec.decode(buf)));
|
||||
|
||||
@Override
|
||||
public Type<? extends CustomPacketPayload> type() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"required": true,
|
||||
"package": "com.coloryr.allmusic.client.mixins",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"client": [
|
||||
"GuiShow",
|
||||
"QuitServer",
|
||||
"Register",
|
||||
"SoundEvent",
|
||||
"Tick"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"overwrites": {
|
||||
"requireAnnotations": true
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
{
|
||||
"config": "allmusic_client.mixins.json",
|
||||
"environment": "client"
|
||||
}
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": ">=0.16.14",
|
||||
"minecraft": ">=1.21.6",
|
||||
"java": ">=21",
|
||||
"fabric-api": "*"
|
||||
},
|
||||
"suggests": {
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ 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")
|
||||
|
||||
Reference in new issue
Block a user