fix glow for 1.21.11

This commit is contained in:
seb
2026-05-11 03:03:40 +02:00
parent 19a85628f2
commit cf97ebe11b
5 changed files with 166 additions and 61 deletions

View File

@@ -3,7 +3,7 @@ import java.time.format.DateTimeFormatter
import org.gradle.jvm.toolchain.JavaLanguageVersion
plugins {
id("dev.isxander.modstitch.base") version "0.8.4"
id("dev.isxander.modstitch.base") version "0.8.5"
id("com.modrinth.minotaur") version "2.+"
}
@@ -84,6 +84,12 @@ stonecutter {
"forge" to loader.equals("forge", ignoreCase = true),
"vanilla" to loader.equals("vanilla", ignoreCase = true),
"mc26" to (minecraft == "26.1.2"),
// TraderHighlightRenderer: MC 26 uses Fabric LevelRenderEvents; 1.21.x uses WorldRenderEvents (v1.world).
"traderWireframeRender" to (minecraft == "26.1.2" || minecraft == "1.21.11" || minecraft == "1.21.10"),
"traderWireframeMc26" to (minecraft == "26.1.2"),
"traderWireframe121" to (minecraft == "1.21.10" || minecraft == "1.21.11"),
"traderWireframe12110" to (minecraft == "1.21.10"),
"traderWireframe12111" to (minecraft == "1.21.11"),
"npcSplit" to (minecraft == "26.1.2" || minecraft == "1.21.11"),
"npcFlat" to (minecraft == "1.21.10"),
)

View File

