更新内容

1. 方块掉落战利品表
2. 实体解析Map
3. 客户端转轮组件
4. NBTWriter 改进
This commit is contained in:
叁玖领域 2025-12-06 23:15:50 +08:00
parent 4f38923636
commit 19463edd61
37 changed files with 2830 additions and 13 deletions

View File

@ -6,6 +6,7 @@ plugins {
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
id 'com.dorongold.task-tree' version '2.1.1'
}
java {
@ -382,3 +383,122 @@ idea {
tasks.withType(GenerateModuleMetadata) {
enabled = false
}
tasks.register('showTaskTree') {
doLast {
def showTaskDeps
showTaskDeps = { task, prefix = '' ->
println "${prefix}${task.name}"
task.getTaskDependencies().getDependencies(task).each { dep ->
showTaskDeps(dep, prefix + ' ')
}
}
def targetTask = tasks.findByName('build')
if (targetTask) {
println "构建任务依赖树:"
showTaskDeps(targetTask)
} else {
println "未找到 build 任务"
}
}
}
/**<pre>
build
check
test
compileTestJava
classes
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
processResources
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
testClasses
processTestResources
compileTestJava
classes
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
processResources
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
classes
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
processResources
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
assemble
reobfJarJar
createMcpToSrg
extractSrg
downloadMcpConfig
configureReobfTaskForReobfJarJar
proguard
jarJar
classes
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
processResources
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
addMixinsToJarJar
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
reobfJar
jar
classes
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
processResources
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
addMixinsToJar
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
createMcpToSrg
extractSrg
downloadMcpConfig
configureReobfTaskForReobfJar
jar
classes
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
processResources
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
addMixinsToJar
compileJava
createMcpToSrg
extractSrg
downloadMcpConfig
</pre>
*/

View File

@ -33,7 +33,7 @@ mod_name=3944Realms 's Lib Mod
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT
# The mod version. See https://semver.org/
mod_version=0.0.17
mod_version=0.0.18
# The group ID for the mod. It is only important when publishing as an artifact to a Maven repository.
# This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html

View File

@ -1,2 +1,2 @@
// 1.20.1 2025-10-25T19:14:21.1829335 Languages: zh_tw
4cb94c651f6aa74538a2ab25cb183cffd75be688 assets/lib39/lang/zh_tw.json
// 1.20.1 2025-11-22T23:38:13.2517748 Languages: zh_tw
84dba66c4c768fd7754eaabe990c96f14a30c1df assets/lib39/lang/zh_tw.json

View File

@ -1,2 +1,2 @@
// 1.20.1 2025-10-25T19:14:21.1764262 Languages: zh_cn
a1db601a0fca923c5434b8b0843773a3115d0b59 assets/lib39/lang/zh_cn.json
// 1.20.1 2025-11-22T23:38:13.249775 Languages: zh_cn
48655f133966c9a854c57b560d070850af5a289f assets/lib39/lang/zh_cn.json

View File

@ -1,3 +1,4 @@
// 1.20.1 2025-10-25T18:42:29.3405259 Item Models: lib39
// 1.20.1 2025-11-22T23:38:13.2527751 Item Models: lib39
14f581c8f8e7f0de004c57a180f371e60e7b12ae assets/lib39/models/item/fabric.json
70583055336790fc837836ea6b49d16cfc8b64b8 assets/lib39/models/item/forge.json
447b36747d0aa8748dcd86715f4cce2cff19aca7 assets/lib39/models/item/neoforge.json

View File

@ -1,2 +1,2 @@
// 1.20.1 2025-10-25T19:14:21.1804258 Languages: lzh
098df025475d3f4d9d2abefa91a7d38c44644ba4 assets/lib39/lang/lzh.json
// 1.20.1 2025-11-22T23:38:13.2517748 Languages: lzh
a12c11d89d484a0e4f193ebd11b63722b8c501df assets/lib39/lang/lzh.json

View File

@ -1,2 +1,2 @@
// 1.20.1 2025-10-25T19:14:21.1784269 Languages: en_us
c6b6aadca0a922823a8c949ebde93f8f999737f9 assets/lib39/lang/en_us.json
// 1.20.1 2025-11-22T23:38:13.2507757 Languages: en_us
65df86271a054138e168311be826408455b3c33a assets/lib39/lang/en_us.json

View File

@ -1,4 +1,5 @@
{
"item.lib39.fabric": "Fabric",
"item.lib39.forge": "Forge",
"item.lib39.neoforge": "NeoForge"
}

View File

@ -1,4 +1,5 @@
{
"item.lib39.fabric": "織",
"item.lib39.forge": "砧",
"item.lib39.neoforge": "狸"
}

View File

@ -1,4 +1,5 @@
{
"item.lib39.fabric": "织布",
"item.lib39.forge": "铁砧",
"item.lib39.neoforge": "小狐狸"
}

View File

@ -1,4 +1,5 @@
{
"item.lib39.fabric": "織布",
"item.lib39.forge": "铁砧",
"item.lib39.neoforge": "狐狸"
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "lib39:item/forge"
}
}

View File

@ -1,8 +1,11 @@
package top.r3944realms.lib39;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.loading.FMLEnvironment;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.r3944realms.lib39.core.network.NetworkHandler;
@ -29,6 +32,11 @@ public class Lib39 {
initialize();
}
/**
* The constant ENABLE_EXAMPLES_PROPERTY_KEY.
*/
public static final String ENABLE_EXAMPLES_PROPERTY_KEY = "lib39.enable_examples";
/**
* Initialize.
*/
@ -43,6 +51,17 @@ public class Lib39 {
}
/**
* Rl resource location.
*
* @param path the path
* @return the resource location
*/
@Contract("_ -> new")
public static @NotNull ResourceLocation rl(String path) {
return new ResourceLocation(MOD_ID, path);
}
/**
* The type Mod info.
*/
@ -66,7 +85,7 @@ public class Lib39 {
* @return the boolean
*/
static boolean shouldRegisterExamples() {
return !FMLEnvironment.production;
return !FMLEnvironment.production || Boolean.getBoolean(ENABLE_EXAMPLES_PROPERTY_KEY);
}
/**

View File

@ -4,6 +4,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.eventbus.api.Event;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.ISyncManager;
import top.r3944realms.lib39.core.sync.SyncData2Manager;
@ -159,7 +160,7 @@ public class SyncManagerRegisterEvent extends Event {
ResourceLocation id,
ISyncManager<K, T> syncManager,
Function<Entity, Optional<T>> dataProvider,
Class<?>... allowedEntityClasses
Class<?> @NotNull ... allowedEntityClasses
) {
registerSyncManager(id, syncManager, dataProvider);
if (allowedEntityClasses.length > 0) {

View File

@ -0,0 +1,769 @@
package top.r3944realms.lib39.client.gui.component;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.network.chat.Component;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import top.r3944realms.lib39.client.shader.Lib39Shaders;
import top.r3944realms.lib39.util.MathUtil;
import top.r3944realms.lib39.util.lang.FourConsumer;
import top.r3944realms.lib39.util.lang.Pair;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* The type Wheel widget.
*
* @author QiuShui1012
*/
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class WheelWidget extends AbstractWidget {
/**
* The constant IGNORE_CURSOR_MOVE_LENGTH.
*/
public static final int IGNORE_CURSOR_MOVE_LENGTH = 15;
private static final Vector2f ROTATION_START = new Vector2f(0, 1);
private static final int SELECTION_EFFECT_COLOR = 0xddFFFF00;
private static final int SELECTION_EFFECT_RADIUS = 20;
private final Minecraft minecraft = Minecraft.getInstance();
private final Vector2f centerPos;
private final float ringInnerRadius;
private final float ringOuterRadius;
private final int delay;
private final int animationMs;
private final int closingAnimationMs; //ms
private final int ringColor;
private final int selectionEffectColor;
private final int selectionEffectRadius;
private final float selectionAnimationSpeedFactor;
private final int textColor;
private final float textScale;
private final List<WheelSection> sections = new ArrayList<>();
private long displayTime = System.currentTimeMillis();
private float currentAngle = 0;
/**
* Gets current section index.
*
* @return the current section index
*/
public int getCurrentSectionIndex() {
return currentSectionIndex;
}
/**
* Is closing animation started boolean.
*
* @return the boolean
*/
public boolean isClosingAnimationStarted() {
return closingAnimationStarted;
}
private int currentSectionIndex = -1;
private Vector2f selectionEffectPos;
private boolean animationStarted = false;
/**
* Sets closing animation started.
*
* @param closingAnimationStarted the closing animation started
*/
public void setClosingAnimationStarted(boolean closingAnimationStarted) {
this.closingAnimationStarted = closingAnimationStarted;
}
private boolean closingAnimationStarted = false;
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param textScale the text scale
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius, float textScale,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, textScale, sections);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param textScale the text scale
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius, float textScale, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, textScale, degreeOffsetAngle, sections);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(x, y, width, height, Component.empty(), ringInnerRadius, ringOuterRadius, sections);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param textScale the text scale
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius, float textScale, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, message,
ringInnerRadius, ringOuterRadius,
150, 300, 150,
0x00000000,
0xddffff00, 20, 5f,
0xfdfdfd, textScale, degreeOffsetAngle,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, message,
ringInnerRadius, ringOuterRadius,
150, 300, 150,
0x00000000,
0xddffff00, 20, 5f,
0xfdfdfd, 1f, 0f,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, message,
ringInnerRadius, ringOuterRadius,
150, 300, 150,
0x00000000,
0xddffff00, 20, 5f,
0xfdfdfd, 1f, degreeOffsetAngle,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param delay the delay
* @param animationMs the animation ms
* @param closingAnimationMs the closing animation ms
* @param ringColor the ring color
* @param selectionEffectColor the selection effect color
* @param selectionEffectRadius the selection effect radius
* @param selectionAnimationSpeedFactor the selection animation speed factor
* @param textColor the text color
* @param textScale the text scale
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height,
float ringInnerRadius, float ringOuterRadius,
int delay, int animationMs, int closingAnimationMs,
int ringColor,
int selectionEffectColor, int selectionEffectRadius, float selectionAnimationSpeedFactor,
int textColor, float textScale,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
this(
x, y, width, height, Component.empty(),
ringInnerRadius, ringOuterRadius,
delay, animationMs, closingAnimationMs,
ringColor,
selectionEffectColor, selectionEffectRadius, selectionAnimationSpeedFactor,
textColor, textScale, 0f,
sections
);
}
/**
* Instantiates a new Wheel widget.
*
* @param x the x
* @param y the y
* @param width the width
* @param height the height
* @param message the message
* @param ringInnerRadius the ring inner radius
* @param ringOuterRadius the ring outer radius
* @param delay the delay
* @param animationMs the animation ms
* @param closingAnimationMs the closing animation ms
* @param ringColor the ring color
* @param selectionEffectColor the selection effect color
* @param selectionEffectRadius the selection effect radius
* @param selectionAnimationSpeedFactor the selection animation speed factor
* @param textColor the text color
* @param textScale the text scale
* @param degreeOffsetAngle the degree offset angle
* @param sections the sections
*/
public WheelWidget(
int x, int y, int width, int height, Component message,
float ringInnerRadius, float ringOuterRadius,
int delay, int animationMs, int closingAnimationMs,
int ringColor,
int selectionEffectColor, int selectionEffectRadius, float selectionAnimationSpeedFactor,
int textColor, float textScale, float degreeOffsetAngle,
List<Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>>> sections
) {
super(x, y, width, height, message);
this.centerPos = new Vector2f(this.getX() + this.getWidth() / 2f, this.getY() + this.getHeight() / 2f);
this.ringInnerRadius = Math.max(ringInnerRadius, IGNORE_CURSOR_MOVE_LENGTH);
this.ringOuterRadius = ringOuterRadius;
this.delay = delay;
this.animationMs = animationMs;
this.closingAnimationMs = closingAnimationMs;
this.ringColor = ringColor;
this.selectionEffectColor = selectionEffectColor;
this.selectionEffectRadius = selectionEffectRadius;
this.selectionAnimationSpeedFactor = selectionAnimationSpeedFactor;
this.textColor = textColor;
this.textScale = textScale;
float degreeEachRotation = 360f / sections.size();
for (int i = 0; i < sections.size(); i++) {
Pair<Component, FourConsumer<GuiGraphics, PoseStack, Integer, Integer>> section = sections.get(i);
float rotation = MathUtil.clampWithProportion((degreeEachRotation * i + degreeOffsetAngle) % 360, 0, 360);
Vector2f rotated = MathUtil.rotationDegrees(ROTATION_START, rotation)
.mul(1, -1)
.mul(this.getSectionCircleDiameter())
.add(this.centerPos);
float detectionStart = (float) (Math.toRadians(rotation - degreeEachRotation / 2f) + Math.PI * 2);
float detectionEnd = (float) (Math.toRadians(rotation + degreeEachRotation / 2f) + Math.PI * 2);
detectionStart = detectionStart % (float) (Math.PI * 2);
detectionEnd = detectionEnd % (float) (Math.PI * 2);
this.sections.add(new WheelSection(
rotated,
(float) (Math.toRadians(rotation) % (Math.PI * 2)),
detectionStart,
detectionEnd,
section.first,
section.second
));
}
this.selectionEffectPos = MathUtil.rotate(
MathUtil.copy(ROTATION_START)
.mul(this.getSectionCircleDiameter()),
this.currentAngle
);
}
/**
* Gets section circle diameter.
*
* @return the section circle diameter
*/
// 滚轮选择器中每个扇形的圆形直径
public float getSectionCircleDiameter() {
return this.ringOuterRadius + this.ringInnerRadius;
}
/**
* Sets current index.
*
* @param index the index
* @return the current index
*/
public WheelWidget setCurrentIndex(int index) {
this.currentSectionIndex = index;
this.currentAngle = this.sections.get(index).angle;
this.selectionEffectPos = MathUtil.rotate(
MathUtil.copy(ROTATION_START)
.mul(this.getSectionCircleDiameter()),
this.currentAngle
);
return this;
}
/**
* Gets section size.
*
* @return the section size
*/
public int getSectionSize() {
return this.sections.size();
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) {
if (delta > 0) {
if (this.currentSectionIndex == this.getSectionSize() - 1) {
this.currentSectionIndex = 0;
} else {
this.currentSectionIndex++;
}
} else if (delta < 0) {
if (this.currentSectionIndex == 0) {
this.currentSectionIndex = this.getSectionSize() - 1;
} else {
this.currentSectionIndex--;
}
}
for (WheelSection section : this.sections) {
if (this.sections.indexOf(section) == this.currentSectionIndex) {
this.currentAngle = section.angle;
return true;
}
}
return true;
}
/**
* Check mouse pos.
*
* @param mouseX the mouse x
* @param mouseY the mouse y
*/
public void checkMousePos(double mouseX, double mouseY) {
if (this.closingAnimationStarted) return;
float centerX = this.centerPos.x;
float centerY = this.centerPos.y;
// 鼠标距离屏幕中心的位置向量
Vector2f cursorPos = new Vector2f((float) mouseX - centerX, (float) mouseY - centerY);
if (cursorPos.length() < IGNORE_CURSOR_MOVE_LENGTH) return;
Vector2f rotationStart = new Vector2f(0, 1);
cursorPos.normalize();
// 计算夹角弧度
double rot = Math.acos(rotationStart.dot(cursorPos) / (rotationStart.length() * cursorPos.length()));
double rotation = cursorPos.x < 0 ? Math.PI - rot : Math.PI + rot;
for (WheelSection section : this.sections) {
if (section.angleStart > section.angleEnd && rotation >= section.angleStart
|| rotation >= section.angleStart && rotation <= section.angleEnd
) {
this.currentAngle = section.angle;
this.currentSectionIndex = this.sections.indexOf(section);
break;
}
}
}
/**
* Should render boolean.
*
* @return the boolean
*/
public boolean shouldRender() {
if (this.animationStarted) return true;
return (this.displayTime + this.delay) <= System.currentTimeMillis();
}
@Override
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
this.checkMousePos(mouseX, mouseY);
this.renderWidget(guiGraphics, mouseX, mouseY, partialTick);
}
@Override
protected void renderWidget(GuiGraphics guiGraphics, int i, int i1, float v) {
RenderSystem.enableDepthTest();
RenderSystem.enableBlend();
this.renderClosingAnimation(guiGraphics);
if (!this.shouldRender()) {
return;
}
if (this.closingAnimationStarted) return;
if (!this.animationStarted) {
this.animationStarted = true;
this.displayTime = System.currentTimeMillis();
}
PoseStack poseStack = guiGraphics.pose();
float delta = this.displayTime + this.animationMs - System.currentTimeMillis();
if (delta > 0) {
float progress = 1 - (delta / this.animationMs);
progress = (float) (-Math.pow(progress, 2) + 2 * progress);
if (progress == 0) return;
this.renderProgressAnimation(guiGraphics, progress);
return;
}
renderRing(
guiGraphics,
this.centerPos.x,
this.centerPos.y,
this.ringColor,
this.ringInnerRadius * 2,
this.ringOuterRadius * 2
);
this.renderSelection(guiGraphics);
for (WheelSection value : this.sections) {
float x = value.center.x;
float y = value.center.y;
poseStack.pushPose();
poseStack.translate(x - 10, y - 10, 100);
value.renderer.accept(guiGraphics, poseStack, 20, 20);
poseStack.popPose();
poseStack.pushPose();
float coordinateScale = 0.7f;
float offsetX = 0.1f * this.width;
float offsetY = 0.1f * this.height;
float adjustedX = (x - offsetX) / coordinateScale;
float adjustedY = (y - offsetY - 20 * this.textScale) / coordinateScale;
poseStack.translate(offsetX, offsetY, 0);
poseStack.scale(coordinateScale, coordinateScale, coordinateScale);
poseStack.translate(adjustedX, adjustedY, 0);
poseStack.scale(this.textScale / coordinateScale, this.textScale / coordinateScale, this.textScale / coordinateScale);
guiGraphics.drawCenteredString(
minecraft.font,
value.subTitle,
0,
0,
(0xff << 24) | this.textColor
);
poseStack.popPose();
}
RenderSystem.disableDepthTest();
RenderSystem.disableBlend();
}
/**
* Render closing animation.
*
* @param guiGraphics the gui graphics
*/
public void renderClosingAnimation(GuiGraphics guiGraphics) {
if (!this.closingAnimationStarted) return;
float delta = this.displayTime + this.closingAnimationMs - System.currentTimeMillis();
float progress = delta / this.closingAnimationMs;
if(progress >= 1 || progress <= 0) {
this.minecraft.setScreen(null);
}
this.renderProgressAnimation(guiGraphics, progress);
}
private void renderProgressAnimation(GuiGraphics guiGraphics, float progress) {
progress = (float) (-Math.pow(progress, 2) + 2 * progress);
if (progress == 0) return;
PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose();
renderRing(
guiGraphics,
this.centerPos.x,
this.centerPos.y,
this.ringColor,
this.ringInnerRadius * 2 * progress,
this.ringOuterRadius * 2 * progress
);
poseStack.popPose();
if(this.currentSectionIndex != -1) {
WheelSection section = this.sections.get(this.currentSectionIndex);
Vector2f center = new Vector2f(
(section.center.x - this.centerPos.x) / this.getSectionCircleDiameter(),
(section.center.y - this.centerPos.y) / this.getSectionCircleDiameter()
).mul(this.getSectionCircleDiameter() * progress).add(this.centerPos.x, this.centerPos.y);
renderSelectionEffect(
guiGraphics,
center.x,
center.y,
this.selectionEffectColor,
this.selectionEffectRadius
);
}
for (WheelSection value : this.sections) {
if (sections.get(0) != value) continue;
Vector2f center = new Vector2f(
(value.center.x - this.centerPos.x) / this.getSectionCircleDiameter(),
(value.center.y - this.centerPos.y) / this.getSectionCircleDiameter()
).mul(this.getSectionCircleDiameter() * progress).add(this.centerPos.x, this.centerPos.y);
float x = center.x;
float y = center.y;
poseStack.pushPose();
poseStack.translate(x - 10, y - 10, 100);
value.renderer.accept(guiGraphics, poseStack, 20, 20);
poseStack.pushPose();
}
}
/**
* Render ring.
*
* @param guiGraphics the gui graphics
* @param centerX the center x
* @param centerY the center y
* @param color the color
* @param innerRadius the inner radius
* @param outerRadius the outer radius
*/
public static void renderRing(
GuiGraphics guiGraphics,
float centerX,
float centerY,
int color,
float innerRadius, // 改为半径
float outerRadius // 改为半径
) {
PoseStack poseStack = guiGraphics.pose();
poseStack.pushPose();
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder buffer = tesselator.getBuilder();
// 计算足够大的绘制区域来覆盖整个环形基于外半径
float margin = outerRadius + 100f; // 使用半径计算边距
float x1 = centerX - margin;
float y1 = centerY - margin;
float x2 = centerX + margin;
float y2 = centerY + margin;
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
Matrix4f matrix = poseStack.last().pose();
buffer.vertex(matrix, x1, y1, -300).color(color).endVertex();
buffer.vertex(matrix, x1, y2, -300).color(color).endVertex();
buffer.vertex(matrix, x2, y2, -300).color(color).endVertex();
buffer.vertex(matrix, x2, y1, -300).color(color).endVertex();
setupRingShader(centerX, centerY, innerRadius, outerRadius);
BufferUploader.drawWithShader(buffer.end());
poseStack.popPose();
}
private static void setupRingShader(float centerX, float centerY, float innerRadius, float outerRadius) {
Window window = Minecraft.getInstance().getWindow();
float guiScale = (float) window.getGuiScale();
RenderSystem.setShader(Lib39Shaders::getRingShader);
// 转换到像素坐标考虑GUI缩放
float pixelCenterX = centerX * guiScale;
float pixelCenterY = window.getHeight() - (centerY * guiScale); // 翻转Y坐标
// 半径考虑GUI缩放
float pixelInnerRadius = innerRadius * guiScale;
float pixelOuterRadius = outerRadius * guiScale;
float pixelAntiAliasing = 2.0f * guiScale; // 抗锯齿范围
System.out.println("Shader Params - Center: (" + pixelCenterX + ", " + pixelCenterY +
"), InnerRadius: " + pixelInnerRadius + ", OuterRadius: " + pixelOuterRadius);
ShaderInstance shader = Lib39Shaders.getRingShader();
shader.safeGetUniform("Center").set(pixelCenterX, pixelCenterY);
shader.safeGetUniform("InnerRadius").set(pixelInnerRadius);
shader.safeGetUniform("OuterRadius").set(pixelOuterRadius);
shader.safeGetUniform("AntiAliasing").set(pixelAntiAliasing);
shader.safeGetUniform("ColorModulator").set(1.0f, 1.0f, 1.0f, .5f);
}
private void renderSelection(GuiGraphics guiGraphics) {
float selectionEffectAngle = MathUtil.angle(
MathUtil.copy(ROTATION_START),
this.selectionEffectPos
);
float diffAngle = this.currentAngle - selectionEffectAngle;
if (diffAngle > Math.PI) {
diffAngle -= (float) (Math.PI * 2);
} else if (diffAngle < -Math.PI) {
diffAngle += (float) (Math.PI * 2);
}
this.selectionEffectPos = MathUtil.rotate(
this.selectionEffectPos,
diffAngle / this.selectionAnimationSpeedFactor
);
Vector2f pos = MathUtil.copy(this.selectionEffectPos)
.mul(1, -1)
.add(this.centerPos);
// 调用时使用半径
renderSelectionEffect(
guiGraphics,
pos.x,
pos.y,
SELECTION_EFFECT_COLOR,
SELECTION_EFFECT_RADIUS // 确保这是半径值
);
}
/**
* Render selection effect.
*
* @param guiGraphics the gui graphics
* @param centerX the center x
* @param centerY the center y
* @param color the color
* @param radius the radius
*/
public static void renderSelectionEffect(
GuiGraphics guiGraphics,
float centerX,
float centerY,
int color,
float radius
) {
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.disableDepthTest();
PoseStack poseStack = guiGraphics.pose();
Matrix4f matrix4f = poseStack.last().pose();
Tesselator tesselator = Tesselator.getInstance();
BufferBuilder buffer = tesselator.getBuilder();
buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
float x1 = centerX - radius - 5;
float y1 = centerY - radius - 5;
float x2 = centerX + radius + 5;
float y2 = centerY + radius + 5;
buffer.vertex(matrix4f, x1, y1, -200).color(color).endVertex();
buffer.vertex(matrix4f, x1, y2, -200).color(color).endVertex();
buffer.vertex(matrix4f, x2, y2, -200).color(color).endVertex();
buffer.vertex(matrix4f, x2, y1, -200).color(color).endVertex();
Window window = Minecraft.getInstance().getWindow();
float guiScale = (float) window.getGuiScale();
RenderSystem.setShader(Lib39Shaders::getSelectionShader);
System.out.println("Selection Effect Params:");
System.out.println(" Center: " + centerX + ", " + centerY);
System.out.println(" Radius: " + radius);
System.out.println(" GUI Scale: " + guiScale);
System.out.println(" Framebuffer: " + window.getWidth() + "x" + window.getHeight());
Lib39Shaders.getSelectionShader()
.safeGetUniform("Center")
.set(centerX * guiScale, centerY * guiScale);
Lib39Shaders.getSelectionShader()
.safeGetUniform("FramebufferSize")
.set((float) window.getWidth(), (float) window.getHeight());
Lib39Shaders.getSelectionShader()
.safeGetUniform("Radius")
.set(radius * guiScale);
Lib39Shaders.getSelectionShader()
.safeGetUniform("AntiAliasingRadius")
.set(guiScale); // 根据需要调整
RenderSystem.setShaderColor(1, 1, 1, 1);
BufferUploader.drawWithShader(Objects.requireNonNull(buffer.end()));
RenderSystem.enableDepthTest();
}
/**
* On closing.
*/
public void onClosing() {
if (this.shouldRender() && !this.closingAnimationStarted) {
this.displayTime = System.currentTimeMillis();
this.closingAnimationStarted = true;
} else {
this.minecraft.setScreen(null);
}
}
@Override
protected void updateWidgetNarration(NarrationElementOutput narrationElementOutput) {
}
/**
* The type Wheel section.
*/
public record WheelSection(
Vector2f center,
float angle,
float angleStart,
float angleEnd,
Component subTitle,
FourConsumer<GuiGraphics, PoseStack, Integer, Integer> renderer
) {
}
}

