summaryrefslogtreecommitdiff
path: root/src/main/java/dev/plutorocks/PlutoBridge.java
diff options
context:
space:
mode:
authorplutorocks <>2026-02-26 16:53:24 -0500
committerplutorocks <>2026-02-26 16:53:24 -0500
commit8b123f240f098b24f8e348f1f36ab3eb72599176 (patch)
tree91a532a9a422104a667137a18137527c83c6889f /src/main/java/dev/plutorocks/PlutoBridge.java
initial commitHEADmain
Diffstat (limited to 'src/main/java/dev/plutorocks/PlutoBridge.java')
-rw-r--r--src/main/java/dev/plutorocks/PlutoBridge.java248
1 files changed, 248 insertions, 0 deletions
diff --git a/src/main/java/dev/plutorocks/PlutoBridge.java b/src/main/java/dev/plutorocks/PlutoBridge.java
new file mode 100644
index 0000000..aa3438f
--- /dev/null
+++ b/src/main/java/dev/plutorocks/PlutoBridge.java
@@ -0,0 +1,248 @@
+package dev.plutorocks;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.fabricmc.loader.api.FabricLoader;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class PlutoBridge implements ClientModInitializer {
+ public static final String MOD_ID = "PlutoBridge";
+ public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
+
+ private static final Gson GSON = new Gson();
+ private static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve("bridge.json");
+
+ public static final int MAX_NICK_LEN = 15;
+ public static final int MAX_MESSAGE_LEN = 400; // must match server
+
+ public static String bridgeHost = "bridge.plutorocks.dev";
+ public static String bridgeToken = "";
+ public static String bridgeNick; // will be set from config/session
+
+ public static BridgeClient BRIDGE_CLIENT;
+
+ // false = normal chat default, true = bridge default
+ public static boolean bridgeChatDefault = false;
+
+ @Override
+ public void onInitializeClient() {
+ loadConfig();
+
+ // Auto-connect if we already have a token
+ if (bridgeToken != null && !bridgeToken.isEmpty()) {
+ BRIDGE_CLIENT = new BridgeClient(bridgeHost, bridgeToken, bridgeNick);
+ BRIDGE_CLIENT.connect();
+ }
+
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
+ dispatcher.register(
+ literal("bridge")
+ .then(literal("connect").executes(this::handleConnect))
+ .then(literal("disconnect").executes(this::handleDisconnect))
+ .then(literal("status").executes(this::handleStatus))
+ .then(literal("host")
+ .then(argument("host", StringArgumentType.word())
+ .executes(this::handleSetHost)))
+ .then(literal("token")
+ .then(argument("token", StringArgumentType.word())
+ .executes(this::handleSetToken)))
+ .then(literal("nick")
+ .then(argument("nick", StringArgumentType.word())
+ .executes(this::handleSetNick)))
+ .then(literal("toggle")
+ .executes(this::handleToggle))
+ );
+ });
+
+ LOGGER.info("Bridge client loaded!");
+ }
+
+ private static void loadConfig() {
+ if (!Files.exists(CONFIG_PATH)) {
+ applyDefaultsFromSession();
+ saveConfig();
+ return;
+ }
+
+ try (Reader reader = Files.newBufferedReader(CONFIG_PATH)) {
+ Config cfg = GSON.fromJson(reader, Config.class);
+ if (cfg == null) {
+ applyDefaultsFromSession();
+ saveConfig();
+ return;
+ }
+
+ if (cfg.host != null && !cfg.host.isEmpty()) {
+ bridgeHost = cfg.host;
+ } else {
+ bridgeHost = "bridge.plutorocks.dev";
+ }
+
+ if (cfg.token != null && !cfg.token.isEmpty()) {
+ bridgeToken = cfg.token;
+ } else {
+ bridgeToken = "";
+ }
+
+ if (cfg.nick != null && !cfg.nick.isEmpty()) {
+ bridgeNick = cfg.nick;
+ } else {
+ bridgeNick = defaultNickFromSession();
+ }
+ } catch (IOException | JsonSyntaxException e) {
+ LOGGER.warn("Failed to read bridge config, using defaults", e);
+ applyDefaultsFromSession();
+ }
+ }
+
+ private static void saveConfig() {
+ Config cfg = new Config();
+ cfg.host = bridgeHost;
+ cfg.token = bridgeToken;
+ cfg.nick = bridgeNick;
+
+ try (Writer writer = Files.newBufferedWriter(CONFIG_PATH)) {
+ GSON.toJson(cfg, writer);
+ } catch (IOException e) {
+ LOGGER.warn("Failed to save bridge config", e);
+ }
+ }
+
+ private static void applyDefaultsFromSession() {
+ bridgeHost = "bridge.plutorocks.dev";
+ bridgeToken = "";
+ bridgeNick = defaultNickFromSession();
+ }
+
+ private static String defaultNickFromSession() {
+ MinecraftClient client = MinecraftClient.getInstance();
+ if (client != null && client.getSession() != null) {
+ String name = client.getSession().getUsername();
+ if (name != null && !name.isEmpty()) {
+ return name;
+ }
+ }
+ return "Player";
+ }
+
+ private int handleConnect(CommandContext<FabricClientCommandSource> ctx) {
+ if (bridgeToken == null || bridgeToken.isEmpty()) {
+ sendCmdFeedback(ctx, "Token is empty. Set it with /bridge token <token> first.");
+ return 1;
+ }
+
+ if (BRIDGE_CLIENT != null && BRIDGE_CLIENT.isConnected()) {
+ sendCmdFeedback(ctx, "Already connected.");
+ return 1;
+ }
+
+ if (BRIDGE_CLIENT != null) {
+ BRIDGE_CLIENT.disconnect();
+ }
+
+ BRIDGE_CLIENT = new BridgeClient(bridgeHost, bridgeToken, bridgeNick);
+
+ sendCmdFeedback(ctx, "Connecting to " + bridgeHost + " as " + bridgeNick + "...");
+ BRIDGE_CLIENT.connect();
+ return 1;
+ }
+
+ private int handleDisconnect(CommandContext<FabricClientCommandSource> ctx) {
+ if (BRIDGE_CLIENT == null || !BRIDGE_CLIENT.isConnected()) {
+ sendCmdFeedback(ctx, "Already disconnected.");
+ return 1;
+ }
+
+ BRIDGE_CLIENT.disconnect();
+ sendCmdFeedback(ctx, "Disconnected.");
+ return 1;
+ }
+
+ private int handleStatus(CommandContext<FabricClientCommandSource> ctx) {
+ String state = (BRIDGE_CLIENT != null && BRIDGE_CLIENT.isConnected()) ? "Connected" : "Not connected";
+ sendCmdFeedback(ctx, state + ". Host=" + bridgeHost + " Nick=" + bridgeNick
+ + " Mode=" + (bridgeChatDefault ? "BRIDGE" : "CHAT"));
+ return 1;
+ }
+
+ private int handleToggle(CommandContext<FabricClientCommandSource> ctx) {
+ bridgeChatDefault = !bridgeChatDefault;
+
+ if (bridgeChatDefault) {
+ sendCmdFeedback(ctx, "Bridge mode ON. Messages go to the bridge by default; prefix with ! to send to normal chat.");
+ } else {
+ sendCmdFeedback(ctx, "Bridge mode OFF. Messages go to normal chat by default; prefix with ! to send to the bridge.");
+ }
+
+ return 1;
+ }
+
+ private int handleSetHost(CommandContext<FabricClientCommandSource> ctx) {
+ bridgeHost = StringArgumentType.getString(ctx, "host");
+ saveConfig();
+ sendCmdFeedback(ctx, "Host set to " + bridgeHost + ". Use /bridge connect to apply.");
+ return 1;
+ }
+
+ private int handleSetToken(CommandContext<FabricClientCommandSource> ctx) {
+ bridgeToken = StringArgumentType.getString(ctx, "token");
+ saveConfig();
+ sendCmdFeedback(ctx, "Token set. Use /bridge connect to apply.");
+ return 1;
+ }
+
+ private int handleSetNick(CommandContext<FabricClientCommandSource> ctx) {
+ String requested = StringArgumentType.getString(ctx, "nick");
+
+ int nickLength = requested.codePointCount(0, requested.length());
+ if (nickLength > MAX_NICK_LEN) {
+ sendCmdFeedback(ctx, "Nick too long (max " + MAX_NICK_LEN + " characters).");
+ return 1;
+ }
+
+ bridgeNick = requested;
+ saveConfig();
+
+ if (PlutoBridge.BRIDGE_CLIENT != null && PlutoBridge.BRIDGE_CLIENT.isConnected()) {
+ PlutoBridge.BRIDGE_CLIENT.updateNick(bridgeNick);
+ sendCmdFeedback(ctx, "Nick set to " + bridgeNick + " (updated live).");
+ } else {
+ sendCmdFeedback(ctx, "Nick set to " + bridgeNick + ". Use /bridge connect to apply.");
+ }
+ return 1;
+ }
+
+ private void sendCmdFeedback(CommandContext<FabricClientCommandSource> ctx, String grayMessage) {
+ MutableText prefix = Text.literal("[Bridge] ").formatted(Formatting.AQUA);
+ MutableText body = Text.literal(grayMessage).formatted(Formatting.GRAY);
+ ctx.getSource().sendFeedback(prefix.append(body));
+ }
+
+ private static class Config {
+ String host;
+ String token;
+ String nick;
+ }
+} \ No newline at end of file