@@ -11,6 +11,7 @@ import java.util.List;
import java.util.Vector;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.inventory.ContainerScreen;
import net.minecraft.network.chat.Component;
import net.minecraft.client.gui.screens.inventory.MerchantScreen;
import net.minecraft.client.gui.screens.inventory.ShulkerBoxScreen;
import net.minecraft.core.BlockPos;
@@ -48,7 +49,6 @@ final class AutoTradeClientTick {
private final Vector<Entity> villagersInRange = new Vector<>();
private int villagerActive = 0;
private boolean state = false;
private boolean inputInRange = false;
private boolean inputOpened = false;
private boolean outputInRange = false;
@@ -68,6 +68,7 @@ final class AutoTradeClientTick {
private int inputContainerHighlightTicks = 0;
private int outputContainerHighlightTicks = 0;
private int postMerchantInventorySyncTicks = 0;
/**
* Entity to draw in-world highlight for; {@code null} when inactive or unknown
@@ -102,7 +103,14 @@ final class AutoTradeClientTick {
if (containerDelay > 0) {
containerDelay--;
}
if (!Configs.Generic.ENABLED.getBooleanValue() || mc.player == null) {
if (mc.player == null) {
return;
}
if (postMerchantInventorySyncTicks > 0) {
postMerchantInventorySyncTicks--;
ContainerIoHelper.syncPlayerInventoryAfterMerchant(mc);
}
if (!Configs.Generic.ENABLED.getBooleanValue()) {
return;
}
Inventory plInv = mc.player.getInventory();
@@ -158,9 +166,9 @@ final class AutoTradeClientTick {
//?} else {
mc.gameMode.interact(mc.player, entity, InteractionHand.MAIN_HAND);
//?}
postMerchantInventorySyncTicks = 0;
voidDelay = Configs.Generic.VOID_TRADING_DELAY.getIntegerValue();
villagerActive = entity.getId();
state = false;
break;
}
}
@@ -296,64 +304,101 @@ final class AutoTradeClientTick {
}
private void tickMerchantScreen(Minecraft mc, MerchantScreen screen) {
if (!state) {
String sellItemStr = Configs.Generic.SELL_ITEM.getStringValue();
String buyItemStr = Configs.Generic.BUY_ITEM.getStringValue();
state = true;
MerchantMenu menu = screen.getMenu();
MerchantOffers offers = menu.getOffers();
MerchantMenu menu = screen.getMenu();
MerchantOffers offers = menu.getOffers();
// Cache offers for the in-world trade overlay.
Entity activeEntity = findEntityById(mc, villagerActive);
if (activeEntity != null && offers != null && !offers.isEmpty()) {
VillagerTradeCache.put(activeEntity.getUUID(), offers);
}
for (int i = 0; i < offers.size(); i++) {
MerchantOffer offer = offers.get(i);
int tradesLeft = offer.getMaxUses() - offer.getUses();
if (TradeItemSpec.matches(offer.getResult(), buyItemStr) && Configs.Generic.ENABLE_BUY.getBooleanValue()
&& offer.getResult().getCount() <= Configs.Generic.BUY_LIMIT.getIntegerValue()) {
if (tradesLeft > 0 && playerHasMerchantCosts(mc.player, offer)) {
Slot slot = menu.getSlot(2);
menu.setSelectionHint(i);
mc.player.connection.send(new ServerboundSelectTradePacket(i));
AutoTrade.bought += offer.getMaxUses();
InfoUtils.showGuiOrInGameMessage(Message.MessageType.INFO, "autotrade.message.trade_bought",
formatItemCountNameForTrades(offer.getResult(), tradesLeft),
formatOfferPriceForTrades(offer, tradesLeft));
try {
ContainerIoHelper.quickMoveResultSlot(mc, menu, slot.index);
} catch (Exception e) {
System.out.println("err " + e);
}
Entity activeEntity = findEntityById(mc, villagerActive);
if (activeEntity != null && offers != null && !offers.isEmpty()) {
VillagerTradeCache.put(activeEntity.getUUID(), offers);
}
if (tryExecuteOneMerchantTrade(mc, screen)) {
ContainerIoHelper.syncPlayerInventoryAfterMerchant(mc);
return;
}
finishMerchantSession(mc, screen);
}
/**
* Runs at most <strong>one</strong> trade per tick so the server can answer before the next
* packet — avoids inventory slot ghosts from batched select-trade + shift-clicks.
*/
private boolean tryExecuteOneMerchantTrade(Minecraft mc, MerchantScreen screen) {
MerchantMenu menu = screen.getMenu();
MerchantOffers offers = menu.getOffers();
if (offers == null || offers.isEmpty()) {
return false;
}
String sellItemStr = Configs.Generic.SELL_ITEM.getStringValue();
String buyItemStr = Configs.Generic.BUY_ITEM.getStringValue();
for (int i = 0; i < offers.size(); i++) {
MerchantOffer offer = offers.get(i);
int tradesLeft = offer.getMaxUses() - offer.getUses();
if (TradeItemSpec.matches(offer.getResult(), buyItemStr) && Configs.Generic.ENABLE_BUY.getBooleanValue()
&& offer.getResult().getCount() <= Configs.Generic.BUY_LIMIT.getIntegerValue()) {
if (tradesLeft > 0 && playerHasMerchantCosts(mc.player, offer)) {
Slot slot = menu.getSlot(2);
menu.setSelectionHint(i);
mc.player.connection.send(new ServerboundSelectTradePacket(i));
AutoTrade.bought += offer.getMaxUses();
showTradeNotice(mc, "autotrade.message.trade_bought",
Component.literal(formatItemCountNameForTrades(offer.getResult(), tradesLeft)),
Component.literal(formatOfferPriceForTrades(offer, tradesLeft)));
try {
ContainerIoHelper.quickMoveResultSlot(mc, menu, slot.index);
} catch (Exception e) {
System.out.println("err " + e);
}
return true;
}
if (TradeItemSpec.matches(offer.getCostA(), sellItemStr)
&& Configs.Generic.ENABLE_SELL.getBooleanValue()
&& offer.getCostA().getCount() <= Configs.Generic.SELL_LIMIT.getIntegerValue()) {
if (tradesLeft > 0 && playerHasMerchantCosts(mc.player, offer)) {
Slot slot = menu.getSlot(2);
menu.setSelectionHint(i);
AutoTrade.sold += offer.getMaxUses();
mc.player.connection.send(new ServerboundSelectTradePacket(i));
InfoUtils.showGuiOrInGameMessage(Message.MessageType.INFO, "autotrade.message.trade_sold",
formatItemCountNameForTrades(offer.getCostA(), tradesLeft)
+ formatOptionalSecondCostForTrades(offer, tradesLeft),
formatItemCountNameForTrades(offer.getResult(), tradesLeft));
try {
ContainerIoHelper.quickMoveResultSlot(mc, menu, slot.index);
} catch (Exception e) {
System.out.println("err " + e);
}
}
if (TradeItemSpec.matches(offer.getCostA(), sellItemStr)
&& Configs.Generic.ENABLE_SELL.getBooleanValue()
&& offer.getCostA().getCount() <= Configs.Generic.SELL_LIMIT.getIntegerValue()) {
if (tradesLeft > 0 && playerHasMerchantCosts(mc.player, offer)) {
Slot slot = menu.getSlot(2);
menu.setSelectionHint(i);
mc.player.connection.send(new ServerboundSelectTradePacket(i));
AutoTrade.sold += offer.getMaxUses();
showTradeNotice(mc, "autotrade.message.trade_sold",
Component.literal(formatItemCountNameForTrades(offer.getCostA(), tradesLeft)
+ formatOptionalSecondCostForTrades(offer, tradesLeft)),
Component.literal(formatItemCountNameForTrades(offer.getResult(), tradesLeft)));
try {
ContainerIoHelper.quickMoveResultSlot(mc, menu, slot.index);
} catch (Exception e) {
System.out.println("err " + e);
}
return true;
}
}
}
return false;
}
private void finishMerchantSession(Minecraft mc, MerchantScreen screen) {
ContainerIoHelper.syncPlayerInventoryAfterMerchant(mc);
screen.onClose();
ContainerIoHelper.syncPlayerInventoryAfterMerchant(mc);
postMerchantInventorySyncTicks = 15;
startTraderGlow(mc, villagerActive);
}
/**
* Malilib's {@code showGuiOrInGameMessage} routes to multiple HUD targets; trade spam looked like 3× duplication.
* Vanilla overlay is a single on-screen line (same idea as vanilla toast-adjacent hints).
*/
private static void showTradeNotice(Minecraft mc, String translationKey, Component arg1, Component arg2) {
if (mc.gui == null) {
return;
}
mc.gui.setOverlayMessage(Component.translatable(translationKey, arg1, arg2), false);
}
private void tickTraderGlow(Minecraft mc) {
if (mc.level == null || traderGlowTicksRemaining <= 0) {
return;

View File

@@ -1,16 +1,13 @@
package com.github.sebseb7.autotrade.render;
//? if mc26 {
//? if traderWireframeRender {
import com.github.sebseb7.autotrade.config.Configs;
import com.github.sebseb7.autotrade.event.KeybindCallbacks;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.ShapeRenderer;
import net.minecraft.client.renderer.rendertype.RenderTypes;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
@@ -18,6 +15,20 @@ import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.Shapes;
//?}
//? if traderWireframeMc26 {
import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents;
//?}
//? if traderWireframe121 {
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents;
//?}
//? if traderWireframe12110 {
import net.minecraft.client.renderer.RenderType;
//?}
//? if traderWireframeMc26 || traderWireframe12111 {
import net.minecraft.client.renderer.rendertype.RenderTypes;
//?}
/**
* Client wireframe highlights: last-traded villager, and input/output container
@@ -30,21 +41,27 @@ public final class TraderHighlightRenderer {
}
public static void register() {
//? if mc26 {
LevelRenderEvents.AFTER_SOLID_FEATURES.register(TraderHighlightRenderer::renderLevel);
//? if traderWireframeMc26 {
LevelRenderEvents.AFTER_SOLID_FEATURES.register(TraderHighlightRenderer::renderLevelMc26);
//?}
//? if traderWireframe121 {
WorldRenderEvents.END_MAIN.register(TraderHighlightRenderer::renderWorld121);
//?}
}
//? if mc26 {
//? if traderWireframeRender {
private static final ShapeRenderer SHAPE_RENDERER = new ShapeRenderer();
private static final int TRADER_OUTLINE_COLOR = 0xFF66FF66;
private static final int INPUT_CONTAINER_COLOR = 0xFFFF6666;
private static final int OUTPUT_CONTAINER_COLOR = 0xFF6666FF;
//? if traderWireframeMc26 || traderWireframe12111 {
private static final float LINE_WIDTH = 2.5F;
//?}
private static void renderLevel(LevelRenderContext context) {
//? if traderWireframeMc26 {
private static void renderLevelMc26(LevelRenderContext context) {
Minecraft mc = Minecraft.getInstance();
if (mc.level == null) {
return;
@@ -65,6 +82,38 @@ public final class TraderHighlightRenderer {
renderBoxes(mc, drawPose, consumer, camera, tickDelta, trader, inTicks, outTicks);
}
//?}
//? if traderWireframe121 {
private static void renderWorld121(WorldRenderContext context) {
Minecraft mc = Minecraft.getInstance();
if (mc.level == null) {
return;
}
KeybindCallbacks kb = KeybindCallbacks.getInstance();
Entity trader = kb.getTraderHighlightEntity(mc);
int inTicks = kb.getInputContainerHighlightTicks();
int outTicks = kb.getOutputContainerHighlightTicks();
if (trader == null && inTicks <= 0 && outTicks <= 0) {
return;
}
var vertexConsumers = context.consumers();
PoseStack drawPose = new PoseStack();
Vec3 camera = mc.gameRenderer.getMainCamera().position();
float tickDelta = mc.getDeltaTracker().getGameTimeDeltaPartialTick(true);
final VertexConsumer consumer;
//? if traderWireframe12110 {
consumer = vertexConsumers.getBuffer(RenderType.lines());
//?}
//? if traderWireframe12111 {
consumer = vertexConsumers.getBuffer(RenderTypes.lines());
//?}
renderBoxes(mc, drawPose, consumer, camera, tickDelta, trader, inTicks, outTicks);
}
//?}
private static void renderBoxes(Minecraft mc, PoseStack drawPose, VertexConsumer consumer, Vec3 camera,
float tickDelta, Entity trader, int inTicks, int outTicks) {
@@ -100,8 +149,13 @@ public final class TraderHighlightRenderer {
}
private static void renderShape(PoseStack drawPose, VertexConsumer consumer, AABB cameraRelative, int color) {
//? if traderWireframe12110 {
SHAPE_RENDERER.renderShape(drawPose, consumer, Shapes.create(cameraRelative), 0.0D, 0.0D, 0.0D, color);
//?}
//? if traderWireframeMc26 || traderWireframe12111 {
SHAPE_RENDERER.renderShape(drawPose, consumer, Shapes.create(cameraRelative), 0.0D, 0.0D, 0.0D, color,
LINE_WIDTH);
//?}
}
//?}
}

View File

@@ -1,7 +1,7 @@
modstitch.platform=fabric-loom-remap
deps.minecraft=1.21.10
fabric_loader_version=0.19.2
fabric_loader_version=0.18.3
fabric_api_version=0.138.4+1.21.10
malilib_version=0.26.8
mod_menu_version=16.0.1

View File

@@ -2,7 +2,7 @@ modstitch.platform=fabric-loom-remap
deps.minecraft=1.21.11
fabric_loader_version=0.19.2
fabric_api_version=0.136.0+1.21.11
malilib_version=0.27.3
fabric_api_version=0.141.3+1.21.11
malilib_version=0.27.9
mod_menu_version=17.0.0
minecraft_version_min=1.21.11