View File

@ -0,0 +1,381 @@
package top.r3944realms.lib39.client.renderer;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
/**
* 圆形径向菜单渲染器
* 用于创建美观的圆形选择菜单
*
* @param <T> the type parameter
*/
public class RadialMenuRenderer<T> {
/**
* The constant DEFAULT_INNER_RADIUS.
*/
// 默认配置常量
public static final float DEFAULT_INNER_RADIUS = 30f;
/**
* The constant DEFAULT_OUTER_RADIUS.
*/
public static final float DEFAULT_OUTER_RADIUS = 80f;
/**
* The constant DEFAULT_MIDDLE_RADIUS.
*/
public static final float DEFAULT_MIDDLE_RADIUS = 55f;
/**
* The constant DEFAULT_SEGMENTS.
*/
public static final int DEFAULT_SEGMENTS = 64;
// 配置选项
private final float innerRadius;
private final float outerRadius;
private final float middleRadius;
private final int segments;
private final boolean enableHoverAnimation;
private final ColorScheme colorScheme;
// 状态
private int hoveredIndex = -1;
private final float[] hoverAnimations;
private long lastAnimationTime = 0;
/**
* The type Color scheme.
*/
public static class ColorScheme {
/**
* The Normal color.
*/
public final float[] normalColor;
/**
* The Hovered color.
*/
public final float[] hoveredColor;
/**
* The Selected color.
*/
public final float[] selectedColor;
/**
* The Background color.
*/
public final float[] backgroundColor;
/**
* Instantiates a new Color scheme.
*
* @param normalColor the normal color
* @param hoveredColor the hovered color
* @param selectedColor the selected color
* @param backgroundColor the background color
*/
public ColorScheme(float[] normalColor, float[] hoveredColor, float[] selectedColor, float[] backgroundColor) {
this.normalColor = normalColor;
this.hoveredColor = hoveredColor;
this.selectedColor = selectedColor;
this.backgroundColor = backgroundColor;
}
/**
* The constant DEFAULT.
*/
// 预定义颜色方案
public static final ColorScheme DEFAULT = new ColorScheme(
new float[]{0.3f, 0.3f, 0.8f, 0.6f}, // 正常 - 蓝色
new float[]{0.9f, 0.7f, 0.1f, 0.8f}, // 悬停 - 金色
new float[]{0.2f, 0.8f, 0.2f, 0.9f}, // 选中 - 绿色
new float[]{0.1f, 0.1f, 0.1f, 0.7f} // 背景
);
/**
* The constant FIRE.
*/
public static final ColorScheme FIRE = new ColorScheme(
new float[]{0.8f, 0.3f, 0.1f, 0.6f}, // 正常 - 红色
new float[]{1.0f, 0.5f, 0.0f, 0.8f}, // 悬停 - 橙色
new float[]{1.0f, 0.9f, 0.0f, 0.9f}, // 选中 - 黄色
new float[]{0.2f, 0.1f, 0.0f, 0.7f} // 背景
);
/**
* The constant NATURE.
*/
public static final ColorScheme NATURE = new ColorScheme(
new float[]{0.2f, 0.6f, 0.3f, 0.6f}, // 正常 - 绿色
new float[]{0.4f, 0.8f, 0.4f, 0.8f}, // 悬停 - 亮绿
new float[]{0.1f, 0.9f, 0.7f, 0.9f}, // 选中 - 青绿
new float[]{0.1f, 0.2f, 0.1f, 0.7f} // 背景
);
}
/**
* Instantiates a new Radial menu renderer.
*/
public RadialMenuRenderer() {
this(DEFAULT_INNER_RADIUS, DEFAULT_OUTER_RADIUS, ColorScheme.DEFAULT);
}
/**
* Instantiates a new Radial menu renderer.
*
* @param innerRadius the inner radius
* @param outerRadius the outer radius
*/
public RadialMenuRenderer(float innerRadius, float outerRadius) {
this(innerRadius, outerRadius, ColorScheme.DEFAULT);
}
/**
* Instantiates a new Radial menu renderer.
*
* @param innerRadius the inner radius
* @param outerRadius the outer radius
* @param colorScheme the color scheme
*/
public RadialMenuRenderer(float innerRadius, float outerRadius, ColorScheme colorScheme) {
this(innerRadius, outerRadius, (innerRadius + outerRadius) / 2f, DEFAULT_SEGMENTS, true, colorScheme);
}
/**
* Instantiates a new Radial menu renderer.
*
* @param innerRadius the inner radius
* @param outerRadius the outer radius
* @param middleRadius the middle radius
* @param segments the segments
* @param enableHoverAnimation the enable hover animation
* @param colorScheme the color scheme
*/
public RadialMenuRenderer(float innerRadius, float outerRadius, float middleRadius,
int segments, boolean enableHoverAnimation, ColorScheme colorScheme) {
this.innerRadius = innerRadius;
this.outerRadius = outerRadius;
this.middleRadius = middleRadius;
this.segments = segments;
this.enableHoverAnimation = enableHoverAnimation;
this.colorScheme = colorScheme;
this.hoverAnimations = new float[0];
}
/**
* 渲染圆形菜单
*
* @param guiGraphics the gui graphics
* @param entries the entries
* @param titleProvider the title provider
* @param iconProvider the icon provider
* @param selectedIndex the selected index
* @param trackMouse the track mouse
*/
public void render(GuiGraphics guiGraphics, List<T> entries,
Function<T, Component> titleProvider,
Function<T, ItemStack> iconProvider,
int selectedIndex, boolean trackMouse) {
if (entries.isEmpty()) return;
// 更新动画状态
updateHoverAnimations(entries.size());
// 设置渲染状态
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShader(GameRenderer::getPositionColorShader);
float centerX = guiGraphics.guiWidth() / 2f;
float centerY = guiGraphics.guiHeight() / 2f;
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(centerX, centerY, 0f);
// 渲染所有扇形区域
renderSectors(guiGraphics, entries, selectedIndex);
// 渲染图标和文本
renderIconsAndText(guiGraphics, entries, titleProvider, iconProvider);
guiGraphics.pose().popPose();
RenderSystem.disableBlend();
}
/**
* 渲染扇形区域
*/
private void renderSectors(GuiGraphics guiGraphics, List<T> entries, int selectedIndex) {
int count = entries.size();
float angleSize = 360f / count;
for (int i = 0; i < count; i++) {
float startAngle = -90f + i * angleSize;
float currentOuterRadius = outerRadius;
// 悬停动画效果
if (enableHoverAnimation && i < hoverAnimations.length) {
currentOuterRadius += hoverAnimations[i] * 5f;
}
// 颜色设置
float[] color = getSectorColor(i, selectedIndex, entries.get(i));
// 绘制扇形
drawSector(guiGraphics, startAngle, angleSize, innerRadius, currentOuterRadius, color);
}
}
/**
* 获取扇形颜色
*/
private float[] getSectorColor(int index, int selectedIndex, T entry) {
if (index == selectedIndex) {
return colorScheme.selectedColor; // 选中状态
} else if (index == hoveredIndex) {
return colorScheme.hoveredColor; // 悬停状态
} else {
return colorScheme.normalColor; // 普通状态
}
}
/**
* 绘制单个扇形
*/
private void drawSector(GuiGraphics guiGraphics, float startAngle, float angleSize,
float innerRadius, float outerRadius, float[] color) {
BufferBuilder buffer = Tesselator.getInstance().getBuilder();
buffer.begin(VertexFormat.Mode.TRIANGLE_STRIP, DefaultVertexFormat.POSITION_COLOR);
Matrix4f matrix = guiGraphics.pose().last().pose();
float segments = Math.max(8, this.segments * (angleSize / 360f));
for (int i = 0; i <= segments; i++) {
float progress = i / segments;
float angle = startAngle + progress * angleSize;
float rad = angle * Mth.DEG_TO_RAD;
float cos = Mth.cos(rad);
float sin = Mth.sin(rad);
// 外圈顶点
buffer.vertex(matrix, outerRadius * cos, outerRadius * sin, 0)
.color(color[0], color[1], color[2], color[3]).endVertex();
// 内圈顶点
buffer.vertex(matrix, innerRadius * cos, innerRadius * sin, 0)
.color(color[0], color[1], color[2], color[3] * 0.6f).endVertex();
}
BufferUploader.drawWithShader(buffer.end());
}
/**
* 渲染图标和文本
*/
private void renderIconsAndText(GuiGraphics guiGraphics, List<T> entries,
Function<T, Component> titleProvider,
Function<T, ItemStack> iconProvider) {
int count = entries.size();
var font = Minecraft.getInstance().font;
for (int i = 0; i < count; i++) {
T entry = entries.get(i);
float angle = (-90f + 360f * (i + 0.5f) / count) * Mth.DEG_TO_RAD;
// 计算位置
float x = Mth.cos(angle) * middleRadius;
float y = Mth.sin(angle) * middleRadius;
// 渲染图标
ItemStack icon = iconProvider.apply(entry);
if (!icon.isEmpty()) {
guiGraphics.renderItem(icon, (int)(x - 8), (int)(y - 8));
}
// 渲染文本
Component title = titleProvider.apply(entry);
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(x, y + 12, 0);
guiGraphics.pose().scale(0.7f, 0.7f, 0.7f);
guiGraphics.drawString(font, title, -font.width(title) / 2, 0, 0xFFFFFF, true);
guiGraphics.pose().popPose();
}
}
/**
* 更新悬停动画
*/
private void updateHoverAnimations(int entryCount) {
if (!enableHoverAnimation) return;
long currentTime = System.currentTimeMillis();
float deltaTime = Math.min((currentTime - lastAnimationTime) / 1000f, 0.1f);
lastAnimationTime = currentTime;
// 确保数组大小正确
if (hoverAnimations.length != entryCount) {
// 这里需要重新初始化数组实际使用时应该处理数组大小变化
}
// 更新动画值
for (int i = 0; i < hoverAnimations.length && i < entryCount; i++) {
if (i == hoveredIndex) {
hoverAnimations[i] = Mth.clamp(hoverAnimations[i] + deltaTime * 2f, 0f, 1f);
} else {
hoverAnimations[i] = Mth.clamp(hoverAnimations[i] - deltaTime * 3f, 0f, 1f);
}
}
}
/**
* 获取鼠标下的条目索引
*
* @param entries the entries
* @param mouseX the mouse x
* @param mouseY the mouse y
* @return the hovered entry
*/
public int getHoveredEntry(List<T> entries, double mouseX, double mouseY) {
float centerX = Minecraft.getInstance().getWindow().getGuiScaledWidth() / 2f;
float centerY = Minecraft.getInstance().getWindow().getGuiScaledHeight() / 2f;
double relX = mouseX - centerX;
double relY = mouseY - centerY;
double distance = Math.sqrt(relX * relX + relY * relY);
// 检查是否在有效范围内
if (distance < innerRadius || distance > outerRadius) {
hoveredIndex = -1;
return -1;
}
// 计算角度
double angle = Math.atan2(relY, relX) * Mth.RAD_TO_DEG;
angle = (angle + 450) % 360; // 标准化到 0-360
int count = entries.size();
int index = (int) (angle / (360f / count)) % count;
hoveredIndex = index;
return index;
}
/**
* 清除状态
*/
public void clearState() {
hoveredIndex = -1;
// 重置动画数组
Arrays.fill(hoverAnimations, 0f);
}
}

