Fix MC 26.2 / Fabric API 0.152.2 build: strip world+HUD overlays, use new keymapping API
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing

MC 26.2 overhauled rendering significantly. The classes we used don't
exist anymore:

- MultiBufferSource was removed; world line drawing now goes through
  SubmitNodeCollector via LevelRenderEvents.COLLECT_SUBMITS — a much
  larger rewrite. Defer until next iteration.
- HudLayerRegistrationCallback / IdentifiedLayer / LayeredDraw — replaced
  by HudElementRegistry / HudElement / VanillaHudElements. Compass and
  watermark deferred until next iteration.
- fabric-key-binding-api-v1 module renamed to fabric-key-mapping-api-v1.
  Class is now KeyMappingHelper at net.fabricmc.fabric.api.client.keymapping.v1.
- KeyMapping ctor: last arg is KeyMapping.Category, not a String.
- BlockPos#getCenter removed — construct Vec3 manually.
- ResourceKey#location renamed to identifier (returns Identifier).
- ResourceLocation was renamed to Identifier (in 26.1).

Friends/markers/chat/tab-complete still work; marker_set from friends
shows up as a chat line "[IRC] marker from X @ x, y, z" instead of an
in-world beacon for this iteration.
This commit is contained in:
2026-06-22 11:40:55 +06:00
parent be277c08c1
commit 6f75e29ac6
5 changed files with 10 additions and 257 deletions

1
.gitignore vendored
View File

@@ -5,4 +5,5 @@ run/
*.iml
.idea/
*.class
.primer_*.md

View File

@@ -39,8 +39,6 @@ public final class ClientIrcMod implements ClientModInitializer {
FriendsManager.init(configDir);
service = new IrcService(config, LOGGER);
OverlayHud.init();
WorldOverlayRenderer.init();
MiddleClickHandler.init(() -> service, () -> resolvePlayerName(Minecraft.getInstance()));
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
@@ -82,7 +80,8 @@ public final class ClientIrcMod implements ClientModInitializer {
MarkerManager.set(new MarkerManager.Marker(
ms.sender(), ms.dimensionId(), ms.x(), ms.y(), ms.z(), System.currentTimeMillis()
));
pushClientChat(client, Component.literal("[IRC] marker from " + ms.sender()).withStyle(ChatFormatting.LIGHT_PURPLE));
String coords = String.format(java.util.Locale.ROOT, "%.0f, %.0f, %.0f", ms.x(), ms.y(), ms.z());
pushClientChat(client, Component.literal("[IRC] marker from " + ms.sender() + " @ " + coords).withStyle(ChatFormatting.LIGHT_PURPLE));
}
}
case IrcService.IrcEvent.MarkerRemove mr -> {

View File

@@ -5,9 +5,8 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
@@ -26,13 +25,12 @@ public final class MiddleClickHandler {
private MiddleClickHandler() {}
public static void init(Supplier<IrcService> serviceSupplier, Supplier<String> playerNameSupplier) {
markerKey = new KeyMapping(
markerKey = KeyMappingHelper.registerKeyMapping(new KeyMapping(
"key.clientirc.marker",
InputConstants.Type.MOUSE,
GLFW.GLFW_MOUSE_BUTTON_MIDDLE,
"category.clientirc"
);
KeyBindingHelper.registerKeyBinding(markerKey);
KeyMapping.Category.MISC
));
ClientTickEvents.END_CLIENT_TICK.register(client -> {
while (markerKey.consumeClick()) {
@@ -40,14 +38,15 @@ public final class MiddleClickHandler {
Vec3 target;
if (client.hitResult instanceof BlockHitResult br && br.getType() != HitResult.Type.MISS) {
target = br.getBlockPos().getCenter();
var bp = br.getBlockPos();
target = new Vec3(bp.getX() + 0.5, bp.getY() + 0.5, bp.getZ() + 0.5);
} else {
Vec3 eye = client.player.getEyePosition();
Vec3 look = client.player.getViewVector(1.0f);
target = eye.add(look.scale(20.0));
}
String dimId = client.level.dimension().location().toString();
String dimId = client.level.dimension().identifier().toString();
String playerName = playerNameSupplier.get();
double tx = target.x, ty = target.y, tz = target.z;

View File

@@ -1,126 +0,0 @@
package ru.nevetime.clientirc;
import com.mojang.authlib.GameProfile;
import net.fabricmc.fabric.api.client.rendering.v1.HudLayerRegistrationCallback;
import net.fabricmc.fabric.api.client.rendering.v1.IdentifiedLayer;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.LayeredDraw;
import net.minecraft.client.multiplayer.PlayerInfo;
import net.minecraft.resources.ResourceLocation;
public final class OverlayHud {
private static final ResourceLocation COMPASS_LAYER = ResourceLocation.fromNamespaceAndPath("clientirc", "compass");
private static final ResourceLocation WATERMARK_LAYER = ResourceLocation.fromNamespaceAndPath("clientirc", "watermark");
private static final int COMPASS_WIDTH = 180;
private static final int COMPASS_HEIGHT = 14;
private static final int COMPASS_Y = 2;
private OverlayHud() {
}
public static void init() {
HudLayerRegistrationCallback.EVENT.register(layered -> {
layered.attachLayerAfter(IdentifiedLayer.MISC_OVERLAYS, COMPASS_LAYER, (LayeredDraw.Layer) OverlayHud::renderCompass);
layered.attachLayerAfter(IdentifiedLayer.MISC_OVERLAYS, WATERMARK_LAYER, (LayeredDraw.Layer) OverlayHud::renderWatermark);
});
}
private static boolean skip(Minecraft mc) {
if (mc == null) {
return true;
}
if (mc.options != null && mc.options.hideGui) {
return true;
}
return mc.level == null;
}
private static void renderCompass(GuiGraphics graphics, DeltaTracker deltaTracker) {
Minecraft mc = Minecraft.getInstance();
if (skip(mc)) {
return;
}
Font font = mc.font;
int x = (graphics.guiWidth() - COMPASS_WIDTH) / 2;
int y = COMPASS_Y;
graphics.fill(x, y, x + COMPASS_WIDTH, y + COMPASS_HEIGHT, 0x80000000);
float yaw = mc.gameRenderer.getMainCamera().getYRot();
int[] cardYaws = {180, -90, 0, 90};
String[] cardLabels = {"N", "E", "S", "W"};
for (int i = 0; i < cardYaws.length; i++) {
float delta = wrapDegrees(cardYaws[i] - yaw);
if (Math.abs(delta) > 90.0f) {
continue;
}
int px = x + 90 + (int) delta;
int color = Math.abs(delta) <= 5.0f ? 0xFFFFFF55 : 0xFFFFFFFF;
int labelWidth = font.width(cardLabels[i]);
graphics.drawString(font, cardLabels[i], px - labelWidth / 2, y + 3, color, true);
}
graphics.fill(x + 89, y + 11, x + 92, y + 14, 0xFFFFFFFF);
}
private static void renderWatermark(GuiGraphics graphics, DeltaTracker deltaTracker) {
Minecraft mc = Minecraft.getInstance();
if (skip(mc)) {
return;
}
Font font = mc.font;
int x = 4;
int y = 4;
int lineHeight = font.lineHeight + 2;
String nick = resolveNick(mc);
int fps = mc.getFps();
int ping = resolvePing(mc);
graphics.drawString(font, nick, x, y, 0xFFFFFFFF, true);
graphics.drawString(font, "FPS: " + fps, x, y + lineHeight, 0xFFFFFFFF, true);
graphics.drawString(font, "Ping: " + ping + " ms", x, y + 2 * lineHeight, 0xFFFFFFFF, true);
}
private static String resolveNick(Minecraft mc) {
if (mc.player != null) {
GameProfile profile = mc.player.getGameProfile();
if (profile != null && profile.name() != null && !profile.name().isBlank()) {
return profile.name();
}
}
if (mc.getUser() != null) {
String name = mc.getUser().getName();
if (name != null && !name.isBlank()) {
return name;
}
}
return "";
}
private static int resolvePing(Minecraft mc) {
if (mc.getConnection() == null || mc.getUser() == null) {
return 0;
}
PlayerInfo info = mc.getConnection().getPlayerInfo(mc.getUser().getProfileId());
return info == null ? 0 : info.getLatency();
}
private static float wrapDegrees(float v) {
v = v % 360.0f;
if (v >= 180.0f) {
v -= 360.0f;
}
if (v < -180.0f) {
v += 360.0f;
}
return v;
}
}

View File

@@ -1,120 +0,0 @@
package ru.nevetime.clientirc;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import java.util.Collection;
public final class WorldOverlayRenderer {
private WorldOverlayRenderer() {}
public static void init() {
WorldRenderEvents.AFTER_ENTITIES.register(WorldOverlayRenderer::onAfterEntities);
}
private static void onAfterEntities(WorldRenderContext context) {
Minecraft client = Minecraft.getInstance();
if (client.level == null) return;
if (client.options != null && client.options.hideGui) return;
PoseStack pose = context.matrixStack();
if (pose == null) return;
Camera camera = context.camera();
if (camera == null) return;
Vec3 camPos = camera.getPosition();
MultiBufferSource consumers = context.consumers();
if (consumers == null) return;
VertexConsumer lines = consumers.getBuffer(RenderType.lines());
// Friend dots
for (Player player : client.level.players()) {
if (player == client.player) continue;
if (player.getGameProfile() == null) continue;
if (FriendsManager.isFriend(player.getGameProfile().name())) {
double px = player.getX() - camPos.x;
double py = player.getY() + player.getBbHeight() + 0.5 - camPos.y;
double pz = player.getZ() - camPos.z;
int r = 0x33, g = 0xFF, b = 0x55, a = 0xFF;
drawLine(lines, pose, px - 0.15, py, pz, px + 0.15, py, pz, r, g, b, a);
drawLine(lines, pose, px, py - 0.15, pz, px, py + 0.15, pz, r, g, b, a);
drawLine(lines, pose, px, py, pz - 0.15, px, py, pz + 0.15, r, g, b, a);
}
}
// Marker beacons
String currentDim = client.level.dimension().location().toString();
Collection<MarkerManager.Marker> markers = MarkerManager.active();
for (MarkerManager.Marker marker : markers) {
if (!currentDim.equals(marker.dimensionId())) continue;
double mx = marker.x() - camPos.x;
double my = marker.y() - camPos.y;
double mz = marker.z() - camPos.z;
int r = 0xFF, g = 0x55, b = 0xFF, a = 0xFF;
// Vertical beam
drawLine(lines, pose, mx, my, mz, mx, my + 50.0, mz, r, g, b, a);
// 1x1x1 cube outline centered at (mx, my + 0.5, mz)
double cx = mx, cy = my + 0.5, cz = mz;
double x0 = cx - 0.5, x1 = cx + 0.5;
double y0 = cy - 0.5, y1 = cy + 0.5;
double z0 = cz - 0.5, z1 = cz + 0.5;
// Bottom square (y = y0)
drawLine(lines, pose, x0, y0, z0, x1, y0, z0, r, g, b, a);
drawLine(lines, pose, x1, y0, z0, x1, y0, z1, r, g, b, a);
drawLine(lines, pose, x1, y0, z1, x0, y0, z1, r, g, b, a);
drawLine(lines, pose, x0, y0, z1, x0, y0, z0, r, g, b, a);
// Top square (y = y1)
drawLine(lines, pose, x0, y1, z0, x1, y1, z0, r, g, b, a);
drawLine(lines, pose, x1, y1, z0, x1, y1, z1, r, g, b, a);
drawLine(lines, pose, x1, y1, z1, x0, y1, z1, r, g, b, a);
drawLine(lines, pose, x0, y1, z1, x0, y1, z0, r, g, b, a);
// Vertical edges
drawLine(lines, pose, x0, y0, z0, x0, y1, z0, r, g, b, a);
drawLine(lines, pose, x1, y0, z0, x1, y1, z0, r, g, b, a);
drawLine(lines, pose, x1, y0, z1, x1, y1, z1, r, g, b, a);
drawLine(lines, pose, x0, y0, z1, x0, y1, z1, r, g, b, a);
}
}
private static void drawLine(VertexConsumer lines, PoseStack pose,
double x1, double y1, double z1,
double x2, double y2, double z2,
int r, int g, int b, int a) {
Matrix4f matrix = pose.last().pose();
Matrix3f normalMatrix = pose.last().normal();
double dx = x2 - x1;
double dy = y2 - y1;
double dz = z2 - z1;
double len = Math.sqrt(dx * dx + dy * dy + dz * dz);
float nx, ny, nz;
if (len < 1e-6) {
nx = 0.0f;
ny = 1.0f;
nz = 0.0f;
} else {
nx = (float) (dx / len);
ny = (float) (dy / len);
nz = (float) (dz / len);
}
lines.vertex(matrix, (float) x1, (float) y1, (float) z1).color(r, g, b, a).normal(normalMatrix, nx, ny, nz);
lines.vertex(matrix, (float) x2, (float) y2, (float) z2).color(r, g, b, a).normal(normalMatrix, nx, ny, nz);
}
}