diff --git a/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/BurgerPanelIntegrator.java b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/BurgerPanelIntegrator.java index c659664..3506e6d 100644 --- a/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/BurgerPanelIntegrator.java +++ b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/BurgerPanelIntegrator.java @@ -1,31 +1,41 @@ package io.github.theblueburger.burgerpanelintegrator; -import org.bukkit.Bukkit; +import io.github.theblueburger.burgerpanelintegrator.packets.*; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; import org.slf4j.Logger; import java.io.IOException; import java.net.StandardProtocolFamily; import java.net.UnixDomainSocketAddress; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.nio.file.Path; +import java.util.ArrayList; public final class BurgerPanelIntegrator extends JavaPlugin { static String burgerpanelID; static String burgerpanelSocketPath; static Logger logger; - SocketChannel client; + static SocketChannel client; + static PacketHandler packetHandler; + static ArrayList pendingPackets = new ArrayList<>(); @Override public void onEnable() { + packetHandler = new PacketHandler(); + packetHandler.addPacket("status", new StatusPacket()); logger = this.getSLF4JLogger(); burgerpanelSocketPath = System.getenv("BURGERPANEL_INTEGRATOR_PATH"); burgerpanelID = System.getenv("BURGERPANEL_INTEGRATOR_SERVER_ID"); if(burgerpanelID == null || burgerpanelSocketPath == null) { logger.error("Can't find server id or integrator path for burgerpanel, is it running from burgerpanel properly?"); this.setEnabled(false); + return; } Path integratorSocketPath = Path.of(burgerpanelSocketPath); UnixDomainSocketAddress socketAddress = UnixDomainSocketAddress.of(integratorSocketPath); @@ -48,14 +58,66 @@ public void onEnable() { } catch (IOException e) { throw new RuntimeException(e); } + new BukkitRunnable() { + @Override + public void run() { + JSONParser parser = new JSONParser(); + ByteBuffer bb = ByteBuffer.allocate(10_000); + int byteCount; + while(true) { + try { + byteCount = client.read(bb); + } catch (IOException e) { + if(e instanceof AsynchronousCloseException) { + break; + } + throw new RuntimeException(e); + } + try { + String bbString = new String(bb.array(), Charset.defaultCharset()).substring(0, byteCount); + logger.info(bbString); + JSONObject obj = (JSONObject) parser.parse(bbString); + pendingPackets.add(obj); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + }.runTaskAsynchronously((Plugin) this); + new BukkitRunnable() { + @Override + public void run() { + if(pendingPackets.isEmpty()) return; + ArrayList toRun = (ArrayList) pendingPackets.clone(); + pendingPackets.clear(); + toRun.forEach((obj) -> { + try { + packetHandler.execute(obj); + } catch (IOException e) { + logger.error(e.getMessage()); + } + }); + } + }.runTaskTimer((Plugin) this, 0, 1); logger.info("Connected to the BurgerPanel!"); } @Override public void onDisable() { - // Plugin shutdown logic + try { + client.close(); + } catch (IOException ignored) { + + } } - public void write(JSONObject obj) throws IOException { + static public void write(JSONObject obj) throws IOException { client.write(ByteBuffer.wrap(obj.toJSONString().getBytes(Charset.defaultCharset()))); } + static public void respond(String id, JSONObject data) throws IOException { + JSONObject responseObj = new JSONObject(); + responseObj.put("dataType", "response"); + responseObj.put("id", id); + responseObj.put("data", data); + write(responseObj); + } } diff --git a/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/Packet.java b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/Packet.java new file mode 100644 index 0000000..c26397d --- /dev/null +++ b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/Packet.java @@ -0,0 +1,9 @@ +package io.github.theblueburger.burgerpanelintegrator; + +import org.json.simple.JSONObject; + +import java.io.IOException; + +public abstract class Packet { + protected abstract void execute(JSONObject data, String id) throws IOException; +} diff --git a/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/PacketHandler.java b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/PacketHandler.java new file mode 100644 index 0000000..948c54a --- /dev/null +++ b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/PacketHandler.java @@ -0,0 +1,31 @@ +package io.github.theblueburger.burgerpanelintegrator; + +import org.json.simple.JSONObject; + +import java.io.IOException; +import java.util.HashMap; + +public class PacketHandler { + HashMap packets = new HashMap<>(); + void addPacket(String name, Packet packet) { + packets.put(name, packet); + } + void execute(JSONObject data) throws IOException { + Object packetNameProbablyString = data.get("packet"); + if(!(packetNameProbablyString instanceof String packetName)) { + BurgerPanelIntegrator.logger.error("BurgerPanel backend sent a invalid packet, .packet isn't a string!"); + return; + } + Packet packet = packets.get(packetName); + if(packet == null) { + BurgerPanelIntegrator.logger.error("BurgerPanel backend sent a invalid packet, .packet isn't a valid packet!"); + return; + } + Object packetID = data.get("id"); + if(!(packetID instanceof String packetIDString)) { + BurgerPanelIntegrator.logger.error("BurgerPanel backend sent a invalid packet, .id isn't a string!"); + return; + } + packet.execute((JSONObject)data.get("data"), packetIDString); + } +} diff --git a/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/packets/StatusPacket.java b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/packets/StatusPacket.java new file mode 100644 index 0000000..20242bf --- /dev/null +++ b/Integrator/src/main/java/io/github/theblueburger/burgerpanelintegrator/packets/StatusPacket.java @@ -0,0 +1,17 @@ +package io.github.theblueburger.burgerpanelintegrator.packets; + +import io.github.theblueburger.burgerpanelintegrator.BurgerPanelIntegrator; +import io.github.theblueburger.burgerpanelintegrator.Packet; +import org.bukkit.Bukkit; +import org.json.simple.JSONObject; + +import java.io.IOException; + +public class StatusPacket extends Packet { + @Override + protected void execute(JSONObject data, String id) throws IOException { + JSONObject responseObj = new JSONObject(); + responseObj.put("tps", Bukkit.getServer().getTPS()[0]); + BurgerPanelIntegrator.respond(id, responseObj); + } +} diff --git a/Server/src/index.ts b/Server/src/index.ts index 0983d43..6e0c62d 100644 --- a/Server/src/index.ts +++ b/Server/src/index.ts @@ -360,25 +360,25 @@ process.on("SIGINT", () => exit("INT")); process.on("SIGTERM", () => exit("TERM")); async function findLogPath() { let logLocationInConfig = await getSetting("logging_logDir"); - if(typeof logLocationInConfig != "string") return null; - if(logLocationInConfig == "") { - let logDir = path.join(__dirname, "logs"); - logLocationInConfig = logDir; - if(!await exists(logLocationInConfig)) fs.mkdirSync(logLocationInConfig); - await setSetting("logging_logDir", logDir); - } else if(logLocationInConfig == "disabled") { - return null; - } + if(typeof logLocationInConfig != "string") return null; + if(logLocationInConfig == "") { + let logDir = path.join(__dirname, "logs"); + logLocationInConfig = logDir; if(!await exists(logLocationInConfig)) fs.mkdirSync(logLocationInConfig); - let filePath = path.join(logLocationInConfig, `BurgerPanel ${logger.makeNiceDate(process.platform == "win32")}`); - if(await exists(filePath + ".log")) { // how would this even trigger it changes every second - let i = 0; - while(await exists(filePath + i + ".log")) { - i++; - } - filePath = filePath + i; + await setSetting("logging_logDir", logDir); + } else if(logLocationInConfig == "disabled") { + return null; + } + if(!await exists(logLocationInConfig)) fs.mkdirSync(logLocationInConfig); + let filePath = path.join(logLocationInConfig, `BurgerPanel ${logger.makeNiceDate(process.platform == "win32")}`); + if(await exists(filePath + ".log")) { // how would this even trigger it changes every second + let i = 0; + while(await exists(filePath + i + ".log")) { + i++; } - return filePath + ".log"; + filePath = filePath + i; + } + return filePath + ".log"; } packetHandler.init().then(async () => { logger.setupWriteStream(await findLogPath()); diff --git a/Server/src/serverIntegrator.ts b/Server/src/serverIntegrator.ts index da44197..2cea996 100644 --- a/Server/src/serverIntegrator.ts +++ b/Server/src/serverIntegrator.ts @@ -1,8 +1,7 @@ const isProd = process.env.NODE_ENV == "production"; import fsSync from "node:fs"; import net from "node:net"; -import url from "node:url"; -import fs from "node:fs/promises"; +import path from "node:path"; import logger, { LogLevel } from "./logger.js"; import type { Server } from "../../Share/Server.js"; interface OurIntegratorClient extends net.Socket { @@ -10,9 +9,9 @@ interface OurIntegratorClient extends net.Socket { server?: string } } -let __dirname = url.fileURLToPath(new URL('.', import.meta.url)); export default new class ServerIntegrator { - path = (isProd ? __dirname : process.cwd()) + "/connector.burgerpanelsock"; + path: string | undefined; + filename = "connector.burgerpanelsock"; requestCallbacks: { [id: string]: (resp: any) => void } = {}; @@ -20,13 +19,23 @@ export default new class ServerIntegrator { servers: { [id: string]: { server: Server, - client?: net.Socket + client?: OurIntegratorClient } } = {}; constructor() { + this.path = this.getPath(); + if(!this.path) return; if(fsSync.existsSync(this.path)) fsSync.rmSync(this.path); this.listen(); } + getPath() { + if(process.platform == "win32") { + logger.log("Integrator cannot be used because microsoft", "debug", LogLevel.DEBUG); + return; + } + if(process.env.BURGERPANEL_INTEGRATOR_SOCKET_PATH) return process.env.BURGERPANEL_INTEGRATOR_SOCKET_PATH; + return path.join(process.cwd(), this.filename); + } private listen() { let server = net.createServer(_c => { let c = _c as OurIntegratorClient; @@ -36,12 +45,13 @@ export default new class ServerIntegrator { try { json = JSON.parse(d.toString()); } catch(err) { - logger.log(`Client sent invalid JSON, server id is ${c.burgerpanelData.server}`, "server.integrator", LogLevel.ERROR); + logger.log(`Client sent invalid JSON, server id is ${c.burgerpanelData.server}: ${d.toString()}`, "server.integrator", LogLevel.ERROR); return; } if(Array.isArray(json)) return; if(!["request", "response"].includes(json.dataType)) return; if(json.dataType == "response") { + console.log(json); if(!this.requestCallbacks[json.id]) return; this.requestCallbacks[json.id](json.data); return; @@ -51,7 +61,15 @@ export default new class ServerIntegrator { case "setID": if(!this.servers[json.id]) return; c.burgerpanelData.server = json.id; + this.servers[json.id].client = c; logger.log(`Server ${json.id} connected`, "server.integrator"); + c.write(JSON.stringify({ + id: "among", + packet: "status", + data: { + + } + })); break; } }); diff --git a/Server/src/serverManager.ts b/Server/src/serverManager.ts index b15f79f..aa47fba 100644 --- a/Server/src/serverManager.ts +++ b/Server/src/serverManager.ts @@ -117,6 +117,7 @@ enforce-secure-profile=false let jvmArgs = server.jvmArgs.split(" "); args = ["-Dnojline=true", "-Xms" + server.mem + "M", "-Xmx" + server.mem + "M", ...jvmArgs]; } + serverIntegrator.prepareServer(server); let childProcess = spawn("java", args, { cwd: server.path, stdio: "pipe", diff --git a/Server/test/testUtil.ts b/Server/test/testUtil.ts index 4f85d4b..41c820a 100644 --- a/Server/test/testUtil.ts +++ b/Server/test/testUtil.ts @@ -42,7 +42,8 @@ export default new class TestUtil { env: { PORT: this.port.toString(), DB: `json:${this.getDataPath()}`, - SKIP_BURGERPANEL_LOGFILE: "1" + SKIP_BURGERPANEL_LOGFILE: "1", + BURGERPANEL_INTEGRATOR_SOCKET_PATH: path.join(this.getContextPath(), "integrator.sock") } }); fs.writeFileSync(path.join(__dirname, "..", "test-context", this.port.toString(), "pid.txt"), (newProcess.pid ?? -1).toString()); @@ -68,8 +69,11 @@ export default new class TestUtil { }); }); } + getContextPath() { + return path.join(__dirname, "..", "test-context", this.port.toString()); + } getDataPath() { - return path.join(__dirname, "..", "test-context", this.port.toString(), "data.json"); + return path.join(this.getContextPath(), "data.json"); } getData() { return JSON.parse(fs.readFileSync(this.getDataPath()).toString());