View File

@ -0,0 +1,73 @@
package top.r3944realms.lib39.client.shader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraftforge.client.event.RegisterShadersEvent;
import top.r3944realms.lib39.Lib39;
/**
* The type Lib 39 shaders.
*/
public class Lib39Shaders {
/**
* The Minecraft.
*/
static final Minecraft MINECRAFT = Minecraft.getInstance();
/**
* Gets ring shader.
*
* @return the ring shader
*/
public static ShaderInstance getRingShader() {
return ringShader;
}
/**
* The Ring shader.
*/
static ShaderInstance ringShader;
/**
* Gets selection shader.
*
* @return the selection shader
*/
public static ShaderInstance getSelectionShader() {
return selectionShader;
}
/**
* The Selection shader.
*/
static ShaderInstance selectionShader;
/**
* Register.
*
* @param event the event
*/
public static void register(RegisterShadersEvent event) {
try {
event.registerShader(
new ShaderInstance(
event.getResourceProvider(),
Lib39.rl("ring"),
DefaultVertexFormat.POSITION_COLOR
),
it -> ringShader = it
);
event.registerShader(
new ShaderInstance(
event.getResourceProvider(),
Lib39.rl("selection"),
DefaultVertexFormat.POSITION_COLOR
),
it -> selectionShader = it
);
} catch (Exception e) {
Lib39.LOGGER.error("Failed to register shader", e);
}
}
}

