diff options
Diffstat (limited to 'src/main/java/dev/plutorocks/IrcClient.java')
| -rw-r--r-- | src/main/java/dev/plutorocks/IrcClient.java | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/main/java/dev/plutorocks/IrcClient.java b/src/main/java/dev/plutorocks/IrcClient.java new file mode 100644 index 0000000..f3abd40 --- /dev/null +++ b/src/main/java/dev/plutorocks/IrcClient.java @@ -0,0 +1,199 @@ +package dev.plutorocks; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.io.*; +import java.net.Socket; +import java.nio.charset.StandardCharsets; + +public class IrcClient { + + private final String host; + private final int port; + private final String channel; + private final String nick; + + private volatile boolean running = false; + private volatile boolean connected = false; + + private Socket socket; + private BufferedWriter writer; + + public IrcClient(String host, int port, String channel, String nick) { + this.host = host; + this.port = port; + this.channel = channel.startsWith("#") ? channel : "#" + channel; + this.nick = nick; + } + + /** + * start the IRC thread. + */ + public void connect() { + if (running) return; + running = true; + + Thread thread = new Thread(this::runLoop, "MinecraftIRC-Thread"); + thread.setDaemon(true); + thread.start(); + } + + public boolean isConnected() { + return connected; + } + + public void disconnect() { + running = false; + try { + if (socket != null && !socket.isClosed()) { + socket.close(); + } + } catch (IOException ignored) {} + connected = false; + } + + private void runLoop() { + try (Socket sock = new Socket(host, port); + BufferedReader reader = new BufferedReader( + new InputStreamReader(sock.getInputStream(), StandardCharsets.UTF_8)); + BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(sock.getOutputStream(), StandardCharsets.UTF_8))) { + + this.socket = sock; + this.writer = writer; + this.connected = true; + + sendRaw("NICK " + nick); + sendRaw("USER " + nick + " 0 * :" + nick); + + sendRaw("JOIN " + channel); + + sendClientChat( + Text.literal("[IRC] ").formatted(Formatting.AQUA) + .append(Text.literal("Connected to " + host + " " + channel + " as " + nick) + .formatted(Formatting.GRAY)) + ); + + String line; + while (running && (line = reader.readLine()) != null) { + handleLine(line); + } + + if (running) { + sendClientChat( + Text.literal("[IRC] ").formatted(Formatting.AQUA) + .append(Text.literal("Disconnected from server. Use /irc connect to reconnect.") + .formatted(Formatting.GRAY)) + ); + } + + } catch (IOException e) { + sendClientChat( + Text.literal("[IRC] ").formatted(Formatting.AQUA) + .append(Text.literal("Connection error: " + e.getMessage() + + " (use /irc connect to try again)") + .formatted(Formatting.GRAY)) + ); + } finally { + connected = false; + running = false; + } + } + + private void handleLine(String line) { + // respond to server PINGs + if (line.startsWith("PING")) { + String payload = line.length() > 5 ? line.substring(5) : ""; + sendRaw("PONG " + payload); + return; + } + + // only care about PRIVMSG + if (!line.contains(" PRIVMSG ")) { + return; + } + + int prefixEnd = line.indexOf(' '); + if (!line.startsWith(":") || prefixEnd <= 1) { + return; + } + + String prefix = line.substring(1, prefixEnd); + + String nick = prefix; + int bang = prefix.indexOf('!'); + if (bang != -1) { + nick = prefix.substring(0, bang); + } + + String[] split = line.split(" :", 2); + if (split.length < 2) { + return; + } + + String trailing = split[1]; + String commandPart = split[0]; + + String[] cmdParts = commandPart.split(" "); + if (cmdParts.length < 3) { + return; + } + + String target = cmdParts[2]; + + if (!target.equalsIgnoreCase(this.channel) + && !target.equalsIgnoreCase(this.nick)) { + return; + } + + MutableText prefixText = Text.literal("[IRC] ") + .formatted(Formatting.AQUA); + MutableText nickText = Text.literal("<" + nick + "> ") + .formatted(Formatting.WHITE); + MutableText msgText = Text.literal(trailing) + .formatted(Formatting.WHITE); + + sendClientChat(prefixText.append(nickText).append(msgText)); + } + + /** + * sends a message to the configured channel + */ + public void sendChannelMessage(String message) { + if (!connected) return; + sendRaw("PRIVMSG " + channel + " :" + message); + } + + /** + * low-level raw IRC send + */ + private synchronized void sendRaw(String line) { + if (writer == null) return; + try { + writer.write(line); + writer.write("\r\n"); + writer.flush(); + } catch (IOException e) { + sendClientChat( + Text.literal("[IRC] ").formatted(Formatting.AQUA) + .append(Text.literal("Send error: " + e.getMessage()) + .formatted(Formatting.GRAY)) + ); + disconnect(); + } + } + + private void sendClientChat(Text text) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client == null) return; + + client.execute(() -> { + if (client.inGameHud != null) { + client.inGameHud.getChatHud().addMessage(text); + } + }); + } +}
\ No newline at end of file |
