From 66a67bd79a490aaf7e573f7847d204769060fe79 Mon Sep 17 00:00:00 2001 From: lax1dude Date: Mon, 6 May 2024 17:18:51 -0700 Subject: [PATCH] more progress on bungee impl --- .../eaglermotd/EaglerMOTDConfiguration.java | 263 +++++++++++++++++- .../EaglerMOTDQueryResponseAdapter.java | 31 +++ .../v1_8/plugin/eaglermotd/QueryCache.java | 188 +++++++++++++ .../v1_8/plugin/eaglermotd/QueryType.java | 129 +++++++++ .../bungee/EaglerMOTDPluginBungee.java | 102 ++++++- .../bungee/EaglerMOTDQueryHandlerBungee.java | 60 ++++ .../command/CommandMOTDReloadBungee.java | 36 +++ 7 files changed, 807 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDQueryResponseAdapter.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryCache.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryType.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDQueryHandlerBungee.java create mode 100644 src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/command/CommandMOTDReloadBungee.java diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDConfiguration.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDConfiguration.java index 36c2d07..547f936 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDConfiguration.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDConfiguration.java @@ -1,11 +1,24 @@ package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; /** * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. @@ -27,12 +40,225 @@ public class EaglerMOTDConfiguration { public final Map> messages = new HashMap(); public final Map messagePools = new HashMap(); public final Map framesCache = new HashMap(); + public final Map queryTypes = new HashMap(); public int close_socket_after = 1200; public int max_sockets_per_ip = 10; public int max_total_sockets = 256; - public void reload(File pluginDir) { + public void reload(File pluginDir, EaglerMOTDLoggerAdapter logger, Collection allowedListeners) throws IOException { + messages.clear(); + messagePools.clear(); + framesCache.clear(); + queryTypes.clear(); + BitmapFile.bitmapCache.clear(); + QueryCache.flush(); + + byte[] damn = new byte[4096]; + int i; + + File msgs = new File(pluginDir, "messages.json"); + + if(!msgs.isFile()) { + try(OutputStream fileNew = new FileOutputStream(msgs)) { + try(InputStream fileDefault = EaglerMOTDConfiguration.class.getResourceAsStream("/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/default_messages.json")) { + while((i = fileDefault.read(damn)) != -1) { + fileNew.write(damn, 0, i); + } + } + } + File f2 = new File(pluginDir, "frames.json"); + if(!f2.isFile()) { + try(OutputStream fileNew = new FileOutputStream(f2)) { + try(InputStream fileDefault = EaglerMOTDConfiguration.class.getResourceAsStream("/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/default_frames.json")) { + while((i = fileDefault.read(damn)) != -1) { + fileNew.write(damn, 0, i); + } + } + } + } + f2 = new File(pluginDir, "queries.json"); + if(!f2.isFile()) { + try(OutputStream fileNew = new FileOutputStream(f2)) { + try(InputStream fileDefault = EaglerMOTDConfiguration.class.getResourceAsStream("/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/default_queries.json")) { + while((i = fileDefault.read(damn)) != -1) { + fileNew.write(damn, 0, i); + } + } + } + } + f2 = new File("server-animation.png"); + if(!f2.isFile()) { + try(OutputStream fileNew = new FileOutputStream(f2)) { + try(InputStream fileDefault = EaglerMOTDConfiguration.class.getResourceAsStream("/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/server-icons-test.png")) { + while((i = fileDefault.read(damn)) != -1) { + fileNew.write(damn, 0, i); + } + } + } + } + } + if(!msgs.exists()) { + throw new NullPointerException("messages.json is missing and could not be created"); + } + + ByteArrayOutputStream bao; + try(InputStream is = new FileInputStream(msgs)) { + bao = new ByteArrayOutputStream(is.available()); + while((i = is.read(damn)) != -1) { + bao.write(damn, 0, i); + } + } + + JsonObject msgsObj = JsonParser.parseString(new String(bao.toByteArray(), StandardCharsets.UTF_8)).getAsJsonObject(); + framesCache.put("messages", msgsObj); + close_socket_after = optInt(msgsObj.get("close_socket_after"), 1200); + max_sockets_per_ip = optInt(msgsObj.get("max_sockets_per_ip"), 10); + max_total_sockets = optInt(msgsObj.get("max_total_sockets"), 256); + msgsObj = msgsObj.get("messages").getAsJsonObject(); + + for(String ss : msgsObj.keySet()) { + try { + List poolEntries = new LinkedList(); + JsonArray arr = msgsObj.get(ss).getAsJsonArray(); + for(int j = 0, l = arr.size(); j < l; ++j) { + JsonObject entry = arr.get(j).getAsJsonObject(); + List frames = new LinkedList(); + JsonArray framesJSON = entry.get("frames").getAsJsonArray(); + for(int k = 0, l2 = framesJSON.size(); k < l2; ++k) { + JsonObject frame = resolveFrame(framesJSON.get(k).getAsString(), pluginDir, logger); + if(frame != null) { + frames.add(frame); + } + } + if(frames.size() > 0) { + poolEntries.add(new MessagePoolEntry(optInt(entry.get("interval"), 0), optInt(entry.get("timeout"), 500), + optBoolean(entry.get("random"), false), optBoolean(entry.get("shuffle"), false), optFloat(entry.get("weight"), 1.0f), + optString(entry.get("next"), null), frames, optString(entry.get("name"), null))); + }else { + logger.error("Message '" + ss + "' has no frames!"); + } + } + if(poolEntries.size() > 0) { + List existingList = messages.get(ss); + if(existingList == null) { + existingList = poolEntries; + messages.put(ss, existingList); + }else { + existingList.addAll(poolEntries); + } + } + }catch(Throwable t) { + logger.error("Could not parse messages for '" + ss + "' " + t.toString()); + } + } + + String flag = null; + for(String s : messages.keySet()) { + if(!s.equals("all")) { + boolean flag2 = false; + for(String l : allowedListeners) { + if(s.equals(l)) { + flag2 = true; + } + } + if(!flag2) { + flag = s; + break; + } + } + } + + if(flag != null) { + logger.error("Listener '" + flag + "' does not exist!"); + String hostsString = ""; + for(String l : allowedListeners) { + if(hostsString.length() > 0) { + hostsString += " "; + } + hostsString += l; + } + logger.error("Listeners configured: " + hostsString); + } + + for(String l : allowedListeners) { + MessagePool m = new MessagePool(l); + List e = messages.get("all"); + if(e != null) { + m.messagePool.addAll(e); + } + e = messages.get(l); + if(e != null) { + m.messagePool.addAll(e); + } + if(m.messagePool.size() > 0) { + logger.info("Loaded " + m.messagePool.size() + " messages for " + l); + messagePools.put(l, m); + } + } + + msgs = new File(pluginDir, "queries.json"); + if(msgs.exists()) { + try { + try(InputStream is = new FileInputStream(msgs)) { + bao = new ByteArrayOutputStream(is.available()); + while((i = is.read(damn)) != -1) { + bao.write(damn, 0, i); + } + } + JsonObject queriesObject = JsonParser.parseString(new String(bao.toByteArray(), StandardCharsets.UTF_8)).getAsJsonObject(); + JsonObject queriesQueriesObject = queriesObject.get("queries").getAsJsonObject(); + for(String s : queriesQueriesObject.keySet()) { + queryTypes.put(s.toLowerCase(), new QueryType(s, queriesQueriesObject.get(s).getAsJsonObject())); + } + if(queryTypes.size() > 0) { + logger.info("Loaded " + queryTypes.size() + " query types"); + } + }catch(Throwable t) { + logger.error("Queries were not loaded: " + t.toString()); + } + } + + } + + public JsonObject resolveFrame(String s, File pluginDir, EaglerMOTDLoggerAdapter logger) throws IOException { + int i = s.indexOf('.'); + if(i == -1) { + logger.error("Frame '" + s + "' cannot be found! (it does not specify a filename)"); + return null; + } + String f = s.substring(0, i); + JsonObject fc = framesCache.get(f); + if(fc == null) { + File ff = new File(pluginDir, f + ".json"); + if(!ff.exists()) { + logger.error("File '" + f + ".json' cannot be found!"); + return null; + } + try { + ByteArrayOutputStream bao; + try(InputStream is = new FileInputStream(ff)) { + bao = new ByteArrayOutputStream(is.available()); + byte[] damn = new byte[4096]; + int j; + while((j = is.read(damn)) != -1) { + bao.write(damn, 0, j); + } + } + fc = JsonParser.parseString(new String(bao.toByteArray(), StandardCharsets.UTF_8)).getAsJsonObject(); + framesCache.put(f, fc); + }catch(Throwable t) { + logger.error("File '" + f + ".json' could not be loaded: " + t.toString()); + return null; + } + } + f = s.substring(i + 1).trim(); + if(fc.has(f)) { + return fc.get(f).getAsJsonObject(); + }else { + logger.error("Frame '" + s + "' cannot be found!"); + return null; + } } public static final char COLOR_CHAR = '\u00A7'; @@ -49,4 +275,39 @@ public class EaglerMOTDConfiguration { return new String(b); } + private static int optInt(JsonElement el, int def) { + if(el != null && !el.isJsonPrimitive()) { + JsonPrimitive prim = el.getAsJsonPrimitive(); + return prim.isNumber() ? prim.getAsInt() : def; + }else { + return def; + } + } + + private static boolean optBoolean(JsonElement el, boolean def) { + if(el != null && !el.isJsonPrimitive()) { + JsonPrimitive prim = el.getAsJsonPrimitive(); + return prim.isBoolean() ? prim.getAsBoolean() : def; + }else { + return def; + } + } + + private static float optFloat(JsonElement el, float def) { + if(el != null && !el.isJsonPrimitive()) { + JsonPrimitive prim = el.getAsJsonPrimitive(); + return prim.isNumber() ? prim.getAsFloat() : def; + }else { + return def; + } + } + + private static String optString(JsonElement el, String def) { + if(el != null && !el.isJsonPrimitive()) { + JsonPrimitive prim = el.getAsJsonPrimitive(); + return prim.isString() ? prim.getAsString() : def; + }else { + return def; + } + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDQueryResponseAdapter.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDQueryResponseAdapter.java new file mode 100644 index 0000000..97d7e85 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/EaglerMOTDQueryResponseAdapter.java @@ -0,0 +1,31 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd; + +import com.google.gson.JsonObject; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public interface EaglerMOTDQueryResponseAdapter { + + void stringResponse(String type, String str); + void jsonResponse(String type, JsonObject json); + + void sendBinary(byte[] bin); + + default void errorResponse(String str) { + stringResponse("error", str); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryCache.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryCache.java new file mode 100644 index 0000000..9f166d7 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryCache.java @@ -0,0 +1,188 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class QueryCache { + + private static class CachedFile { + protected final String name; + protected final File file; + protected long lastReload; + protected long lastRescan; + protected CachedFile(String name, File file) { + this.name = name; + this.file = file; + this.lastReload = this.lastRescan = 0l; + } + protected boolean needsReload() { + long l = System.currentTimeMillis(); + if(l - lastRescan > 5000l) { + lastRescan = l; + return file.exists() && lastReload < file.lastModified(); + }else { + return false; + } + } + } + + private static class CachedFileBinary extends CachedFile { + protected byte[] bytes = null; + protected CachedFileBinary(String name, File file) { + super(name, file); + } + protected byte[] getOrReload() { + synchronized(this) { + if(needsReload()) { + if(file.exists()) { + try(FileInputStream fis = new FileInputStream(file)) { + ByteArrayOutputStream read = new ByteArrayOutputStream(fis.available()); + byte[] d = new byte[8192]; + int i; + while((i = fis.read(d)) != -1) { + read.write(d, 0, i); + } + lastRescan = lastReload = System.currentTimeMillis(); + bytes = read.toByteArray(); + }catch(Throwable t) { + bytes = null; + System.err.println("[EaglerMOTD] Failed to load binary: " + name); + t.printStackTrace(); + } + } + } + return bytes; + } + } + } + + private static class CachedFileString extends CachedFile { + protected String chars = null; + protected CachedFileString(String name, File file) { + super(name, file); + + } + protected String getOrReload() { + synchronized(this) { + if(needsReload()) { + if(file.exists()) { + try(FileInputStream fis = new FileInputStream(file)) { + ByteArrayOutputStream read = new ByteArrayOutputStream(fis.available()); + byte[] d = new byte[8192]; + int i; + while((i = fis.read(d)) != -1) { + read.write(d, 0, i); + } + lastRescan = lastReload = System.currentTimeMillis(); + chars = new String(read.toByteArray(), StandardCharsets.UTF_8); + }catch(Throwable t) { + chars = null; + System.err.println("[EaglerMOTD] Failed to load text: " + name); + t.printStackTrace(); + } + } + } + return chars; + } + } + } + + private static class CachedFileJSON extends CachedFile { + protected JsonObject json = null; + protected CachedFileJSON(String name, File file) { + super(name, file); + + } + protected JsonObject getOrReload() { + synchronized(this) { + if(needsReload()) { + if(file.exists()) { + try(FileInputStream fis = new FileInputStream(file)) { + ByteArrayOutputStream read = new ByteArrayOutputStream(fis.available()); + byte[] d = new byte[8192]; + int i; + while((i = fis.read(d)) != -1) { + read.write(d, 0, i); + } + lastRescan = lastReload = System.currentTimeMillis(); + json = JsonParser.parseString(new String(read.toByteArray(), StandardCharsets.UTF_8)).getAsJsonObject(); + }catch(Throwable t) { + json = null; + System.err.println("[EaglerMOTD] Failed to load json: " + name); + t.printStackTrace(); + } + } + } + return json; + } + } + } + + private static final HashMap cachedBinary = new HashMap(); + private static final HashMap cachedString = new HashMap(); + private static final HashMap cachedJSON = new HashMap(); + + public static void flush() { + cachedBinary.clear(); + cachedString.clear(); + cachedJSON.clear(); + } + + public static byte[] getBinaryFile(String s) { + CachedFileBinary fb; + synchronized(cachedBinary) { + fb = cachedBinary.get(s); + if(fb == null) { + fb = new CachedFileBinary(s, new File(s)); + cachedBinary.put(s, fb); + } + } + return fb.getOrReload(); + } + + public static String getStringFile(String s) { + CachedFileString fb; + synchronized(cachedString) { + fb = cachedString.get(s); + if(fb == null) { + fb = new CachedFileString(s, new File(s)); + cachedString.put(s, fb); + } + } + return fb.getOrReload(); + } + + public static JsonObject getJSONFile(String s) { + CachedFileJSON fb; + synchronized(cachedJSON) { + fb = cachedJSON.get(s); + if(fb == null) { + fb = new CachedFileJSON(s, new File(s)); + cachedJSON.put(s, fb); + } + } + return fb.getOrReload(); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryType.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryType.java new file mode 100644 index 0000000..b192e92 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/QueryType.java @@ -0,0 +1,129 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +/** + * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class QueryType { + + public final String name; + public final String type; + + public final String dataString; + public final String dataJSONFile; + public final JsonObject dataJSONObject; + public final String dataTextFile; + public final String dataBinaryFile; + + public QueryType(String name, JsonObject tag) { + this.name = name; + JsonElement el = tag.get("json"); + if(el == null || !el.isJsonObject()) { + this.dataJSONObject = null; + this.dataJSONFile = optString(tag.get("json"), null); + if(this.dataJSONFile == null) { + this.dataTextFile = optString(tag.get("txt"), null); + if(this.dataTextFile == null) { + this.dataString = optString(tag.get("string"), null); + }else { + this.dataString = null; + } + }else { + this.dataTextFile = null; + this.dataString = null; + } + }else { + this.dataJSONObject = el.getAsJsonObject(); + this.dataJSONFile = null; + this.dataTextFile = null; + this.dataString = null; + } + this.dataBinaryFile = optString(tag.get("file"), null); + String t = optString(tag.get("type"), null); + if(t == null) { + if(this.dataJSONObject != null || this.dataJSONFile != null) { + t = "json"; + }else if(this.dataString != null || this.dataTextFile != null) { + t = "text"; + }else { + t = "binary"; + } + } + this.type = t; + } + + public void doQuery(EaglerMOTDQueryResponseAdapter query) { + byte[] bin = null; + if(dataBinaryFile != null) { + bin = QueryCache.getBinaryFile(dataBinaryFile); + if(bin == null) { + query.errorResponse("Error: could not load binary file '" + dataBinaryFile + "' for query '" + type + "'"); + return; + } + } + boolean flag = false; + if(dataJSONObject != null) { + query.jsonResponse(type, dataJSONObject); + flag = true; + }else if(dataJSONFile != null) { + JsonObject obj = QueryCache.getJSONFile(dataJSONFile); + if(obj == null) { + query.errorResponse("Error: could not load or parse JSON file '" + dataJSONFile + "' for query '" + type + "'"); + return; + }else { + query.jsonResponse(type, obj); + flag = true; + } + }else if(dataTextFile != null) { + String txt = QueryCache.getStringFile(dataTextFile); + if(txt == null) { + query.errorResponse("Error: could not load text file '" + dataJSONFile + "' for query '" + type + "'"); + return; + }else { + query.stringResponse(type, txt); + flag = true; + } + }else if(dataString != null) { + query.stringResponse(type, dataString); + flag = true; + } + if(!flag) { + if(bin != null) { + JsonObject json = new JsonObject(); + json.addProperty("binary", true); + json.addProperty("file", dataBinaryFile); + json.addProperty("size", bin.length); + query.jsonResponse(type, json); + }else { + query.stringResponse(type, ""); + } + } + if(bin != null) { + query.sendBinary(bin); + } + } + + private static String optString(JsonElement el, String def) { + if(el == null || !el.isJsonPrimitive()) { + return def; + }else { + JsonPrimitive pr = el.getAsJsonPrimitive(); + return pr.isString() ? pr.getAsString() : def; + } + } +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDPluginBungee.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDPluginBungee.java index 9b38b38..d4c5527 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDPluginBungee.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDPluginBungee.java @@ -1,11 +1,20 @@ package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.bungee; +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Level; +import java.util.logging.Logger; import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.EaglerMOTDConfiguration; import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.EaglerMOTDConnectionUpdater; +import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.EaglerMOTDLoggerAdapter; +import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.bungee.command.CommandMOTDReloadBungee; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query.EaglerQueryHandler; import net.md_5.bungee.api.plugin.Plugin; /** @@ -28,7 +37,98 @@ public class EaglerMOTDPluginBungee extends Plugin { private static EaglerMOTDPluginBungee instance = null; public final EaglerMOTDConfiguration conf = new EaglerMOTDConfiguration(); - public final Timer tickTimer = new Timer("MOTD Tick Timer"); public final List motdConnections = new LinkedList(); + public final EaglerMOTDLoggerAdapter loggerAdapter; + private Timer tickTimer = null; + private final List installedQueries = new ArrayList(); + public EaglerMOTDPluginBungee() { + instance = this; + loggerAdapter = new EaglerMOTDLoggerAdapter() { + @Override + public void info(String msg) { + EaglerMOTDPluginBungee.this.getLogger().info(msg); + } + + @Override + public void warn(String msg) { + EaglerMOTDPluginBungee.this.getLogger().warning(msg); + } + + @Override + public void error(String msg) { + EaglerMOTDPluginBungee.this.getLogger().severe(msg); + } + }; + } + + @Override + public void onLoad() { + File dataFolder = getDataFolder(); + if(!dataFolder.isDirectory() && !dataFolder.mkdirs()) { + throw new RuntimeException("Could not create config folder!"); + } + conf.reload(dataFolder, loggerAdapter); + } + + public void installQueryHandlers() { + for(String str : conf.queryTypes.keySet()) { + EaglerQueryHandler.registerQueryType(str, EaglerMOTDQueryHandlerBungee.class); + installedQueries.add(str); + } + } + + public void removeQueryHandlers() { + for(String str : installedQueries) { + EaglerQueryHandler.unregisterQueryType(str); + } + installedQueries.clear(); + } + + @Override + public void onEnable() { + getProxy().getPluginManager().registerCommand(this, new CommandMOTDReloadBungee(this)); + installQueryHandlers(); + if(tickTimer == null) { + tickTimer = new Timer("MOTD Tick Timer"); + tickTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + synchronized(motdConnections) { + Iterator itr = motdConnections.iterator(); + while(itr.hasNext()) { + EaglerMOTDConnectionUpdater c = itr.next(); + try { + if(!c.tick()) { + itr.remove(); + } + }catch(Throwable t) { + EaglerMOTDPluginBungee.this.getLogger().log(Level.SEVERE, "Error ticking MOTD '" + (c.currentMessage == null ? "null" : c.currentMessage.name) + "' on listener " + c.listenerName, t); + c.close(); + itr.remove(); + } + } + } + } + + }, 0, 50l); + } + } + + @Override + public void onDisable() { + removeQueryHandlers(); + if(tickTimer != null) { + tickTimer.cancel(); + tickTimer = null; + } + } + + public static EaglerMOTDPluginBungee getPlugin() { + return instance; + } + + public static Logger logger() { + return instance.getLogger(); + } } diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDQueryHandlerBungee.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDQueryHandlerBungee.java new file mode 100644 index 0000000..ebc6c89 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/EaglerMOTDQueryHandlerBungee.java @@ -0,0 +1,60 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.bungee; + +import java.util.logging.Level; + +import com.google.gson.JsonObject; + +import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.EaglerMOTDQueryResponseAdapter; +import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.QueryType; +import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query.EaglerQuerySimpleHandler; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class EaglerMOTDQueryHandlerBungee extends EaglerQuerySimpleHandler implements EaglerMOTDQueryResponseAdapter { + + @Override + protected void begin(String queryType) { + if(this.isClosed()) { + return; + } + try { + QueryType queryHandler = EaglerMOTDPluginBungee.getPlugin().conf.queryTypes.get(queryType.toLowerCase()); + if(queryHandler != null) { + queryHandler.doQuery(this); + } + }catch(Throwable t) { + EaglerMOTDPluginBungee.logger().log(Level.SEVERE, "Failed to handle query type \"" + queryType + "\"!", t); + }finally { + this.close(); + } + } + + @Override + public void stringResponse(String type, String str) { + this.sendStringResponse(type, str); + } + + @Override + public void jsonResponse(String type, JsonObject json) { + this.sendJsonResponse(type, json); + } + + @Override + public void sendBinary(byte[] bin) { + this.sendBinaryResponse(bin); + } + +} diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/command/CommandMOTDReloadBungee.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/command/CommandMOTDReloadBungee.java new file mode 100644 index 0000000..b8918a7 --- /dev/null +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/eaglermotd/bungee/command/CommandMOTDReloadBungee.java @@ -0,0 +1,36 @@ +package net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.bungee.command; + +import net.lax1dude.eaglercraft.v1_8.plugin.eaglermotd.bungee.EaglerMOTDPluginBungee; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.plugin.Command; + +/** + * Copyright (c) 2024 lax1dude. All Rights Reserved. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + */ +public class CommandMOTDReloadBungee extends Command { + + private final EaglerMOTDPluginBungee plugin; + + public CommandMOTDReloadBungee(EaglerMOTDPluginBungee plugin) { + super("motd-reload", "eaglermotd.command.reload"); + this.plugin = plugin; + } + + @Override + public void execute(CommandSender arg0, String[] arg1) { + + } + +}