View File

@ -1,5 +1,11 @@
package top.r3944realms.lib39.core.event;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RegisterShadersEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.client.shader.Lib39Shaders;
/**
* The type Client handler.
*/
@ -7,7 +13,18 @@ public class ClientEventHandler {
/**
* The type Mod.
*/
public static class Mod extends ClientEventHandler {}
@net.minecraftforge.fml.common.Mod.EventBusSubscriber(value = Dist.CLIENT, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD, modid = Lib39.MOD_ID)
public static class Mod extends ClientEventHandler {
/**
* On register shaders.
*
* @param event the event
*/
@SubscribeEvent
public static void onRegisterShaders(RegisterShadersEvent event) {
Lib39Shaders.register(event);
}
}
/**
* The type Game.

View File

@ -186,6 +186,7 @@ public class CommonEventHandler {
IEventBus gameBus = MinecraftForge.EVENT_BUS;
compatManager = new CompatManager(modBus, gameBus);
MinecraftForge.EVENT_BUS.post(new RegisterCompatEvent(compatManager));
compatManager.initializeAll();
});
}

View File

@ -0,0 +1,42 @@
package top.r3944realms.lib39.datagen.provider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.loot.LootTableProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.ValidationContext;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Set;
/**
* The type Simple loot table provider.
*/
public class SimpleLootTableProvider extends LootTableProvider {
/**
* Instantiates a new Simple loot table provider.
*
* @param output the output
* @param subProvidersWrapper the sub providers wrapper
*/
public SimpleLootTableProvider(PackOutput output, @NotNull SubProvidersWrapper subProvidersWrapper) {
super(output, Set.of(), subProvidersWrapper.entries);
}
/**
* Instantiates a new Simple loot table provider.
*
* @param output the output
* @param requiredTables the required tables
* @param subProvidersWrapper the sub providers wrapper
*/
public SimpleLootTableProvider(PackOutput output, Set<ResourceLocation> requiredTables, @NotNull SubProvidersWrapper subProvidersWrapper) {
super(output, requiredTables, subProvidersWrapper.entries);
}
@Override
protected void validate(@NotNull Map<ResourceLocation, LootTable> map, @NotNull ValidationContext validationcontext) {
map.forEach((id, table) -> table.validate(validationcontext));
}
}

View File

@ -0,0 +1,46 @@
package top.r3944realms.lib39.datagen.provider;
import net.minecraft.data.loot.LootTableProvider;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import top.r3944realms.lib39.datagen.provider.subprovider.BlockLootTables;
import java.util.ArrayList;
import java.util.List;
/**
* The type Sub providers wrapper.
*/
public class SubProvidersWrapper {
/**
* The Entries.
*/
public List<LootTableProvider.SubProviderEntry> entries = new ArrayList<>();
/**
* Instantiates a new Sub providers wrapper.
*/
public SubProvidersWrapper() {}
/**
* Add entry sub providers wrapper.
*
* @param entry the entry
* @return the sub providers wrapper
*/
public SubProvidersWrapper addEntry(LootTableProvider.SubProviderEntry entry) {
entries.add(entry);
return this;
}
/**
* Add block entry sub providers wrapper.
*
* @param blockLootTables the block loot tables
* @return the sub providers wrapper
*/
public SubProvidersWrapper addBlockEntry(BlockLootTables blockLootTables) {
entries.add(new LootTableProvider.SubProviderEntry(() -> blockLootTables, LootContextParamSets.BLOCK));
return this;
}
}

View File

@ -0,0 +1,373 @@
package top.r3944realms.lib39.datagen.provider.subprovider;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.data.loot.BlockLootSubProvider;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerPotBlock;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount;
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
import net.minecraftforge.registries.RegistryObject;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
/**
* The type Block loot tables.
*/
@SuppressWarnings("unused")
public class BlockLootTables extends BlockLootSubProvider {
private final List<BlockEntry> blockEntries = new ArrayList<>();
/**
* Instantiates a new Block loot tables.
*/
public BlockLootTables() {
super(Set.of(), FeatureFlags.REGISTRY.allFlags());
}
// ==================== 流畅 API 构建方法 ====================
/**
* 添加自掉落的方块
*
* @param block the block
*/
public void dropSelf(RegistryObject<Block> block) {
addEntry(block, this::createSingleItemTable);
}
/**
* 批量添加自掉落的方块
*
* @param blocks the blocks
* @return the block loot tables
*/
@Contract("_ -> this")
@SafeVarargs
public final BlockLootTables dropSelf(RegistryObject<Block> @NotNull ... blocks) {
for (RegistryObject<Block> block : blocks) {
dropSelf(block);
}
return this;
}
/**
* 添加需要丝绸之触才掉落的方块
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropWhenSilkTouch(RegistryObject<Block> block) {
return addEntry(block, BlockLootSubProvider::createSilkTouchOnlyTable);
}
/**
* 添加掉落其他物品的方块
*
* @param block the block
* @param item the item
* @return the block loot tables
*/
public BlockLootTables dropOther(RegistryObject<Block> block, RegistryObject<? extends ItemLike> item) {
return addEntry(block, pBlock -> this.createSingleItemTable(item.get()));
}
/**
* 添加只能被剪子剪下的方块
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropWhenShears(RegistryObject<Block> block) {
return addEntry(block, BlockLootSubProvider::createShearsOnlyDrop);
}
/**
* 添加矿物的掉落表支持时运附魔
*
* @param block the block
* @param oreItem the ore item
* @return the block loot tables
*/
public BlockLootTables dropOre(RegistryObject<Block> block, RegistryObject<Item> oreItem) {
return addEntry(block, b -> this.createOreDrop(b, oreItem.get()));
}
/**
* 添加红石矿石掉落表
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropRedstoneOre(RegistryObject<Block> block) {
return addEntry(block, this::createRedstoneOreDrops);
}
/**
* 添加青金石矿石掉落表
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropLapisOre(RegistryObject<Block> block) {
return addEntry(block, this::createLapisOreDrops);
}
/**
* 添加铜矿石掉落表
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropCopperOre(RegistryObject<Block> block) {
return addEntry(block, this::createCopperOreDrops);
}
/**
* 添加地毯类方块的掉落一次掉落2个
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropCarpet(RegistryObject<Block> block) {
return addEntry(block, b -> LootTable.lootTable()
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(b)
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(2.0F))))));
}
/**
* 添加台阶方块的掉落
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropSlab(RegistryObject<Block> block) {
return addEntry(block, this::createSlabItemTable);
}
/**
* 添加门方块的掉落只掉落下半部分
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropDoor(RegistryObject<Block> block) {
return addEntry(block, this::createDoorTable);
}
/**
* 添加花盆的掉落
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables dropFlowerPot(RegistryObject<Block> block) {
return addEntry(block, (pBlock) -> this.createPotFlowerItemTable(((FlowerPotBlock)pBlock).getContent()));
}
/**
* 添加树叶的掉落
*
* @param leavesBlock the leaves block
* @param saplingBlock the sapling block
* @param chances the chances
* @return the block loot tables
*/
public BlockLootTables dropLeaves(RegistryObject<Block> leavesBlock,
RegistryObject<Block> saplingBlock,
float... chances) {
return addEntry(leavesBlock, b -> this.createLeavesDrops(b, saplingBlock.get(), chances));
}
/**
* 添加橡树叶的掉落包含苹果
*
* @param leavesBlock the leaves block
* @param saplingBlock the sapling block
* @param chances the chances
* @return the block loot tables
*/
public BlockLootTables dropOakLeaves(RegistryObject<Block> leavesBlock,
RegistryObject<Block> saplingBlock,
float... chances) {
return addEntry(leavesBlock, b -> this.createOakLeavesDrops(b, saplingBlock.get(), chances));
}
/**
* 添加农作物的掉落
*
* @param cropBlock the crop block
* @param cropItem the crop item
* @param seedsItem the seeds item
* @param ageProperty the age property
* @param maxAge the max age
* @return the block loot tables
*/
public BlockLootTables dropCrop(RegistryObject<Block> cropBlock,
RegistryObject<Item> cropItem,
RegistryObject<Item> seedsItem,
Property<Integer> ageProperty,
int maxAge) {
return addEntry(cropBlock, b -> this.createCropDrops(
b,
cropItem.get(),
seedsItem.get(),
LootItemBlockStatePropertyCondition.hasBlockStateProperties(b)
.setProperties(net.minecraft.advancements.critereon.StatePropertiesPredicate.Builder.properties()
.hasProperty(ageProperty, maxAge))
));
}
/**
* 自定义掉落表
*
* @param block the block
* @param factory the factory
* @return the block loot tables
*/
public BlockLootTables custom(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {
return addEntry(block, factory);
}
/**
* 没有掉落
*
* @param block the block
* @return the block loot tables
*/
public BlockLootTables noDrop(RegistryObject<Block> block) {
return addEntry(block, b -> noDrop());
}
// ==================== 批量操作方法 ====================
/**
* 批量操作一系列方块
*
* @param blocks the blocks
* @param operation the operation
* @return the block loot tables
*/
public BlockLootTables batch(@NotNull Iterable<RegistryObject<Block>> blocks,
Function<RegistryObject<Block>, BlockLootTables> operation) {
for (RegistryObject<Block> block : blocks) {
operation.apply(block);
}
return this;
}
/**
* 对一组方块应用相同的操作
*
* @param operation the operation
* @param blocks the blocks
* @return the block loot tables
*/
@Contract("_, _ -> this")
@SafeVarargs
public final BlockLootTables applyToAll(Function<RegistryObject<Block>, BlockLootTables> operation,
RegistryObject<Block> @NotNull ... blocks) {
for (RegistryObject<Block> block : blocks) {
operation.apply(block);
}
return this;
}
// ==================== 构建方法 ====================
/**
* 构建并返回自身用于流畅API链式调用
*
* @return the block loot tables
*/
public BlockLootTables build() {
return this;
}
@Override
protected void generate() {
for (BlockEntry entry : blockEntries) {
this.add(entry.block.get(), entry.factory);
}
}
// ==================== 内部类和方法 ====================
private BlockLootTables addEntry(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {
blockEntries.add(new BlockEntry(block, factory));
return this;
}
private record BlockEntry(RegistryObject<Block> block, Function<Block, LootTable.Builder> factory) {}
// ==================== 静态工厂方法 ====================
/**
* 创建新的战利品表生成器
*
* @return the block loot tables
*/
@Contract(" -> new")
public static @NotNull BlockLootTables create() {
return new BlockLootTables();
}
/**
* 快速创建基本矿物的掉落表
*
* @param oreBlock the ore block
* @param oreItem the ore item
* @return the loot table .@ not null builder
*/
public static LootTable.@NotNull Builder simpleOreDrop(Block oreBlock, Item oreItem) {
return LootTable.lootTable()
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(oreItem)
.when(MatchTool.toolMatches(ItemPredicate.Builder.item()
.hasEnchantment(new net.minecraft.advancements.critereon.EnchantmentPredicate(
Enchantments.SILK_TOUCH,
net.minecraft.advancements.critereon.MinMaxBounds.Ints.atLeast(1)
))).invert())
.apply(SetItemCountFunction.setCount(UniformGenerator.between(1, 1)))
.apply(ApplyBonusCount.addOreBonusCount(Enchantments.BLOCK_FORTUNE))));
}
/**
* 快速创建石质方块的掉落表需要镐子
*
* @param stoneBlock the stone block
* @param dropItem the drop item
* @return the loot table .@ not null builder
*/
public static LootTable.@NotNull Builder stoneDrop(Block stoneBlock, Item dropItem) {
return LootTable.lootTable()
.withPool(LootPool.lootPool()
.setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(dropItem)
.when(MatchTool.toolMatches(ItemPredicate.Builder.item()
.hasEnchantment(new net.minecraft.advancements.critereon.EnchantmentPredicate(
Enchantments.SILK_TOUCH,
net.minecraft.advancements.critereon.MinMaxBounds.Ints.atLeast(1)
))))));
}
}

