- 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.
73 lines
2.8 KiB
Java
73 lines
2.8 KiB
Java
package ru.nevetime.clientirc;
|
|
|
|
import com.mojang.blaze3d.platform.InputConstants;
|
|
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.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;
|
|
import org.lwjgl.glfw.GLFW;
|
|
|
|
public final class MiddleClickHandler {
|
|
private static final ExecutorService IO_EXECUTOR =
|
|
Executors.newSingleThreadExecutor(runnable -> {
|
|
Thread t = new Thread(runnable, "clientirc-marker-io");
|
|
t.setDaemon(true);
|
|
return t;
|
|
});
|
|
|
|
private static KeyMapping markerKey;
|
|
|
|
private MiddleClickHandler() {}
|
|
|
|
public static void init(Supplier<IrcService> serviceSupplier, Supplier<String> playerNameSupplier) {
|
|
markerKey = new KeyMapping(
|
|
"key.clientirc.marker",
|
|
InputConstants.Type.MOUSE,
|
|
GLFW.GLFW_MOUSE_BUTTON_MIDDLE,
|
|
"category.clientirc"
|
|
);
|
|
KeyBindingHelper.registerKeyBinding(markerKey);
|
|
|
|
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
|
while (markerKey.consumeClick()) {
|
|
if (client.player == null || client.level == null) break;
|
|
|
|
Vec3 target;
|
|
if (client.hitResult instanceof BlockHitResult br && br.getType() != HitResult.Type.MISS) {
|
|
target = br.getBlockPos().getCenter();
|
|
} 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 playerName = playerNameSupplier.get();
|
|
double tx = target.x, ty = target.y, tz = target.z;
|
|
|
|
MarkerManager.set(new MarkerManager.Marker(playerName, dimId, tx, ty, tz, System.currentTimeMillis()));
|
|
|
|
IO_EXECUTOR.execute(() -> {
|
|
IrcService service = serviceSupplier.get();
|
|
if (service != null) service.sendMarkerSet(playerName, dimId, tx, ty, tz);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void removeOwnMarker(Supplier<IrcService> serviceSupplier, Supplier<String> playerNameSupplier) {
|
|
String name = playerNameSupplier.get();
|
|
MarkerManager.remove(name);
|
|
IO_EXECUTOR.execute(() -> {
|
|
IrcService service = serviceSupplier.get();
|
|
if (service != null) service.sendMarkerRemove(name);
|
|
});
|
|
}
|
|
}
|