diff --git a/src/main/java/me/chayapak1/testingbot/Utilities.java b/src/main/java/me/chayapak1/testingbot/Utilities.java index ce0a7bd..d57300a 100644 --- a/src/main/java/me/chayapak1/testingbot/Utilities.java +++ b/src/main/java/me/chayapak1/testingbot/Utilities.java @@ -6,6 +6,8 @@ import net.kyori.adventure.text.format.NamedTextColor; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.util.UUID; // all in 1 class unlike ChomeNS Bot because laziness public class Utilities { @@ -40,7 +42,7 @@ public class Utilities { character == '\u007f' || // check if character is a control code, also space is the first character after - // the control characters so this is why we can do `character < ' '` + // the control characters so this is why we can do `character < ' ' character < ' ' ) continue; @@ -50,4 +52,30 @@ public class Utilities { return replaced.toString(); } + + // uuid + + public static int[] intArray (final UUID uuid) { + final ByteBuffer buffer = ByteBuffer.wrap(new byte[16]); + buffer.putLong(0, uuid.getMostSignificantBits()); + buffer.putLong(8, uuid.getLeastSignificantBits()); + + final int[] intArray = new int[4]; + for (int i = 0; i < intArray.length; i++) intArray[i] = buffer.getInt(); + + return intArray; + } + + public static String snbt (final UUID uuid) { + final int[] array = intArray(uuid); + return String.format( + "[I;%d,%d,%d,%d]", + array[0], + array[1], + array[2], + array[3] + ); + } + + public static String selector (final UUID uuid) { return "@n[nbt={UUID:" + snbt(uuid) + "}]"; } } diff --git a/src/main/java/me/chayapak1/testingbot/bots/VMBot.java b/src/main/java/me/chayapak1/testingbot/bots/VMBot.java index 2695098..28d24f8 100644 --- a/src/main/java/me/chayapak1/testingbot/bots/VMBot.java +++ b/src/main/java/me/chayapak1/testingbot/bots/VMBot.java @@ -1,9 +1,10 @@ package me.chayapak1.testingbot.bots; +import me.chayapak1.testingbot.ComponentParser; import me.chayapak1.testingbot.Utilities; import me.chayapak1.testingbot.base.BaseBot; import me.chayapak1.testingbot.base.Core; -import net.kyori.adventure.text.Component; +import org.cloudburstmc.math.vector.Vector3i; import org.geysermc.mcprotocollib.network.Session; import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; import org.geysermc.mcprotocollib.network.packet.Packet; @@ -13,6 +14,7 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; +import java.util.*; // requires qemu-system-x86_64 in /usr/bin/ // image.iso = cdrom, you can just `touch image.iso` if you don't want it @@ -24,14 +26,44 @@ public class VMBot extends SessionAdapter { private Process process; + private final TruncatingStringBuilder buffer = new TruncatingStringBuilder(128); + + private final UUID textDisplayUUID = UUID.randomUUID(); + + private String lastOutput = ""; + + private final Vector3i position = Vector3i.from( + Integer.parseInt(System.getProperty("x")), + Integer.parseInt(System.getProperty("y")), + Integer.parseInt(System.getProperty("z")) + ); + public VMBot () { + Runtime.getRuntime().addShutdownHook(new Thread(this::cleanup)); + this.bot = new BaseBot("vm"); + final Timer timer = new Timer(); + final TimerTask recreateTask = new TimerTask() { + @Override + public void run () { + recreate(); + } + }; + final TimerTask tickTask = new TimerTask() { + @Override + public void run () { + tick(); + } + }; + timer.schedule(recreateTask, 500, 2 * 1000); + timer.schedule(tickTask, 50, 50); + bot.session.addListener(this); bot.core.addListener(new Core.Listener() { @Override - public void refilled() { + public void refilled () { VMBot.this.coreRefilled(); } }); @@ -39,11 +71,54 @@ public class VMBot extends SessionAdapter { bot.connect(); } + private void recreate () { + if (!bot.loggedIn) return; + + bot.core.run(String.format( + "summon text_display %d %d %d {UUID:%s}", + + position.getX(), + position.getY(), + position.getZ(), + + Utilities.snbt(textDisplayUUID) + )); + + bot.core.run(String.format( + "minecraft:data merge entity %s {alignment:left,line_width:1024,shadow:1b,background:-16777216}", + Utilities.selector(textDisplayUUID) + )); + } + + private void tick () { + if (!bot.loggedIn) return; + + final String output = buffer.getString(); + + if (output.equals(lastOutput)) return; + + lastOutput = output; + + bot.core.run(String.format( + "data modify entity %s text set value '\"%s\"'", + Utilities.selector(textDisplayUUID), + output + // insane regex + .replaceAll("[\\u001B\\u009B][\\[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", "") + .replace("\r", "") + .replace("\\", "\\\\\\\\") + .replace("'", "\\'") + .replace("\"", "\\\\") + )); + } + private void coreRefilled () { if (started) return; started = true; + tick(); // creates the text display + new Thread(() -> { try { process = new ProcessBuilder( @@ -72,25 +147,31 @@ public class VMBot extends SessionAdapter { final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - String output; + int character; - while ((output = reader.readLine()) != null) { - if (output.isBlank()) continue; - - System.out.println(output); - - bot.chat.tellraw( - Component.text( - output - // INSANE regex. - .replaceAll("[\\u001B\\u009B][\\[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?(?:\\u0007|\\u001B\\u005C|\\u009C))|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", "") - ) - ); + while ((character = reader.read()) != -1) { + final char[] chars = Character.toChars(character); + final String string = new String(chars); + System.out.print(string); + buffer.append(string); } } catch (final IOException e) { bot.chat.tellraw(Utilities.getErrorComponent(e)); } - }).start(); + }, "VM Process Thread").start(); + } + + private void cleanup () { + System.out.println("\n\nCleaning up..."); + + bot.core.run(String.format( + "kill %s", + Utilities.selector(textDisplayUUID) + )); + + try { + Thread.sleep(500); + } catch (final InterruptedException ignored) { } } @Override @@ -107,10 +188,6 @@ public class VMBot extends SessionAdapter { final OutputStream output = process.getOutputStream(); if (content.trim().equalsIgnoreCase("sendc")) { - // ALSO clear the core queue - - bot.core.clearQueue(); - output.write("\u0003".getBytes()); output.flush(); @@ -127,4 +204,41 @@ public class VMBot extends SessionAdapter { bot.chat.tellraw(Utilities.getErrorComponent(e)); } } + + // chatgpt ahh code + private final static class TruncatingStringBuilder { + private final StringBuilder sb = new StringBuilder(); + private final Deque<Integer> newlineIndices = new ArrayDeque<>(); + private final int maxNewlines; + + public TruncatingStringBuilder (final int maxNewlines) { + this.maxNewlines = maxNewlines; + } + + public void append (final String str) { + for (int i = 0; i < str.length(); i++) { + final char c = str.charAt(i); + sb.append(c); + + if (c == '\n') { + newlineIndices.addLast(sb.length() - 1); + + if (newlineIndices.size() > maxNewlines) { + final Integer cutoffIndex = newlineIndices.pollFirst(); // remove the oldest newline + if (cutoffIndex == null) continue; + sb.delete(0, cutoffIndex + 1); + + // Shift all stored newline indices accordingly + for (int j = 0; j < newlineIndices.size(); j++) { + newlineIndices.addLast(newlineIndices.pollFirst() - (cutoffIndex + 1)); + } + } + } + } + } + + public String getString () { + return sb.toString(); + } + } }