View File

@ -0,0 +1,127 @@
package top.r3944realms.lib39.example.client.screen;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Renderable;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.chat.Component;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector2f;
import top.r3944realms.lib39.client.gui.component.WheelWidget;
import top.r3944realms.lib39.util.lang.FourConsumer;
import top.r3944realms.lib39.util.lang.Pair;
import java.util.List;
import java.util.Objects;
import static top.r3944realms.lib39.client.gui.component.WheelWidget.IGNORE_CURSOR_MOVE_LENGTH;
/**
* The type Forge screen.
*/
public class ForgeScreen extends Screen {
private final LocalPlayer player = Objects.requireNonNull(Minecraft.getInstance().player);
private final InteractionHand hand;
private final int mode;
/**
* The Wheel.
*/
public WheelWidget wheel;
/**
* Instantiates a new Forge screen.
*
* @param hand the hand
* @param mode the mode
*/
public ForgeScreen(InteractionHand hand, int mode) {
super(Component.literal("Test"));
this.hand = hand;
this.mode = mode;
}
@Override
protected void init() {
int leftPos = (this.width - 75) / 2;
int topPos = (this.height - 75) / 2;
ItemStack holding = player.getItemInHand(this.hand);
WheelWidget wheel = new WheelWidget(
leftPos, topPos, 75, 75,
12.5f, 32.5f, 0.75f,
List.of(
Pair.of(
Component.literal("auto"),
renderItem(holding)),
Pair.of(
Component.literal("axe"),
renderItem(holding)),
Pair.of(
Component.literal("shovel"),
renderItem(holding)),
Pair.of(
Component.literal("hoe"),
renderItem(holding)),
Pair.of(
Component.literal("pickaxe"),
renderItem(holding))
)
).setCurrentIndex(this.wheel != null ? this.wheel.getCurrentSectionIndex() : this.mode);
this.clearWidgets();
this.wheel = this.addRenderableWidget(wheel);
}
@Contract(pure = true)
private static @NotNull FourConsumer<GuiGraphics, PoseStack, Integer, Integer> renderItem(ItemStack holding) {
return (graphics, pose, width, height) -> {
ItemStack stack = holding.copy();
graphics.renderItem(stack, 2, 2, 9910597);
};
}
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double dragX, double dragY) {
if (wheel != null && wheel.isClosingAnimationStarted()) return true;
float screenCenterX = this.width / 2f;
float screenCenterY = this.height / 2f;
Vector2f cursorVec2 = new Vector2f(
(float) mouseX - screenCenterX,
(float) mouseY - screenCenterY
);
if (cursorVec2.length() < IGNORE_CURSOR_MOVE_LENGTH) {
return true;
}
return super.mouseDragged(mouseX, mouseY, button, dragX, dragY);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
if (wheel != null ) {
wheel.onClosing();
}
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public void removed() {
super.removed();
}
@Override
public void render(@NotNull GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTick) {
for (Renderable renderable : this.renderables) {
renderable.render(guiGraphics, mouseX, mouseY, partialTick);
}
}
@Override
public boolean isPauseScreen() {
return false;
}
}

