package dev.plutorocks; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.arguments.StringArgumentType; 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.fabric.api.client.message.v1.ClientSendMessageEvents; 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 MinecraftIRC implements ClientModInitializer { public static final String MOD_ID = "minecraftirc"; 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("minecraftirc.json"); public static String ircHost = "irc.libera.chat"; public static final int ircPort = 6667; public static String ircChannel = "#chat"; public static String ircNick = "Player"; public static IrcClient IRC_CLIENT; @Override public void onInitializeClient() { loadConfig(); IRC_CLIENT = new IrcClient(ircHost, ircPort, ircChannel, ircNick); IRC_CLIENT.connect(); ClientSendMessageEvents.CHAT.register(message -> { if (message.startsWith("!")) { if (IRC_CLIENT != null && IRC_CLIENT.isConnected()) { IRC_CLIENT.sendChannelMessage(message.substring(1)); } else { sendIrcMessage("Not connected."); } } }); ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { dispatcher.register( literal("irc") .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("channel") .then(argument("channel", StringArgumentType.word()) .executes(this::handleSetChannel))) .then(literal("nick") .then(argument("nick", StringArgumentType.word()) .executes(this::handleSetNick))) ); }); LOGGER.info("MinecraftIRC 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()) { ircHost = cfg.host; } else { ircHost = "irc.libera.chat"; } if (cfg.channel != null && !cfg.channel.isEmpty()) { ircChannel = normalizeChannel(cfg.channel); } else { ircChannel = normalizeChannel("chat"); } if (cfg.nick != null && !cfg.nick.isEmpty()) { ircNick = cfg.nick; } else { ircNick = defaultNickFromSession(); } } catch (IOException | JsonSyntaxException e) { LOGGER.warn("Failed to read MinecraftIRC config, using defaults", e); applyDefaultsFromSession(); } } private static void saveConfig() { Config cfg = new Config(); cfg.host = ircHost; cfg.channel = ircChannel; cfg.nick = ircNick; try (Writer writer = Files.newBufferedWriter(CONFIG_PATH)) { GSON.toJson(cfg, writer); } catch (IOException e) { LOGGER.warn("Failed to save MinecraftIRC config", e); } } private static void applyDefaultsFromSession() { ircHost = "irc.libera.chat"; ircChannel = normalizeChannel("chat"); ircNick = 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 ctx) { if (IRC_CLIENT != null && IRC_CLIENT.isConnected()) { IRC_CLIENT.disconnect(); } IRC_CLIENT = new IrcClient(ircHost, ircPort, ircChannel, ircNick); sendCmdFeedback(ctx, "Connecting to " + ircHost + " " + ircChannel + " as " + ircNick + " (port " + ircPort + ")..."); IRC_CLIENT.connect(); return 1; } private int handleDisconnect(CommandContext ctx) { if (IRC_CLIENT == null || !IRC_CLIENT.isConnected()) { sendCmdFeedback(ctx, "Already disconnected."); return 1; } IRC_CLIENT.disconnect(); sendCmdFeedback(ctx, "Disconnected."); return 1; } private int handleStatus(CommandContext ctx) { String state = (IRC_CLIENT != null && IRC_CLIENT.isConnected()) ? "Connected" : "Not connected"; sendCmdFeedback(ctx, state + ". Host=" + ircHost + " Channel=" + ircChannel + " Nick=" + ircNick + " Port=" + ircPort); return 1; } private int handleSetHost(CommandContext ctx) { ircHost = StringArgumentType.getString(ctx, "host"); saveConfig(); sendCmdFeedback(ctx, "Host set to " + ircHost + ". Use /irc connect to apply."); return 1; } private static String normalizeChannel(String raw) { raw = raw.trim(); if (raw.isEmpty()) return "#chat"; if (!raw.startsWith("#")) raw = "#" + raw; return raw; } private int handleSetChannel(CommandContext ctx) { String raw = StringArgumentType.getString(ctx, "channel"); ircChannel = normalizeChannel(raw); saveConfig(); sendCmdFeedback(ctx, "Channel set to " + ircChannel + ". Use /irc connect to apply."); return 1; } private int handleSetNick(CommandContext ctx) { ircNick = StringArgumentType.getString(ctx, "nick"); saveConfig(); sendCmdFeedback(ctx, "Nick set to " + ircNick + ". Use /irc connect to apply."); return 1; } private void sendCmdFeedback(CommandContext ctx, String grayMessage) { MutableText prefix = Text.literal("[IRC] ").formatted(Formatting.AQUA); MutableText body = Text.literal(grayMessage).formatted(Formatting.GRAY); ctx.getSource().sendFeedback(prefix.append(body)); } private void sendIrcMessage(String grayMessage) { MinecraftClient client = MinecraftClient.getInstance(); if (client == null) return; client.execute(() -> { if (client.inGameHud != null) { MutableText prefix = Text.literal("[IRC] ").formatted(Formatting.AQUA); MutableText body = Text.literal(grayMessage).formatted(Formatting.GRAY); client.inGameHud.getChatHud().addMessage(prefix.append(body)); } }); } private static class Config { String host; String channel; String nick; } }