From e91007f11eb64762e3c92f943e341c3480447d2a Mon Sep 17 00:00:00 2001 From: cloudfl4re <178539564+cloudfl4re@users.noreply.github.com> Date: Fri, 17 Jul 2026 19:14:07 +0800 Subject: [PATCH] Fix OSC networking and platform initialization --- .../com/github/squi2rel/mcft/AutoBlink.java | 5 +- .../java/com/github/squi2rel/mcft/Config.java | 2 + .../java/com/github/squi2rel/mcft/MCFT.java | 11 +- .../com/github/squi2rel/mcft/MCFTClient.java | 3 +- .../squi2rel/mcft/ServerPacketHandler.java | 5 + .../github/squi2rel/mcft/services/DNS.java | 69 +++++-- .../github/squi2rel/mcft/services/HTTP.java | 191 +++++++++++++++--- .../github/squi2rel/mcft/services/OSC.java | 167 ++++++++++++--- .../mcft/fabric/ClientPacketHandlerImpl.java | 2 - .../mcft/fabric/ServerPacketHandlerImpl.java | 4 + fabric/src/main/resources/fabric.mod.json | 2 +- gradle.properties | 4 +- .../mcft/neoforge/ClientPlatformEvents.java | 17 ++ .../squi2rel/mcft/neoforge/MCFTNeoForge.java | 9 - .../mcft/neoforge/PacketHandlers.java | 44 +++- .../squi2rel/mcft/neoforge/PlatformImpl.java | 13 +- .../neoforge/ServerPacketHandlerImpl.java | 4 + .../resources/META-INF/neoforge.mods.toml | 6 +- 18 files changed, 440 insertions(+), 118 deletions(-) create mode 100644 neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ClientPlatformEvents.java diff --git a/common/src/main/java/com/github/squi2rel/mcft/AutoBlink.java b/common/src/main/java/com/github/squi2rel/mcft/AutoBlink.java index 80ab275..0ebd6d2 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/AutoBlink.java +++ b/common/src/main/java/com/github/squi2rel/mcft/AutoBlink.java @@ -20,12 +20,12 @@ public class AutoBlink { private static long lastUpdateTime = System.currentTimeMillis(); public static void update() { - if (!config.autoBlink || config.autoSwitchBlink && OSC.lastReceived != lastUpdateTime && System.currentTimeMillis() - OSC.lastReceived < 3000) { + long currentTime = System.currentTimeMillis(); + if (!config.autoBlink || config.autoSwitchBlink && currentTime - OSC.lastOscReceived < 3000) { enabled = false; return; } enabled = true; - long currentTime = System.currentTimeMillis(); float delta = currentTime - lastUpdateTime; lastUpdateTime = currentTime; last += delta; @@ -50,6 +50,5 @@ public static void update() { } } model.eyeL.percent = model.eyeR.percent = eyeOpenness * config.blinkMaxY; - OSC.lastReceived = lastUpdateTime; } } diff --git a/common/src/main/java/com/github/squi2rel/mcft/Config.java b/common/src/main/java/com/github/squi2rel/mcft/Config.java index ce32be1..5c2bf44 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/Config.java +++ b/common/src/main/java/com/github/squi2rel/mcft/Config.java @@ -4,6 +4,8 @@ public class Config { public int httpPort = 8999; public int oscReceivePort = 9000; public int oscSendPort = 9001; + public String oscBindHost = "127.0.0.1"; + public String oscTargetHost = "127.0.0.1"; public FTModel model = new FTModel(); public float eyeXMul = 0.5f, eyeYMul = 0.3f; diff --git a/common/src/main/java/com/github/squi2rel/mcft/MCFT.java b/common/src/main/java/com/github/squi2rel/mcft/MCFT.java index a5b1d79..d4cfb92 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/MCFT.java +++ b/common/src/main/java/com/github/squi2rel/mcft/MCFT.java @@ -4,7 +4,6 @@ import com.github.squi2rel.mcft.network.TrackingParamsPayload; import com.github.squi2rel.mcft.network.TrackingUpdatePayload; import com.google.gson.Gson; -import net.minecraft.client.resource.language.I18n; import net.minecraft.server.network.ServerPlayerEntity; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +30,7 @@ public static void onInitialize() { ServerPacketHandler.registerC2S(TrackingParamsPayload.ID, TrackingParamsPayload.CODEC, (payload, p) -> { FTModel old = models.get(p.getUuid()); - if (old == null) LOGGER.info(I18n.translate("mcft.logging.playerconnected", Objects.requireNonNull(p.getDisplayName()).getString())); + if (old == null) LOGGER.info("Player {} is using MCFT", Objects.requireNonNull(p.getDisplayName()).getString()); FTModel model = new FTModel(payload.eyeR(), payload.eyeL(), payload.mouth(), payload.flat()); model.validate(true); if (old != null) model.enabled = old.enabled; @@ -49,7 +48,7 @@ public static void onInitialize() { model.validate(false); if (!model.enabled) { model.enabled = true; - LOGGER.info(I18n.translate("mcft.logging.oscreceived", Objects.requireNonNull(p.getDisplayName()).getString())); + LOGGER.info("Player {} has OSC connected", Objects.requireNonNull(p.getDisplayName()).getString()); TrackingParamsPayload packet = new TrackingParamsPayload(p.getUuid(), model.eyeR, model.eyeL, model.mouth, model.isFlat); for (ServerPlayerEntity player : Objects.requireNonNull(p.getServer()).getPlayerManager().getPlayerList()) ServerPacketHandler.sendS2C(player, packet); } @@ -59,6 +58,10 @@ public static void onInitialize() { )) ServerPacketHandler.sendS2C(player, packet); }); + ServerPacketHandler.registerS2C(TrackingParamsPayload.ID, TrackingParamsPayload.CODEC); + ServerPacketHandler.registerS2C(TrackingUpdatePayload.ID, TrackingUpdatePayload.CODEC); + ServerPacketHandler.registerS2C(ConfigPayload.ID, ConfigPayload.CODEC); + Platform.register(); } @@ -95,4 +98,4 @@ public static void saveConfig(Object config, Path path) { throw new RuntimeException(e); } } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/github/squi2rel/mcft/MCFTClient.java b/common/src/main/java/com/github/squi2rel/mcft/MCFTClient.java index d673331..9f46f1c 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/MCFTClient.java +++ b/common/src/main/java/com/github/squi2rel/mcft/MCFTClient.java @@ -67,6 +67,7 @@ public static void onInitializeClient() { public static void update() { if (MC.player == null || MC.world == null) return; AutoBlink.update(); + OSC.applyPendingParameters(); if (model.active() && System.currentTimeMillis() - lastSync > 1000 / fps) { FTClient.writeSync(model); lastSync = System.currentTimeMillis(); @@ -86,4 +87,4 @@ private static boolean checkVersion(String v) { if (p1.length < 2 || p2.length < 2) return false; return p1[0].equals(p2[0]) && p1[1].equals(p2[1]); } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/github/squi2rel/mcft/ServerPacketHandler.java b/common/src/main/java/com/github/squi2rel/mcft/ServerPacketHandler.java index 6d2de88..b0ae473 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/ServerPacketHandler.java +++ b/common/src/main/java/com/github/squi2rel/mcft/ServerPacketHandler.java @@ -15,6 +15,11 @@ public static

void registerC2S(CustomPayload.Id

id, throw new AssertionError(); } + @ExpectPlatform + public static

void registerS2C(CustomPayload.Id

id, PacketCodec codec) { + throw new AssertionError(); + } + @ExpectPlatform public static

void sendS2C(ServerPlayerEntity player, P packet) { throw new AssertionError(); diff --git a/common/src/main/java/com/github/squi2rel/mcft/services/DNS.java b/common/src/main/java/com/github/squi2rel/mcft/services/DNS.java index b8f6f5a..84ec3f1 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/services/DNS.java +++ b/common/src/main/java/com/github/squi2rel/mcft/services/DNS.java @@ -7,24 +7,67 @@ import java.util.concurrent.TimeUnit; public class DNS { + private static final Object lock = new Object(); + private static volatile boolean running; + private static Thread thread; public static final int port = 5353; public static void init() { - Thread dns = new Thread(() -> { - while (true) { - try { - TimeUnit.SECONDS.sleep(10); - sendReply(); - } catch (InterruptedException ignored) { - } catch (Exception e) { - throw new RuntimeException(e); - } + synchronized (lock) { + if (running) return; + running = true; + Thread candidate = new Thread(DNS::run); + candidate.setName("MCFT DNS"); + candidate.setDaemon(true); + thread = candidate; + try { + candidate.start(); + } catch (RuntimeException e) { + thread = null; + running = false; + throw e; } - }); - dns.setDaemon(true); - dns.start(); + } MCFT.LOGGER.info("DNS started on port {}", port); } + public static void shutdown() { + Thread current; + synchronized (lock) { + running = false; + current = thread; + thread = null; + } + if (current != null) current.interrupt(); + } + + private static void run() { + Thread current = Thread.currentThread(); + boolean failureLogged = false; + while (running && thread == current) { + try { + sendReply(); + if (failureLogged) MCFT.LOGGER.info("DNS replies resumed"); + failureLogged = false; + } catch (Exception e) { + if (!failureLogged) MCFT.LOGGER.error("DNS reply failed", e); + failureLogged = true; + } + if (!running || thread != current) break; + try { + TimeUnit.SECONDS.sleep(10); + } catch (InterruptedException ignored) { + current.interrupt(); + break; + } + } + synchronized (lock) { + if (thread == current) { + thread = null; + running = false; + } + } + } + private static void sendReply() throws Exception { Message response = new Message(); Header header = response.getHeader(); @@ -44,4 +87,4 @@ private static void sendReply() throws Exception { socket.send(packet); } } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/github/squi2rel/mcft/services/HTTP.java b/common/src/main/java/com/github/squi2rel/mcft/services/HTTP.java index f456fff..b883d9d 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/services/HTTP.java +++ b/common/src/main/java/com/github/squi2rel/mcft/services/HTTP.java @@ -14,8 +14,12 @@ import net.minecraft.client.session.Session; import java.io.*; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; +import java.net.SocketException; +import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; @@ -25,37 +29,168 @@ import java.util.function.Consumer; public class HTTP { + private static final Object lifecycleLock = new Object(); + private static final Object lock = new Object(); + private static final String HTTP_LOOPBACK_HOST = "127.0.0.1"; + private static final int SOCKET_READ_TIMEOUT = 5000; + private static final int MAX_HEADER_BYTES = 16384; public static final int port = MCFTClient.config.httpPort; - public static void init() throws IOException { - createInfo(); - Thread http = new Thread(() -> { - try (ServerSocket serverSocket = new ServerSocket(port)) { - OSC.init(); + private static ServerSocket serverSocket; + private static Thread httpThread; + + public static void init() { + synchronized (lifecycleLock) { + initServices(); + } + } + + private static void initServices() { + try { + OSC.init(); + } catch (Exception e) { + MCFT.LOGGER.error("OSC start failed", e); + } + if (MinecraftClient.getInstance().getSession().getUuidOrNull() == null) { + MCFT.LOGGER.warn("OSCQuery start skipped without a session UUID"); + return; + } + try { + createInfo(); + } catch (Exception e) { + MCFT.LOGGER.error("OSC avatar info creation failed", e); + } + boolean httpStarted = false; + try { + httpStarted = startServer(); + } catch (Exception e) { + MCFT.LOGGER.error("HTTP start failed", e); + } + if (httpStarted) { + try { DNS.init(); - MCFT.LOGGER.info("HTTP started on port {}", port); - while (true) { - try (Socket s = serverSocket.accept()) { - BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); - while (true) { - String headerLine = in.readLine(); - if (headerLine == null || headerLine.isEmpty()) { - break; - } - } - String response = getString(); - OutputStream out = s.getOutputStream(); - out.write(response.getBytes(StandardCharsets.UTF_8)); - out.flush(); - } catch (IOException ignored) { - } - } } catch (Exception e) { - MCFT.LOGGER.error("Service start failed", e); + MCFT.LOGGER.error("DNS start failed", e); } - }); - http.setName("MCFT Service"); - http.setDaemon(true); - http.start(); + } + } + + public static void shutdown() { + synchronized (lifecycleLock) { + shutdownServices(); + } + } + + private static void shutdownServices() { + ServerSocket current; + synchronized (lock) { + current = serverSocket; + serverSocket = null; + httpThread = null; + } + if (current != null) { + try { + current.close(); + } catch (IOException e) { + MCFT.LOGGER.error("Failed to close HTTP server", e); + } + } + OSC.shutdown(); + DNS.shutdown(); + } + + private static boolean startServer() throws IOException { + synchronized (lock) { + if (serverSocket != null && !serverSocket.isClosed() && httpThread != null && httpThread.isAlive()) return true; + requirePort(port); + ServerSocket candidate = new ServerSocket(); + try { + candidate.bind(new InetSocketAddress(InetAddress.getByName(HTTP_LOOPBACK_HOST), port)); + Thread thread = new Thread(() -> runServer(candidate)); + thread.setName("MCFT HTTP"); + thread.setDaemon(true); + serverSocket = candidate; + httpThread = thread; + thread.start(); + MCFT.LOGGER.info("HTTP started on {}:{}", HTTP_LOOPBACK_HOST, port); + return true; + } catch (IOException | RuntimeException e) { + if (serverSocket == candidate) { + serverSocket = null; + httpThread = null; + } + try { + candidate.close(); + } catch (IOException closeException) { + e.addSuppressed(closeException); + } + throw e; + } + } + } + + private static void runServer(ServerSocket socket) { + try { + while (!socket.isClosed()) { + Socket client; + try { + client = socket.accept(); + } catch (IOException e) { + if (!socket.isClosed()) MCFT.LOGGER.error("HTTP accept failed", e); + break; + } + try (client) { + handleClient(client); + } catch (SocketTimeoutException | SocketException ignored) { + } catch (IOException e) { + MCFT.LOGGER.error("HTTP request failed", e); + } catch (RuntimeException e) { + MCFT.LOGGER.error("HTTP request failed", e); + } + } + } finally { + try { + socket.close(); + } catch (IOException e) { + MCFT.LOGGER.error("Failed to close HTTP server", e); + } + boolean stoppedCurrent = false; + synchronized (lock) { + if (serverSocket == socket) { + serverSocket = null; + httpThread = null; + stoppedCurrent = true; + } + } + if (stoppedCurrent) DNS.shutdown(); + } + } + + private static void handleClient(Socket client) throws IOException { + client.setSoTimeout(SOCKET_READ_TIMEOUT); + readHeaders(client.getInputStream()); + String response = getString(); + OutputStream out = client.getOutputStream(); + out.write(response.getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + + private static void readHeaders(InputStream input) throws IOException { + int third = -1; + int second = -1; + int previous = -1; + for (int count = 0; count < MAX_HEADER_BYTES; count++) { + int current = input.read(); + if (current == -1) return; + if (previous == '\n' && current == '\n' || third == '\r' && second == '\n' && previous == '\r' && current == '\n') return; + third = second; + second = previous; + previous = current; + } + throw new IOException("HTTP request headers exceed " + MAX_HEADER_BYTES + " bytes"); + } + + private static void requirePort(int value) { + if (value < 1 || value > 65535) throw new IllegalArgumentException("httpPort must be between 1 and 65535"); } private static String getString() throws IOException { @@ -160,4 +295,4 @@ public void serialize(Object value, JsonGenerator gen, SerializerProvider serial gen.writeEndArray(); } } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/github/squi2rel/mcft/services/OSC.java b/common/src/main/java/com/github/squi2rel/mcft/services/OSC.java index 1dff517..2089ca9 100644 --- a/common/src/main/java/com/github/squi2rel/mcft/services/OSC.java +++ b/common/src/main/java/com/github/squi2rel/mcft/services/OSC.java @@ -9,54 +9,163 @@ import com.illposed.osc.transport.OSCPortOut; import net.minecraft.client.MinecraftClient; +import java.io.IOException; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.List; import java.util.Map; -import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import static com.github.squi2rel.mcft.FTModel.model; import static com.github.squi2rel.mcft.MCFTClient.config; public class OSC { - public static long lastReceived = 0; - public static Map>> allParameters = Map.ofEntries( - Map.entry("EyeLeftX", f -> model.eyeL.rawPos.x = config.eyeOffsetXL + (float) f.get(0) * -config.eyeXMul), - Map.entry("EyeLeftY", f -> model.eyeL.rawPos.y = config.eyeOffsetYL + (float) f.get(0) * -config.eyeYMul), + private static final Object lock = new Object(); + private static final Map pendingParameters = new ConcurrentHashMap<>(); + private static OSCPortIn receiver; + public static volatile long lastOscReceived = 0; + public static volatile long lastReceived = 0; + public static final Map> allParameters = Map.ofEntries( + Map.entry("EyeLeftX", f -> model.eyeL.rawPos.x = config.eyeOffsetXL + f * -config.eyeXMul), + Map.entry("EyeLeftY", f -> model.eyeL.rawPos.y = config.eyeOffsetYL + f * -config.eyeYMul), Map.entry("EyeLidLeft", f -> { - if (!AutoBlink.enabled) model.eyeL.percent = (float) f.get(0); + if (!config.autoBlink || config.autoSwitchBlink) model.eyeL.percent = f; }), - Map.entry("EyeRightX", f -> model.eyeR.rawPos.x = config.eyeOffsetXR + (float) f.get(0) * -config.eyeXMul), - Map.entry("EyeRightY", f -> model.eyeR.rawPos.y = config.eyeOffsetYR + (float) f.get(0) * -config.eyeYMul), + Map.entry("EyeRightX", f -> model.eyeR.rawPos.x = config.eyeOffsetXR + f * -config.eyeXMul), + Map.entry("EyeRightY", f -> model.eyeR.rawPos.y = config.eyeOffsetYR + f * -config.eyeYMul), Map.entry("EyeLidRight", f -> { - if (!AutoBlink.enabled) model.eyeR.percent = (float) f.get(0); + if (!config.autoBlink || config.autoSwitchBlink) model.eyeR.percent = f; }), - Map.entry("JawOpen", f -> model.mouth.percent = (float) f.get(0)) + Map.entry("JawOpen", f -> model.mouth.percent = f) ); public static void init() throws Exception { - OSCPortIn receiver = new OSCPortIn(new InetSocketAddress("localhost", config.oscReceivePort)); - OSCPortOut sender = new OSCPortOut(new InetSocketAddress("localhost", config.oscSendPort)); - receiver.getDispatcher().addListener(new MessageSelector() { - @Override - public boolean isInfoRequired() { - return false; - } + InetAddress bindAddress = resolveLoopback(config.oscBindHost, "oscBindHost"); + int receivePort = requirePort(config.oscReceivePort, "oscReceivePort"); + synchronized (lock) { + if (receiver != null && receiver.isListening()) return; + closeReceiver(); + OSCPortIn candidate = null; + try { + candidate = new OSCPortIn(new InetSocketAddress(bindAddress, receivePort)); + candidate.getDispatcher().addListener(new MessageSelector() { + @Override + public boolean isInfoRequired() { + return false; + } - @Override - public boolean matches(OSCMessageEvent oscMessageEvent) { - return true; + @Override + public boolean matches(OSCMessageEvent oscMessageEvent) { + return true; + } + }, OSC::handleMessage); + candidate.startListening(); + receiver = candidate; + candidate = null; + } finally { + if (candidate != null) candidate.close(); } - }, e -> { - OSCMessage msg = e.getMessage(); - Consumer> c = allParameters.get(msg.getAddress().replace("/v2/", "")); - if (c != null) { - lastReceived = System.currentTimeMillis(); - c.accept(msg.getArguments()); + } + MCFT.LOGGER.info("OSC started on {}:{}", bindAddress.getHostAddress(), receivePort); + try { + InetAddress targetAddress = resolveLoopback(config.oscTargetHost, "oscTargetHost"); + int sendPort = requirePort(config.oscSendPort, "oscSendPort"); + sendAvatarChange(targetAddress, sendPort); + } catch (Exception e) { + MCFT.LOGGER.error("OSC avatar change target is invalid", e); + } + } + + public static void applyPendingParameters() { + lastReceived = AutoBlink.enabled ? System.currentTimeMillis() : lastOscReceived; + pendingParameters.forEach((name, value) -> { + if (pendingParameters.remove(name, value)) { + try { + allParameters.get(name).accept(value); + } catch (RuntimeException e) { + MCFT.LOGGER.error("Failed to apply OSC parameter {}", name, e); + } } }); - receiver.startListening(); - MCFT.LOGGER.info("OSC started on port {}", 9000); - sender.send(new OSCMessage("/avatar/change", List.of(Objects.requireNonNull(MinecraftClient.getInstance().getSession().getUuidOrNull()).toString()))); + } + + public static void shutdown() { + synchronized (lock) { + try { + closeReceiver(); + } catch (IOException e) { + MCFT.LOGGER.error("Failed to close OSC receiver", e); + } + pendingParameters.clear(); + } + } + + private static void handleMessage(OSCMessageEvent event) { + String address = "unknown"; + try { + OSCMessage message = event.getMessage(); + address = message.getAddress(); + if (!address.startsWith("/v2/")) return; + String parameter = address.substring(4); + if (!allParameters.containsKey(parameter)) return; + List arguments = message.getArguments(); + if (arguments.size() != 1 || !(arguments.get(0) instanceof Number value)) { + MCFT.LOGGER.warn("Ignored invalid OSC arguments for {}", address); + return; + } + float argument = value.floatValue(); + if (!Float.isFinite(argument)) { + MCFT.LOGGER.warn("Ignored non-finite OSC argument for {}", address); + return; + } + lastOscReceived = System.currentTimeMillis(); + pendingParameters.put(parameter, argument); + } catch (RuntimeException e) { + MCFT.LOGGER.error("Failed to handle OSC message {}", address, e); + } + } + + private static void sendAvatarChange(InetAddress targetAddress, int sendPort) { + UUID uuid = MinecraftClient.getInstance().getSession().getUuidOrNull(); + if (uuid == null) { + MCFT.LOGGER.warn("Skipped OSC avatar change without a session UUID"); + return; + } + OSCPortOut sender = null; + try { + sender = new OSCPortOut(new InetSocketAddress(targetAddress, sendPort)); + sender.send(new OSCMessage("/avatar/change", List.of(uuid.toString()))); + } catch (Exception e) { + MCFT.LOGGER.error("Failed to send OSC avatar change to {}:{}", targetAddress.getHostAddress(), sendPort, e); + } finally { + if (sender != null) { + try { + sender.close(); + } catch (IOException e) { + MCFT.LOGGER.error("Failed to close OSC sender", e); + } + } + } + } + + private static InetAddress resolveLoopback(String host, String setting) throws IOException { + if (host == null || host.isBlank()) throw new IllegalArgumentException(setting + " must not be blank"); + InetAddress address = InetAddress.getByName(host); + if (!address.isLoopbackAddress()) throw new IllegalArgumentException(setting + " must resolve to a loopback address"); + return address; + } + + private static int requirePort(int port, String setting) { + if (port < 1 || port > 65535) throw new IllegalArgumentException(setting + " must be between 1 and 65535"); + return port; + } + + private static void closeReceiver() throws IOException { + if (receiver == null) return; + OSCPortIn current = receiver; + receiver = null; + current.close(); } } diff --git a/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ClientPacketHandlerImpl.java b/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ClientPacketHandlerImpl.java index e578942..7468f33 100644 --- a/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ClientPacketHandlerImpl.java +++ b/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ClientPacketHandlerImpl.java @@ -1,7 +1,6 @@ package com.github.squi2rel.mcft.fabric; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.minecraft.network.PacketByteBuf; import net.minecraft.network.codec.PacketCodec; import net.minecraft.network.packet.CustomPayload; @@ -11,7 +10,6 @@ @SuppressWarnings("unused") public class ClientPacketHandlerImpl { public static

void registerS2C(CustomPayload.Id

id, PacketCodec codec, Consumer

receiver) { - PayloadTypeRegistry.playS2C().register(id, codec); ClientPlayNetworking.registerGlobalReceiver(id, (packet, context) -> receiver.accept(packet)); } diff --git a/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ServerPacketHandlerImpl.java b/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ServerPacketHandlerImpl.java index 8d83fe3..5598e1b 100644 --- a/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ServerPacketHandlerImpl.java +++ b/fabric/src/main/java/com/github/squi2rel/mcft/fabric/ServerPacketHandlerImpl.java @@ -16,6 +16,10 @@ public static

void registerC2S(CustomPayload.Id

id, ServerPlayNetworking.registerGlobalReceiver(id, (packet, context) -> receiver.accept(packet, context.player())); } + public static

void registerS2C(CustomPayload.Id

id, PacketCodec codec) { + PayloadTypeRegistry.playS2C().register(id, codec); + } + public static

void sendS2C(ServerPlayerEntity player, P packet) { ServerPlayNetworking.send(player, packet); } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index fb5064a..d995aeb 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -23,7 +23,7 @@ ], "depends": { "fabricloader": ">=0.16.14", - "minecraft": "~1.21.4", + "minecraft": "1.21.4", "java": ">=21", "fabric-api": "*" }, diff --git a/gradle.properties b/gradle.properties index e8545a9..098ca52 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx2G org.gradle.parallel=true # Mod properties -mod_version = 1.0.4.6 +mod_version = 1.0.4.7 maven_group = com.github.squi2rel.mcft archives_name = mcft enabled_platforms = fabric,neoforge @@ -16,4 +16,4 @@ yarn_mappings = 1.21.4+build.1 fabric_loader_version = 0.16.14 fabric_api_version = 0.119.4+1.21.4 neoforge_version = 21.4.150 -yarn_mappings_patch_neoforge_version = 1.21+build.4 \ No newline at end of file +yarn_mappings_patch_neoforge_version = 1.21+build.4 diff --git a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ClientPlatformEvents.java b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ClientPlatformEvents.java new file mode 100644 index 0000000..44f8033 --- /dev/null +++ b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ClientPlatformEvents.java @@ -0,0 +1,17 @@ +package com.github.squi2rel.mcft.neoforge; + +import com.github.squi2rel.mcft.MCFTClient; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.minecraft.server.command.ServerCommandSource; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent; + +public class ClientPlatformEvents { + @SubscribeEvent + public static void onCommandRegister(RegisterClientCommandsEvent event) { + event.getDispatcher().register(LiteralArgumentBuilder.literal("mcft").executes(s -> { + MCFTClient.configScreen = true; + return 1; + })); + } +} diff --git a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/MCFTNeoForge.java b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/MCFTNeoForge.java index 4173349..43ebc36 100644 --- a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/MCFTNeoForge.java +++ b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/MCFTNeoForge.java @@ -10,12 +10,8 @@ import net.neoforged.neoforge.network.registration.HandlerThread; import net.neoforged.neoforge.network.registration.PayloadRegistrar; -import java.util.concurrent.CountDownLatch; - @Mod(MCFT.MOD_ID) public final class MCFTNeoForge { - public static CountDownLatch latch = new CountDownLatch(1); - public MCFTNeoForge(IEventBus modEventBus) { // Run our common setup. MCFT.onInitialize(); @@ -25,15 +21,10 @@ public MCFTNeoForge(IEventBus modEventBus) { @SubscribeEvent public static void onClientSetup(FMLClientSetupEvent event) { MCFTClient.onInitializeClient(); - latch.countDown(); } @SubscribeEvent public static void register(RegisterPayloadHandlersEvent event) { - try { - latch.await(); - } catch (InterruptedException ignored) { - } PayloadRegistrar registrar = event.registrar("1").executesOn(HandlerThread.NETWORK); PacketHandlers.register(registrar); } diff --git a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PacketHandlers.java b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PacketHandlers.java index 5d6feba..f563f9f 100644 --- a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PacketHandlers.java +++ b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PacketHandlers.java @@ -18,12 +18,22 @@ public class PacketHandlers { @SuppressWarnings("unchecked") public static

void registerC2S(CustomPayload.Id

id, PacketCodec codec, BiConsumer receiver) { - channels.computeIfAbsent(id, k -> new Handler<>(codec)).serverHandler = (p, s) -> receiver.accept((P) p, s); + Handler

handler = (Handler

) channels.computeIfAbsent(id, k -> new Handler<>(codec)); + handler.serverbound = true; + handler.serverHandler = receiver; + } + + @SuppressWarnings("unchecked") + public static

void registerS2C(CustomPayload.Id

id, PacketCodec codec) { + Handler

handler = (Handler

) channels.computeIfAbsent(id, k -> new Handler<>(codec)); + handler.clientbound = true; } @SuppressWarnings("unchecked") public static

void registerS2C(CustomPayload.Id

id, PacketCodec codec, Consumer

receiver) { - channels.computeIfAbsent(id, k -> new Handler<>(codec)).clientHandler = p -> receiver.accept((P) p); + Handler

handler = (Handler

) channels.get(id); + if (handler == null || !handler.clientbound) throw new IllegalStateException("Unregistered clientbound payload " + id); + handler.clientHandler = receiver; } @SuppressWarnings("unchecked") @@ -31,19 +41,19 @@ public static

void register(PayloadRegistrar registrar for (Map.Entry, Handler> entry : channels.entrySet()) { CustomPayload.Id

id = (CustomPayload.Id

) entry.getKey(); Handler

handler = (Handler

) entry.getValue(); - if (handler.clientHandler != null && handler.serverHandler != null) { + if (handler.clientbound && handler.serverbound) { registrar.playBidirectional( id, handler.packetCodec, new DirectionalPayloadHandler<>( - (p, c) -> handler.clientHandler.accept(p), - (p, c) -> handler.serverHandler.accept(p, (ServerPlayerEntity) c.player()) + (p, c) -> handler.handleClientbound(p), + (p, c) -> handler.handleServerbound(p, (ServerPlayerEntity) c.player()) ) ); } else { - if (handler.clientHandler != null) { - registrar.playToClient(id, handler.packetCodec, (p, c) -> handler.clientHandler.accept(p)); - } else if (handler.serverHandler != null) { - registrar.playToServer(id, handler.packetCodec, (p, c) -> handler.serverHandler.accept(p, (ServerPlayerEntity) c.player())); + if (handler.clientbound) { + registrar.playToClient(id, handler.packetCodec, (p, c) -> handler.handleClientbound(p)); + } else if (handler.serverbound) { + registrar.playToServer(id, handler.packetCodec, (p, c) -> handler.handleServerbound(p, (ServerPlayerEntity) c.player())); } } } @@ -51,11 +61,23 @@ public static

void register(PayloadRegistrar registrar public static class Handler

{ public final PacketCodec packetCodec; - public Consumer

clientHandler; - public BiConsumer serverHandler; + public boolean clientbound; + public boolean serverbound; + public volatile Consumer

clientHandler; + public volatile BiConsumer serverHandler; public Handler(PacketCodec packetCodec) { this.packetCodec = packetCodec; } + + public void handleClientbound(P packet) { + Consumer

receiver = clientHandler; + if (receiver != null) receiver.accept(packet); + } + + public void handleServerbound(P packet, ServerPlayerEntity player) { + BiConsumer receiver = serverHandler; + if (receiver != null) receiver.accept(packet, player); + } } } diff --git a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PlatformImpl.java b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PlatformImpl.java index 4f5830d..9aeec4e 100644 --- a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PlatformImpl.java +++ b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/PlatformImpl.java @@ -1,14 +1,10 @@ package com.github.squi2rel.mcft.neoforge; import com.github.squi2rel.mcft.MCFT; -import com.github.squi2rel.mcft.MCFTClient; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModList; import net.neoforged.fml.loading.FMLPaths; -import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.entity.player.PlayerEvent; @@ -33,14 +29,7 @@ public static void register() { } public static void registerCommand() { - } - - @SubscribeEvent - public static void onCommandRegister(RegisterClientCommandsEvent event) { - event.getDispatcher().register(LiteralArgumentBuilder.literal("mcft").executes(s -> { - MCFTClient.configScreen = true; - return 1; - })); + NeoForge.EVENT_BUS.register(ClientPlatformEvents.class); } @SubscribeEvent diff --git a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ServerPacketHandlerImpl.java b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ServerPacketHandlerImpl.java index 1dc2c13..31e3694 100644 --- a/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ServerPacketHandlerImpl.java +++ b/neoforge/src/main/java/com/github/squi2rel/mcft/neoforge/ServerPacketHandlerImpl.java @@ -14,6 +14,10 @@ public static

void registerC2S(CustomPayload.Id id, Pa PacketHandlers.registerC2S(id, codec, receiver); } + public static

void registerS2C(CustomPayload.Id

id, PacketCodec codec) { + PacketHandlers.registerS2C(id, codec); + } + public static

void sendS2C(ServerPlayerEntity player, P packet) { PacketDistributor.sendToPlayer(player, packet); } diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 20a6227..9e0305f 100644 --- a/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[4,)" +loaderVersion = "[6,7)" issueTrackerURL = "https://github.com/squi2rel/MCFaceTracking/issues" license = "MIT" @@ -16,14 +16,14 @@ logoFile = "assets/mcft/icon.png" [[dependencies.mcft]] modId = "neoforge" type = "required" -versionRange = "[21.4,)" +versionRange = "[21.4,21.5)" ordering = "NONE" side = "BOTH" [[dependencies.mcft]] modId = "minecraft" type = "required" -versionRange = "[1.21.4,)" +versionRange = "[1.21.4,1.21.5)" ordering = "NONE" side = "BOTH"