View File

@ -0,0 +1,33 @@
package top.r3944realms.lib39.example.content.item;
import net.minecraft.client.Minecraft;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.example.client.screen.ForgeScreen;
/**
* The type Forge item.
*/
public class ForgeItem extends Item {
/**
* Instantiates a new Forge item.
*
* @param properties the properties
*/
public ForgeItem(Properties properties) {
super(properties);
}
@Override
public @NotNull InteractionResultHolder<ItemStack> use(@NotNull Level level, @NotNull Player player, @NotNull InteractionHand usedHand) {
if (level.isClientSide() && usedHand == InteractionHand.MAIN_HAND) {
Minecraft.getInstance().setScreen(new ForgeScreen(usedHand, 0));
}
return super.use(level, player, usedHand);
}
}

View File

@ -7,6 +7,7 @@ import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.example.content.item.FabricItem;
import top.r3944realms.lib39.example.content.item.ForgeItem;
import top.r3944realms.lib39.example.content.item.NeoForgeItem;
/**
@ -38,6 +39,16 @@ public class ExLib39Items {
.stacksTo(1)
.fireResistant()
));
/**
* The constant FORGE.
*/
public static final RegistryObject<Item> FORGE =
ITEMS.register("forge",
() -> new ForgeItem(
new Item.Properties()
.stacksTo(1)
.fireResistant()
));
/**
* Register.

View File

@ -40,6 +40,10 @@ public enum ExLib39LangKeys implements ILangKeyValueCollection {
ExLib39Items.NEOFORGE, ModPartEnum.ITEM,
"NeoForge", "小狐狸", "狐狸", "", true
));
addLang(LangKeyValue.ofSupplier(
ExLib39Items.FORGE, ModPartEnum.ITEM,
"Forge", "铁砧", "铁砧", "", true
));
}

View File

@ -0,0 +1,195 @@
package top.r3944realms.lib39.util;
import it.unimi.dsi.fastutil.ints.Int2DoubleMap;
import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Vec3i;
import org.joml.Vector2f;
import static java.lang.Math.*;
/**
* 直接拿铁砧工艺的
*/
public class MathUtil {
/**
* Calc a vector2 that equals to a vector2 rotated an angle
*
* @param v origin vector, wont be changed
* @param deg angle rotated, in degrees
* @return rotated vector2
*/
public static Vector2f rotationDegrees(Vector2f v, float deg) {
return rotate(v, (float) toRadians(deg));
}
/**
* Calc a vector2 that equals to a vector2 rotated an angle
*
* @param v origin vector, wont be changed
* @param d angle rotated, in radians
* @return rotated vector2
*/
public static Vector2f rotate(Vector2f v, float d) {
return new Vector2f(
(float) (v.x * cos(d) - v.y * sin(d)),
(float) (v.x * sin(d) + v.y * cos(d))
);
}
/**
* Copy vector 2 f.
*
* @param v the v
* @return the vector 2 f
*/
public static Vector2f copy(Vector2f v) {
return new Vector2f(v.x, v.y);
}
/**
* Angle float.
*
* @param from the from
* @param to the to
* @return Angle in radians
*/
public static float angle(Vector2f from, Vector2f to) {
return (float) ((atan2(to.y, to.x) - atan2(from.y, from.x)) % (Math.PI * 2));
}
/**
* Angle degrees float.
*
* @param from the from
* @param to the to
* @return Angle in degrees
*/
public static float angleDegrees(Vector2f from, Vector2f to) {
return (float) toDegrees(angle(from, to));
}
/**
* Safe divide float.
*
* @param a the a
* @param b the b
* @return the float
*/
public static float safeDivide(float a, float b) {
if (a == b) return 1;
return a / b;
}
/**
* Is in range boolean.
*
* @param value the value
* @param min the min
* @param max the max
* @return the boolean
*/
public static boolean isInRange(double value, double min, double max) {
if (min > max) {
double min1 = min;
min = max;
max = min1;
}
return value > min && value < max;
}
/**
* Is in range boolean.
*
* @param valueX the value x
* @param valueY the value y
* @param minX the min x
* @param minY the min y
* @param maxX the max x
* @param maxY the max y
* @return the boolean
*/
public static boolean isInRange(double valueX, double valueY, double minX, double minY, double maxX, double maxY) {
if (minX > maxX) {
double minX1 = minX;
minX = maxX;
maxX = minX1;
}
if (minY > maxY) {
double minY1 = minY;
minY = maxY;
maxY = minY1;
}
return valueX > minX && valueX < maxX && valueY > minY && valueY < maxY;
}
/**
* Dist vec 3 i.
*
* @param a the a
* @param b the b
* @return the vec 3 i
*/
public static Vec3i dist(BlockPos a, BlockPos b) {
return new Vec3i(a.getX() - b.getX(), a.getY() - b.getY(), a.getZ() - b.getZ());
}
/**
* Gets direction.
*
* @param from the from
* @param to the to
* @return the direction
*/
public static Direction getDirection(BlockPos from, BlockPos to) {
return Direction.fromDelta(from.getX() - to.getX(), from.getY() - to.getY(), from.getZ() - to.getZ());
}
private static final Int2DoubleMap FACTORIAL_CACHE = new Int2DoubleOpenHashMap();
/**
* Factorial double.
*
* @param value the value
* @return the double
*/
public static double factorial(int value) {
if (value < 1) return 1;
if (FACTORIAL_CACHE.containsKey(value)) return FACTORIAL_CACHE.get(value);
double result = 1;
for (int i = 2; i <= value; i++) {
result *= i;
}
FACTORIAL_CACHE.put(value, result);
return result;
}
/**
* Clamp with proportion float.
*
* @param value the value
* @param min the min
* @param max the max
* @return the float
*/
public static float clampWithProportion(float value, float min, float max) {
float length = Math.abs(max - min);
if (length == 0) throw new IllegalArgumentException("The min value " + min + " cannot be equal to the max value" + max + "!");
if (value > max) {
while (value > max + length) {
value -= length;
}
return max - (max - value);
} else if (value < min) {
while (value < min + length) {
value += length;
}
return min + (value - min);
}
return value;
}
}

