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

@@ -106,6 +106,46 @@ const server = net.createServer((socket) => {
continue;
}
if (payload.type === 'marker_set') {
const clientName = String(payload.client || 'UNKNOWN-CLIENT').trim();
const playerName = String(payload.player || 'unknown').trim();
const dimensionId = String(payload.dimensionId || 'minecraft:overworld').trim();
const x = Number(payload.x);
const y = Number(payload.y);
const z = Number(payload.z);
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(z)) {
sendJson(socket, systemMessage('Bad marker_set coords.'));
continue;
}
const outgoing = {
type: 'marker_set',
client: clientName,
player: playerName,
dimensionId,
x,
y,
z,
timestamp: Date.now()
};
console.log(`[marker_set] ${clientName} >> ${playerName} @ ${dimensionId} ${x.toFixed(1)},${y.toFixed(1)},${z.toFixed(1)}`);
broadcast(outgoing);
continue;
}
if (payload.type === 'marker_remove') {
const clientName = String(payload.client || 'UNKNOWN-CLIENT').trim();
const playerName = String(payload.player || 'unknown').trim();
const outgoing = {
type: 'marker_remove',
client: clientName,
player: playerName,
timestamp: Date.now()
};
console.log(`[marker_remove] ${clientName} >> ${playerName}`);
broadcast(outgoing);
continue;
}
sendJson(socket, systemMessage(`Unknown packet type: ${payload.type}`));
}
});