refactor: make eval use only 1 connection to hopefully reduce threads amount + some misc refactors

fix: in GetBotInfoFunction, i misspelled `username` as `usernane`, it has been fixed
refactor: remove GetLatestChatMessageFunction, since the functions are now global and not bot-specific (yes, i know i can still implement it somewhere like in EvalPlugin or even call a function in GetLatestChatMessageFunction itself passing the bot, but no one really use this function anyway except the last time when ploat made some telnet ahh eval thing which used this function)
This commit is contained in:
ChomeNS
2025-05-27 17:48:17 +07:00
parent f68ce719f9
commit 48ffc7c01c
11 changed files with 110 additions and 131 deletions

View File

@@ -2,10 +2,7 @@ package me.chayapak1.chomens_bot;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.chayapak1.chomens_bot.plugins.ConsolePlugin;
import me.chayapak1.chomens_bot.plugins.DatabasePlugin;
import me.chayapak1.chomens_bot.plugins.DiscordPlugin;
import me.chayapak1.chomens_bot.plugins.IRCPlugin;
import me.chayapak1.chomens_bot.plugins.*;
import me.chayapak1.chomens_bot.util.*;
import net.dv8tion.jda.api.requests.restaction.MessageCreateAction;
import org.yaml.snakeyaml.LoaderOptions;
@@ -146,6 +143,7 @@ public class Main {
console = new ConsolePlugin(config);
if (config.discord.enabled) discord = new DiscordPlugin(config);
if (config.irc.enabled) irc = new IRCPlugin(config);
EvalPlugin.connect(config.eval.address);
LoggerUtilities.log(I18nUtilities.get("initialized"));

View File

@@ -7,6 +7,7 @@ import me.chayapak1.chomens_bot.command.CommandException;
import me.chayapak1.chomens_bot.command.TrustLevel;
import me.chayapak1.chomens_bot.data.chat.ChatPacketType;
import me.chayapak1.chomens_bot.data.eval.EvalOutput;
import me.chayapak1.chomens_bot.plugins.EvalPlugin;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -28,7 +29,7 @@ public class EvalCommand extends Command {
public Component execute (final CommandContext context) throws CommandException {
final Bot bot = context.bot;
if (!bot.eval.connected) throw new CommandException(Component.translatable("commands.eval.error.offline"));
if (!EvalPlugin.connected) throw new CommandException(Component.translatable("commands.eval.error.offline"));
final String action = context.getAction();
@@ -38,16 +39,19 @@ public class EvalCommand extends Command {
final CompletableFuture<EvalOutput> future = bot.eval.run(command);
future.thenApply(output -> {
if (output.isError()) context.sendOutput(Component.text(output.output()).color(NamedTextColor.RED));
else context.sendOutput(Component.text(output.output()));
// it returns null when the eval server isn't online, even though we have already checked,
// i'm just fixing the warning here
if (future == null) return null;
return output;
future.thenApply(result -> {
if (result.isError()) context.sendOutput(Component.text(result.output(), NamedTextColor.RED));
else context.sendOutput(Component.text(result.output()));
return result;
});
}
case "reset" -> {
bot.eval.reset();
EvalPlugin.reset();
return Component.translatable("commands.eval.reset", bot.colorPalette.defaultColor);
}
default -> throw new CommandException(Component.translatable("commands.generic.error.invalid_action"));

View File

@@ -5,17 +5,11 @@ import me.chayapak1.chomens_bot.Bot;
public class EvalFunction {
public final String name;
protected final Bot bot;
public EvalFunction (
final String name,
final Bot bot
) {
public EvalFunction (final String name) {
this.name = name;
this.bot = bot;
}
public Output execute (final Object... args) throws Exception { return null; }
public Output execute (final Bot bot, final Object... args) throws Exception { return null; }
public record Output(String message, boolean parseJSON) {
}

View File

@@ -4,12 +4,12 @@ import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
public class ChatFunction extends EvalFunction {
public ChatFunction (final Bot bot) {
super("chat", bot);
public ChatFunction () {
super("chat");
}
@Override
public Output execute (final Object... args) {
public Output execute (final Bot bot, final Object... args) {
if (args.length == 0) return null;
final String message = (String) args[0];

View File

@@ -11,12 +11,12 @@ import java.util.concurrent.TimeUnit;
public class CoreFunction extends EvalFunction {
private long lastExecutionTime = System.currentTimeMillis();
public CoreFunction (final Bot bot) {
super("core", bot);
public CoreFunction () {
super("core");
}
@Override
public Output execute (final Object... args) throws Exception {
public Output execute (final Bot bot, final Object... args) throws Exception {
if (args.length == 0) return null;
// prevent 69 DDOS exploit !!!

View File

@@ -4,12 +4,12 @@ import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
public class CorePlaceBlockFunction extends EvalFunction {
public CorePlaceBlockFunction (final Bot bot) {
super("corePlaceBlock", bot);
public CorePlaceBlockFunction () {
super("corePlaceBlock");
}
@Override
public Output execute (final Object... args) {
public Output execute (final Bot bot, final Object... args) {
if (args.length == 0) return null;
final String command = (String) args[0];

View File

@@ -5,15 +5,15 @@ import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
public class GetBotInfoFunction extends EvalFunction {
public GetBotInfoFunction (final Bot bot) {
super("getBotInfo", bot);
public GetBotInfoFunction () {
super("getBotInfo");
}
@Override
public Output execute (final Object... args) {
public Output execute (final Bot bot, final Object... args) {
final JsonObject object = new JsonObject();
object.addProperty("usernane", bot.username);
object.addProperty("username", bot.username);
object.addProperty("host", bot.host);
object.addProperty("port", bot.port);
object.addProperty("loggedIn", bot.loggedIn);

View File

@@ -1,29 +0,0 @@
package me.chayapak1.chomens_bot.evalFunctions;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
import me.chayapak1.chomens_bot.data.listener.Listener;
import me.chayapak1.chomens_bot.util.SNBTUtilities;
import net.kyori.adventure.text.Component;
public class GetLatestChatMessageFunction extends EvalFunction implements Listener {
private String latestMessage = "";
public GetLatestChatMessageFunction (final Bot bot) {
super("getLatestChatMessage", bot);
bot.listener.addListener(this);
}
@Override
public boolean onSystemMessageReceived (final Component component, final String string, final String ansi) {
latestMessage = SNBTUtilities.fromComponent(false, component);
return true;
}
@Override
public Output execute (final Object... args) {
return new Output(latestMessage, true);
}
}

View File

@@ -10,12 +10,12 @@ import me.chayapak1.chomens_bot.util.SNBTUtilities;
import java.util.List;
public class GetPlayerListFunction extends EvalFunction {
public GetPlayerListFunction (final Bot bot) {
super("getPlayerList", bot);
public GetPlayerListFunction () {
super("getPlayerList");
}
@Override
public Output execute (final Object... args) {
public Output execute (final Bot bot, final Object... args) {
final List<PlayerEntry> list = bot.players.list;
final JsonArray array = new JsonArray();

View File

@@ -2,115 +2,127 @@ package me.chayapak1.chomens_bot.plugins;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import io.socket.client.IO;
import io.socket.client.Socket;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectList;
import me.chayapak1.chomens_bot.Bot;
import me.chayapak1.chomens_bot.Main;
import me.chayapak1.chomens_bot.data.eval.EvalFunction;
import me.chayapak1.chomens_bot.data.eval.EvalOutput;
import me.chayapak1.chomens_bot.evalFunctions.*;
import me.chayapak1.chomens_bot.util.LoggerUtilities;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
public class EvalPlugin {
public static final String BRIDGE_PREFIX = "function:";
private static final String BRIDGE_PREFIX = "function:";
public static final List<EvalFunction> FUNCTIONS = ObjectList.of(
new CoreFunction(),
new CorePlaceBlockFunction(),
new ChatFunction(),
new GetPlayerListFunction(),
new GetBotInfoFunction()
);
private static final Gson GSON = new Gson();
public boolean connected = false;
private static Socket socket = null;
public static boolean connected = false;
private Socket socket = null;
private int transactionId = 0;
private final Map<Integer, CompletableFuture<EvalOutput>> futures = new Object2ObjectOpenHashMap<>();
public final List<EvalFunction> functions = new ArrayList<>();
private final Gson gson = new Gson();
public EvalPlugin (final Bot bot) {
functions.add(new CoreFunction(bot));
functions.add(new CorePlaceBlockFunction(bot));
functions.add(new ChatFunction(bot));
functions.add(new GetPlayerListFunction(bot));
functions.add(new GetBotInfoFunction(bot));
functions.add(new GetLatestChatMessageFunction(bot));
private static final AtomicInteger transactionId = new AtomicInteger();
private static final Map<Integer, CompletableFuture<EvalOutput>> futures = new Object2ObjectOpenHashMap<>();
public static void connect (final String address) {
try {
socket = IO.socket(bot.config.eval.address);
socket = IO.socket(address);
} catch (final Exception e) {
bot.logger.error(e);
LoggerUtilities.error(e);
return;
}
socket.on(Socket.EVENT_CONNECT, (args) -> {
socket.on(Socket.EVENT_CONNECT, args -> {
connected = true;
final JsonArray array = new JsonArray();
final JsonArray functionsArray = new JsonArray();
for (final EvalFunction function : functions) array.add(function.name);
for (final Bot bot : Main.bots) {
final String server = bot.getServerString(true);
for (final EvalFunction function : FUNCTIONS) {
final JsonObject object = new JsonObject();
object.addProperty("name", function.name);
object.addProperty("server", server);
functionsArray.add(object);
}
for (final EvalFunction function : FUNCTIONS) {
socket.on(
BRIDGE_PREFIX + function.name + ":" + bot.getServerString(true),
functionArgs ->
Main.EXECUTOR_SERVICE.submit(() -> {
try {
final EvalFunction.Output output = function.execute(bot, functionArgs);
if (output == null) return;
socket.emit("functionOutput:" + function.name, output.message(), output.parseJSON());
} catch (final Exception ignored) { }
})
);
}
socket.on("codeOutput", outputArgs -> {
if (outputArgs.length < 3) return;
try {
final int id = (int) outputArgs[0];
final boolean isError = (boolean) outputArgs[1];
final String output = (String) outputArgs[2];
if (!futures.containsKey(id)) return;
final CompletableFuture<EvalOutput> future = futures.remove(id);
future.complete(new EvalOutput(isError, output));
} catch (final ClassCastException | NumberFormatException ignored) { }
});
}
socket.emit(
"setFunctions",
gson.toJson(array)
GSON.toJson(functionsArray)
);
});
socket.on(Socket.EVENT_DISCONNECT, (args) -> connected = false);
socket.on(Socket.EVENT_CONNECT_ERROR, (args) -> connected = false);
for (final EvalFunction function : functions) {
socket.on(
BRIDGE_PREFIX + function.name,
args ->
bot.executorService.submit(() -> {
try {
final EvalFunction.Output output = function.execute(args);
if (output == null) return;
socket.emit("functionOutput:" + function.name, output.message(), output.parseJSON());
} catch (final Exception ignored) { }
})
);
}
socket.on("codeOutput", (args) -> {
if (args.length < 3) return;
try {
final int id = (int) args[0];
final boolean isError = (boolean) args[1];
final String output = (String) args[2];
if (!futures.containsKey(id)) return;
final CompletableFuture<EvalOutput> future = futures.remove(id);
future.complete(new EvalOutput(isError, output));
} catch (final NumberFormatException ignored) { }
});
socket.on(Socket.EVENT_DISCONNECT, args -> connected = false);
socket.on(Socket.EVENT_CONNECT_ERROR, args -> connected = false);
socket.connect();
}
private final Bot bot;
public EvalPlugin (final Bot bot) {
this.bot = bot;
}
public CompletableFuture<EvalOutput> run (final String code) {
final CompletableFuture<EvalOutput> future = new CompletableFuture<>();
if (!connected) return null;
socket.emit("runCode", transactionId, code);
socket.emit("runCode", bot.getServerString(true), transactionId.get(), code);
futures.put(transactionId, future);
transactionId++;
futures.put(transactionId.getAndIncrement(), future);
return future;
}
public void reset () {
public static void reset () {
if (!connected) return;
socket.emit("reset");
}
}