View File

@ -0,0 +1,7 @@
package top.r3944realms.lib39.util.command;
/**
* The type Command help helper.
*/
public class CommandHelpHelper {
}

View File

@ -0,0 +1,36 @@
package top.r3944realms.lib39.util.lang;
/**
* The interface Four consumer.
*
* @param <X> the type parameter
* @param <Y> the type parameter
* @param <Z> the type parameter
* @param <W> the type parameter
*/
public interface FourConsumer<X, Y, Z, W> {
/**
* Accept.
*
* @param x the x
* @param y the y
* @param z the z
* @param w the w
*/
void accept(X x, Y y, Z z, W w);
/**
* Noop.
*
* @param <X> the type parameter
* @param <Y> the type parameter
* @param <Z> the type parameter
* @param <W> the type parameter
* @param x the x
* @param y the y
* @param z the z
* @param w the w
*/
static <X, Y, Z, W> void noop(X x, Y y, Z z, W w) {
}
}

View File

@ -585,6 +585,21 @@ public class NBTWriter {
return this;
}
/**
* Uuid value if nbt writer.
*
* @param key the key
* @param value the value
* @param condition the condition
* @return the nbt writer
*/
public NBTWriter uuidValueIf(String key, UUID value, boolean condition) {
if (condition && value != null) {
root.putUUID(key, value);
}
return this;
}
/**
* Boolean value if nbt writer.
*

View File

@ -0,0 +1,161 @@
package top.r3944realms.lib39.util.resolve;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import java.util.ArrayList;
import java.util.List;
/**
* The type Entity list resolve.
*/
public abstract class EntityListResolve {
/**
* The Result.
*/
protected EntityListResolve.EntityResolveResult result;
/**
* The type Entity resolve result.
*/
public static class EntityResolveResult {
/**
* The Entity list.
*/
protected final List<String> entityList = new ArrayList<>();
/**
* The Tag list.
*/
protected final List<String> tagList = new ArrayList<>();
/**
* The Mod list.
*/
protected final List<String> modList = new ArrayList<>();
/**
* The enum Type.
*/
public enum Type {
/**
* Entity type.
*/
ENTITY,
/**
* Tag type.
*/
TAG,
/**
* Mod type.
*/
MOD
}
/**
* Gets map.
*
* @param type the type
* @return the map
*/
public List<String> getMap(@NotNull Type type) {
return switch (type) {
case ENTITY -> entityList;
case TAG -> tagList;
case MOD -> modList;
};
}
/**
* Update.
*
* @param entity the entity
* @param tag the tag
* @param mod the mod
*/
public void update(List<String> entity, List<String> tag, List<String> mod) {
entityList.clear();
entityList.addAll(entity);
tagList.clear();
tagList.addAll(tag);
modList.clear();
modList.addAll(mod);
}
}
/**
* Resolve entity resolve result.
*
* @param configs the configs
* @return the entity resolve result
*/
public EntityResolveResult resolve(@NotNull List<String> configs) {
List<String> entityList = new ArrayList<>();
List<String> tagList = new ArrayList<>();
List<String> modList = new ArrayList<>();
for (String config : configs) {
if (!isMatch(config)) continue;
try {
String[] entities = resolveEntities(config);
for (String e : entities) {
String trimmed = e.trim();
if (trimmed.equals("*")) modList.add("*");
else if (trimmed.startsWith("#")) {
String body = trimmed.substring(1).trim();
if (body.contains(":")) tagList.add(body);
else modList.add(body);
} else entityList.add(trimmed);
}
result.update(entityList, tagList, modList);
} catch (NumberFormatException ex) {
Lib39.LOGGER.error("Invalid offset config: {}", config);
}
}
return result;
}
/**
* Is match boolean.
*
* @param input the input
* @return the boolean
*/
protected abstract boolean isMatch(String input);
/**
* Resolve entities string [ ].
*
* @param input the input
* @return the string [ ]
*/
protected abstract String[] resolveEntities(String input);
/**
* Is entity in list boolean.
*
* @param type the type
* @return the boolean
*/
@SuppressWarnings("deprecation")
public boolean isEntityInList(EntityType<?> type) {
String entityId = type.builtInRegistryHolder().key().location().toString();
String modId = entityId.split(":")[0];
for (String rs : result.entityList) {
if (rs.equals(entityId)) return true;
}
for(String rs : result.tagList) {
String body = rs.substring(1);
ResourceLocation tagId = new ResourceLocation(body);
TagKey<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
if (type.builtInRegistryHolder().is(tag)) return true;
}
for(String rs : result.modList) {
String body = rs.substring(1);
if (!body.contains(":") && body.equals(modId)) return true;
}
return false;
}
}

