Add friends, markers, HUD overlays, tab complete

- FriendsManager persists a per-user friend list at config/clientirc-friends.json.
- MarkerManager holds one ephemeral marker per owner (5 min expiry).
- IRC protocol extended with marker_set / marker_remove; server relays them.
- IrcService DisplayLine replaced with sealed IrcEvent (Chat / System / MarkerSet / MarkerRemove); chat formatting moved into the client.
- ClientIrcMod handles .friends add/remove/list, .marker remove, .help; chat lines from friends are prefixed with a green dot; marker events from non-friends are ignored.
- OverlayHud draws a top-center compass strip and a top-left watermark (nick / FPS / ping) via HudLayerRegistrationCallback.
- WorldOverlayRenderer draws green crosses above friends and magenta beams + base cubes at active markers via WorldRenderEvents.AFTER_ENTITIES.
- MiddleClickHandler binds MMB via KeyMapping, raycasts a block or 20-block look ray, stores locally and sends marker_set on a daemon executor.
- ChatScreenMixin completes . prefix commands on Tab; first mixin in the project, plumbed through clientirc.client.mixins.json and fabric.mod.json.
This commit is contained in:
2026-06-21 21:16:32 +06:00
parent 0112bde962
commit c25e394d50
12 changed files with 864 additions and 66 deletions

View File

@@ -0,0 +1,49 @@
package ru.nevetime.clientirc.mixin;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.client.gui.screens.ChatScreen;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChatScreen.class)
public abstract class ChatScreenMixin {
@Shadow protected EditBox input;
private static final String[] CLIENTIRC_COMMANDS = new String[] {
".irc",
".irc status",
".irc reconnect",
".irc reload",
".friends add ",
".friends remove ",
".friends list",
".marker remove",
".help"
};
@Inject(method = "keyPressed", at = @At("HEAD"), cancellable = true)
private void clientirc$tabComplete(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) {
if (keyCode != GLFW.GLFW_KEY_TAB) return;
if (input == null) return;
String value = input.getValue();
if (value == null || !value.startsWith(".")) return;
String best = null;
for (String candidate : CLIENTIRC_COMMANDS) {
if (candidate.startsWith(value) && !candidate.equals(value)) {
if (best == null || candidate.length() < best.length()) {
best = candidate;
}
}
}
if (best != null) {
input.setValue(best);
input.setCursorPosition(best.length());
cir.setReturnValue(true);
}
}
}