View File

@ -0,0 +1,214 @@
package top.r3944realms.lib39.util.resolve;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.EntityType;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The type Entity map resolve.
*
* @param <T> the type parameter
*/
public abstract class EntityMapResolve<T> {
/**
* The Result.
*/
protected EntityResolveResult<T> result;
/**
* The type Entity resolve result.
*
* @param <T> the type parameter
*/
public static class EntityResolveResult<T> {
/**
* The Entity map.
*/
protected final Map<String, T> entityMap = new HashMap<>();
/**
* The Tag map.
*/
protected final Map<String, T> tagMap = new HashMap<>();
/**
* The Mod map.
*/
protected final Map<String, T> modMap = new HashMap<>();
/**
* The enum Type.
*/
public enum Type {
/**
* Entity type.
*/
ENTITY,
/**
* Tag type.
*/
TAG,
/**
* Mod type.
*/
MOD
}
/**
* Gets map.
*
* @param type the type
* @return the map
*/
public Map<String, T> getMap(@NotNull Type type) {
return switch (type) {
case ENTITY -> entityMap;
case TAG -> tagMap;
case MOD -> modMap;
};
}
/**
* Update.
*
* @param entity the entity
* @param tag the tag
* @param mod the mod
*/
public void update(Map<String, T> entity, Map<String, T> tag, Map<String, T> mod) {
entityMap.clear();
entityMap.putAll(entity);
tagMap.clear();
tagMap.putAll(tag);
modMap.clear();
modMap.putAll(mod);
}
}
/**
* Resolve entity resolve result.
*
* @param configs the configs
* @return the entity resolve result
*/
public EntityResolveResult<T> resolve(@NotNull List<String> configs) {
Map<String, T> entityMap = new HashMap<>();
Map<String, T> tagMap = new HashMap<>();
Map<String, T> modMap = new HashMap<>();
for (String config : configs) {
if (!isMatch(config)) continue;
try {
T t = resolveT(config);
String[] entities = resolveEntities(config);
for (String e : entities) {
String trimmed = e.trim();
if (trimmed.equals("*")) modMap.put("*", t);
else if (trimmed.startsWith("#")) {
String body = trimmed.substring(1).trim();
if (body.contains(":")) tagMap.put(body, t);
else modMap.put(body, t);
} else entityMap.put(trimmed, t);
}
result.update(entityMap, tagMap, modMap);
} catch (NumberFormatException ex) {
Lib39.LOGGER.error("Invalid offset config: {}", config);
}
}
return result;
}
/**
* Is match boolean.
*
* @param input the input
* @return the boolean
*/
protected abstract boolean isMatch(String input);
/**
* Resolve t t.
*
* @param input the input
* @return the t
*/
protected abstract T resolveT(String input);
/**
* Resolve entities string [ ].
*
* @param input the input
* @return the string [ ]
*/
protected abstract String[] resolveEntities(String input);
/**
* 查找实体对应的值如果找到返回匹配结果否则返回null
*/
@SuppressWarnings("deprecation")
private EntityMatchResult<T> findEntityMatch(EntityType<?> type) {
String entityId = type.builtInRegistryHolder().key().location().toString();
String modId = entityId.split(":")[0];
// 检查实体ID匹配
for (String rs : result.entityMap.keySet()) {
if (rs.equals(entityId)) {
return new EntityMatchResult<>(EntityResolveResult.Type.ENTITY, rs, result.entityMap.get(rs));
}
}
// 检查标签匹配
for (String rs : result.tagMap.keySet()) {
String body = rs.startsWith("#") ? rs.substring(1) : rs;
ResourceLocation tagId = new ResourceLocation(body);
TagKey<EntityType<?>> tag = TagKey.create(Registries.ENTITY_TYPE, tagId);
if (type.builtInRegistryHolder().is(tag)) {
return new EntityMatchResult<>(EntityResolveResult.Type.TAG, rs, result.tagMap.get(rs));
}
}
// 检查模组匹配
for (String rs : result.modMap.keySet()) {
String body = rs.startsWith("#") ? rs.substring(1) : rs;
if (!body.contains(":") && body.equals(modId)) {
return new EntityMatchResult<>(EntityResolveResult.Type.MOD, rs, result.modMap.get(rs));
}
}
return null;
}
/**
* Is entity t in map boolean.
*
* @param type the type
* @return the boolean
*/
public boolean isEntityTInMap(EntityType<?> type) {
return findEntityMatch(type) != null;
}
/**
* Gets entity t in map.
*
* @param type the type
* @return the entity t in map
*/
public T getEntityTInMap(EntityType<?> type) {
EntityMatchResult<T> match = findEntityMatch(type);
return match != null ? match.value : null;
}
/**
* 内部类用于存储实体匹配结果
*/
private record EntityMatchResult<T>(EntityResolveResult.Type type, String key, T value) {
}
}

View File

@ -0,0 +1,49 @@
#version 150
in vec4 vertexColor;
uniform vec4 ColorModulator;
uniform vec2 Center;
uniform float InnerRadius;
uniform float OuterRadius;
uniform float AntiAliasing;
out vec4 fragColor;
void main() {
float dist = distance(gl_FragCoord.xy, Center);
float alpha = 0.0;
// 确保内外半径合理
if (OuterRadius <= InnerRadius) {
vec4 color = vertexColor;
color.a = 0;
fragColor = color;
}
// 计算环形 alpha
if (dist >= InnerRadius && dist <= OuterRadius) {
alpha = 1.0;
// 内边缘抗锯齿
if (dist < InnerRadius + AntiAliasing) {
float fade = (dist - InnerRadius) / AntiAliasing;
alpha *= fade;
}
// 外边缘抗锯齿
if (dist > OuterRadius - AntiAliasing) {
float fade = 1.0 - (dist - (OuterRadius - AntiAliasing)) / AntiAliasing;
alpha *= fade;
}
}
vec4 color = vertexColor;
color.a *= alpha;
if (alpha > 0.0) {
fragColor = color * ColorModulator;
} else {
discard;
}
}

View File

@ -0,0 +1,44 @@
{
"vertex": "position_color",
"fragment": "lib39:ring",
"samplers": [
],
"uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
{
"name": "Center",
"type": "float",
"count": 2,
"values": [
0.0,
0.0
]
},
{
"name": "InnerRadius",
"type": "float",
"count": 1,
"values": [
0.0
]
},
{
"name": "OuterRadius",
"type": "float",
"count": 1,
"values": [
0.0
]
},
{
"name": "AntiAliasing",
"type": "float",
"count": 1,
"values": [
1.25
]
}
]
}

View File

@ -0,0 +1,23 @@
#version 150
in vec4 vertexColor;
uniform vec4 ColorModulator;
uniform vec2 FramebufferSize;
uniform vec2 Center;
uniform float Radius;
uniform float AntiAliasingRadius;
out vec4 fragColor;
void main() {
vec2 fragPos = vec2(gl_FragCoord.x, FramebufferSize.y - gl_FragCoord.y);
float distance = distance(fragPos, Center);
vec4 color = vec4(0, 0, 0, 0);
if (distance <= Radius) {
color = vertexColor;
color.a = smoothstep(Radius, 0.0, distance) * vertexColor.a;
}
fragColor = color * ColorModulator;
}

View File

@ -0,0 +1,45 @@
{
"vertex": "position_color",
"fragment": "lib39:selection",
"samplers": [
],
"uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
{
"name": "Center",
"type": "float",
"count": 2,
"values": [
0.0,
0.0
]
},
{
"name": "FramebufferSize",
"type": "float",
"count": 2,
"values": [
0.0,
0.0
]
},
{
"name": "Radius",
"type": "float",
"count": 1,
"values": [
0.0
]
},
{
"name": "AntiAliasingRadius",
"type": "float",
"count": 1,
"values": [
1.5
]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB