From 06d3aeb9d18673adff4b7c3e373944933a061d2b Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 6 Apr 2023 14:23:26 +0200 Subject: [PATCH 01/11] Basic SchemSearch --- src/de/steamwar/bungeecore/BungeeCore.java | 2 + .../commands/SchemSearchTestCommand.java | 40 +++ .../listeners/SchematicSearchListener.java | 22 ++ .../bungeecore/util/SchematicSearch.java | 252 ++++++++++++++++++ .../steamwar/messages/BungeeCore.properties | 11 + 5 files changed, 327 insertions(+) create mode 100644 src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java create mode 100644 src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java create mode 100644 src/de/steamwar/bungeecore/util/SchematicSearch.java diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index ec81983..e08e461 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -122,6 +122,7 @@ public class BungeeCore extends Plugin { new Fabric(); new SubserverProtocolFixer(); new PingListener(); + new SchematicSearchListener(); local = new Node.LocalNode(); if(MAIN_SERVER) { @@ -170,6 +171,7 @@ public class BungeeCore extends Plugin { new CalendarListener(); new ModCommand(); + new SchemSearchTestCommand(); // Punishment Commands: new PunishmentCommand("ban", Punishment.PunishmentType.Ban); diff --git a/src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java b/src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java new file mode 100644 index 0000000..595f97d --- /dev/null +++ b/src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java @@ -0,0 +1,40 @@ +package de.steamwar.bungeecore.commands; + +import de.steamwar.bungeecore.util.SchematicSearch; +import de.steamwar.command.PreviousArguments; +import de.steamwar.command.SWCommand; +import de.steamwar.command.TypeMapper; +import de.steamwar.sql.SchematicNode; +import de.steamwar.sql.SteamwarUser; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +import java.util.Collection; + +public class SchemSearchTestCommand extends SWCommand { + public SchemSearchTestCommand() { + super("schemsearch"); + } + + + @Register + public void genericCommand(ProxiedPlayer player, SchematicNode node) { + SchematicSearch.queueSearch(player, node); + } + + @ClassMapper(SchematicNode.class) + public TypeMapper getSchematicNodeMapper() { + return new TypeMapper() { + + @Override + public SchematicNode map(CommandSender commandSender, String[] previousArguments, String s) { + return SchematicNode.getNodeFromPath(SteamwarUser.get(commandSender.getName()), s); + } + + @Override + public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { + return SchematicNode.getNodeTabcomplete(SteamwarUser.get(sender.getName()), s); + } + }; + } +} diff --git a/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java b/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java new file mode 100644 index 0000000..103ebe0 --- /dev/null +++ b/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java @@ -0,0 +1,22 @@ +package de.steamwar.bungeecore.listeners; + +import de.steamwar.bungeecore.Message; +import de.steamwar.bungeecore.util.SchematicSearch; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.ServerSwitchEvent; +import net.md_5.bungee.event.EventHandler; + +public class SchematicSearchListener extends BasicListener { + + @EventHandler + public void onServerSwitch(ServerSwitchEvent event) { + if(SchematicSearch.removeFromQueue(event.getPlayer())) { + Message.send("SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE", event.getPlayer()); + } + } + + @EventHandler + public void onQuit(PlayerDisconnectEvent event) { + SchematicSearch.removeFromQueue(event.getPlayer()); + } +} diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java new file mode 100644 index 0000000..0f46753 --- /dev/null +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -0,0 +1,252 @@ +package de.steamwar.bungeecore.util; + +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.Message; +import de.steamwar.sql.NodeData; +import de.steamwar.sql.SchematicNode; +import de.steamwar.sql.SteamwarUser; +import lombok.AllArgsConstructor; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.scheduler.ScheduledTask; +import org.apache.commons.lang3.time.DurationFormatUtils; + +import java.io.*; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.zip.GZIPOutputStream; + +public class SchematicSearch { + + private static final Gson gson = new Gson(); + private static final String searchBinary = "/home/chaoscaot/schemsearch/target/release/schemsearch-cli"; + private static final List searchQueue = new ArrayList<>(); + private static SchematicSearch currentSearch; + private static ScheduledTask watchdog; + + private static void startQueueWatchdog() { + watchdog = BungeeCore.get().getProxy().getScheduler().schedule(BungeeCore.get(), () -> { + synchronized (searchQueue) { + if(currentSearch == null) { + if(!searchQueue.isEmpty()) { + currentSearch = searchQueue.remove(0); + currentSearch.start(); + } else { + watchdog.cancel(); + watchdog = null; + } + } + } + }, 0, 1, TimeUnit.SECONDS); + } + + public static void queueSearch(ProxiedPlayer player, SchematicNode node ) { + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + synchronized (searchQueue) { + if(user.getUserGroup().isAdminGroup()) { + searchQueue.add(0, new SchematicSearch(player, node)); + } else { + searchQueue.add(new SchematicSearch(player, node)); + } + } + if(watchdog == null) { + startQueueWatchdog(); + } else { + Message.send("SCHEMATIC_SEARCH_QUEUED", player); + } + } + + private static List constructArguments(SteamwarUser user, File pattern) { + return Arrays.asList(searchBinary, "-T", "1", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50", pattern.getAbsolutePath()); + } + + public static boolean removeFromQueue(ProxiedPlayer player) { + boolean removed = false; + synchronized (searchQueue) { + removed = searchQueue.removeIf(search -> search.player.equals(player)); + } + + if (currentSearch != null && currentSearch.player.equals(player)) { + currentSearch.end(); + removed = true; + } + + return removed; + } + + private static File schematicNodeToTempFile(NodeData node) { + try { + File f = File.createTempFile("schemsearch", ".schem"); + f.deleteOnExit(); + OutputStream os = new GZIPOutputStream(Files.newOutputStream(f.toPath())); + InputStream is = node.schemData(); + + byte[] buffer = new byte[1024]; + int read; + while((read = is.read(buffer)) != -1) { + os.write(buffer, 0, read); + } + + os.close(); + is.close(); + return f; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private final ProxiedPlayer player; + private final SchematicNode node; + private File pattern; + private ScheduledTask task; + private Process process; + + private SchematicSearch(ProxiedPlayer player, SchematicNode node) { + this.player = player; + this.node = node; + } + + private static String readInputStream(InputStream is) throws IOException { + StringBuilder sb = new StringBuilder(); + byte[] buffer = new byte[1024]; + long total = 0; + while (is.available() > 0) { + int read = is.read(buffer); + total += read; + if (read > 0) { + sb.append(new String(buffer, 0, read)); + } + if (total > 1024 * 128) { + break; + } + } + return sb.toString(); + } + + private void start() { + task = BungeeCore.get().getProxy().getScheduler().runAsync(BungeeCore.get(), () -> { + try { + NodeData data = NodeData.get(node); + if(!data.getNodeFormat()) { + Message.send("SCHEMATIC_SEARCH_NOT_SUPPORTED", player); + end(); + return; + } + Message.send("SCHEMATIC_SEARCH_STARTED", player, node.getName()); + pattern = schematicNodeToTempFile(data); + ProcessBuilder builder = new ProcessBuilder(constructArguments(SteamwarUser.get(player.getUniqueId()), pattern)); + process = builder.start(); + InputStream stdout = process.getInputStream(); + InputStream stderr = process.getErrorStream(); + + while (!process.waitFor(100, TimeUnit.MILLISECONDS)) { + String s = readInputStream(stderr); + if(s.length() > 0) { + if(s.contains("s[")) { + s = s.substring(s.lastIndexOf("s[") + 1); + } + BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + s.replace("█", "§e█§7")); + } + } + + + String s = readInputStream(stderr); + if(s.contains("s[")) { + s = s.substring(s.lastIndexOf("s[") + 1); + } + BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + s.replace("█", "§e█§7")); + s = readInputStream(stdout); + String[] outputs = s.split("\n"); + + List elements = Arrays.stream(outputs).map(JsonParser::parseString).map(JsonElement::getAsJsonObject).collect(Collectors.toList()); + + int searchCount = 0; + long searchTime = 0; + List matches = new ArrayList<>(); + + for (JsonObject element : elements) { + switch (element.get("event").getAsString()) { + case "Init": + searchCount = element.get("total").getAsInt(); + break; + case "Found": + matches.add(new Match(element.get("x").getAsInt(), element.get("y").getAsInt(), element.get("z").getAsInt(), element.get("percent").getAsFloat() * 100, element.get("name").getAsString())); + break; + case "End": + searchTime = element.get("end_time").getAsLong(); + break; + default: + break; + } + } + + Message.send("SCHEMATIC_SEARCH_RESULT_HEADER", player, searchCount, DurationFormatUtils.formatDuration(searchTime, "' 'm'm 's's 'S'ms'") + .replace(" 0ms", "") + .replace(" 0s", "") + .replace(" 0m", "")); + + if (matches.size() <= 1) { + Message.send("SCHEMATIC_SEARCH_NO_RESULTS", player); + end(); + return; + } + + SteamwarUser user = SteamwarUser.get(player.getUniqueId()); + for (Match match : matches) { + String[] nameSplit = match.name.split(" "); + String name = nameSplit[0]; + int id = Integer.parseInt(nameSplit[1].substring(1, nameSplit[1].length() - 1)); + if (id == node.getId()) continue; + Message.sendPrefixless("SCHEMATIC_SEARCH_RESULT", player, Message.parse("SCHEMATIC_SEARCH_RESULT_HOVER", player, name), + new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/schem info " + SchematicNode.byIdAndUser(user, id).generateBreadcrumbs()), + name, match.percent, match.x + 1, match.y + 1, match.z + 1); + } + + if (matches.size() >= 49) { + Message.send("SCHEMATIC_SEARCH_TOO_MANY_RESULTS", player); + } + + end(); + } catch (Exception e) { + end(); + throw new SecurityException(e); + } + }); + } + + public void end() { + if(process != null && process.isAlive()) { + process.destroy(); + } + if(pattern != null) { + pattern.delete(); + } + if(task != null) { + task.cancel(); + } + + synchronized (searchQueue) { + if(currentSearch == this) { + currentSearch = null; + } + } + } + + @AllArgsConstructor + private static class Match { + int x; + int y; + int z; + float percent; + String name; + } +} diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index 1845f38..e06383e 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -680,3 +680,14 @@ MOD_FORBIDDEN=§eForbidden MOD_AUTOBAN=§cAutoban MOD_YT=§5YT Only MOD_ITEM_BACK=§7Back + +#Schematic Search +SCHEMATIC_SEARCH_QUEUED=§7Your search has been queued and will be executed shortly. +SCHEMATIC_SEARCH_STARTED=§7Your search for "§e{0}§7" has started. +SCHEMATIC_SEARCH_NO_RESULTS=§cNo results found. +SCHEMATIC_SEARCH_RESULT_HEADER=§7Searched in §e{0} §7schematics in§e{1}. +SCHEMATIC_SEARCH_RESULT=§7{0}: §e{1}§7% §8(§e{2}§7,§e{3},§e{4}§8) +SCHEMATIC_SEARCH_RESULT_HOVER=§7Click to get more info about {0}. +SCHEMATIC_SEARCH_NOT_SUPPORTED=§cThis schematic is not supported by the schematic search. +SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE=§cYour search has been removed from the queue because you switched servers. +SCHEMATIC_SEARCH_TOO_MANY_RESULTS=§cToo many results found. Please be more specific. \ No newline at end of file -- 2.39.2 From 81dbcc691a2f8d02fcc0cd19f3da36b74334a208 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 6 Apr 2023 17:07:16 +0200 Subject: [PATCH 02/11] Add Softreload cancel --- src/de/steamwar/bungeecore/BungeeCore.java | 2 ++ src/de/steamwar/bungeecore/util/SchematicSearch.java | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index e08e461..0076c06 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -28,6 +28,7 @@ import de.steamwar.bungeecore.listeners.ping.PingListener; import de.steamwar.bungeecore.network.BungeeNetworkHandler; import de.steamwar.bungeecore.network.NetworkReceiver; import de.steamwar.bungeecore.network.SWScriptSyntaxForwarder; +import de.steamwar.bungeecore.util.SchematicSearch; import de.steamwar.sql.Punishment; import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.UserElo; @@ -235,6 +236,7 @@ public class BungeeCore extends Plugin { tablistManager.disable(); errorLogger.unregister(); Statement.closeAll(); + SchematicSearch.abortAll(); } public static BungeeCore get() { diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index 0f46753..2dd1e40 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -65,6 +65,15 @@ public class SchematicSearch { } } + public static void abortAll() { + synchronized (searchQueue) { + searchQueue.clear(); + } + if(currentSearch != null) { + currentSearch.end(); + } + } + private static List constructArguments(SteamwarUser user, File pattern) { return Arrays.asList(searchBinary, "-T", "1", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50", pattern.getAbsolutePath()); } -- 2.39.2 From edb108b7b337712405cdbaef9bddb9a5735f0eeb Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 6 Apr 2023 17:11:17 +0200 Subject: [PATCH 03/11] Add Softreload cancel --- src/de/steamwar/bungeecore/util/SchematicSearch.java | 8 ++++++-- src/de/steamwar/messages/BungeeCore.properties | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index 2dd1e40..b458223 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -67,19 +67,23 @@ public class SchematicSearch { public static void abortAll() { synchronized (searchQueue) { + searchQueue.stream().map(schematicSearch -> schematicSearch.player).collect(Collectors.toSet()).forEach(player -> { + Message.send("SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE_SOFTRELOAD", player); + }); searchQueue.clear(); } if(currentSearch != null) { + Message.send("SCHEMATIC_SEARCH_CANCELED_SOFTRELOAD", currentSearch.player); currentSearch.end(); } } private static List constructArguments(SteamwarUser user, File pattern) { - return Arrays.asList(searchBinary, "-T", "1", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50", pattern.getAbsolutePath()); + return Arrays.asList(searchBinary, "-T", "2", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50", pattern.getAbsolutePath()); } public static boolean removeFromQueue(ProxiedPlayer player) { - boolean removed = false; + boolean removed; synchronized (searchQueue) { removed = searchQueue.removeIf(search -> search.player.equals(player)); } diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index e06383e..852f24c 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -690,4 +690,6 @@ SCHEMATIC_SEARCH_RESULT=§7{0}: §e{1}§7% §8(§e{2}§7,§e{3},§e{4}§8) SCHEMATIC_SEARCH_RESULT_HOVER=§7Click to get more info about {0}. SCHEMATIC_SEARCH_NOT_SUPPORTED=§cThis schematic is not supported by the schematic search. SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE=§cYour search has been removed from the queue because you switched servers. +SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE_SOFTRELOAD=§cYour search has been removed from the queue because of an update. +SCHEMATIC_SEARCH_CANCELED_SOFTRELOAD=§cYour search has been canceled because of an update. SCHEMATIC_SEARCH_TOO_MANY_RESULTS=§cToo many results found. Please be more specific. \ No newline at end of file -- 2.39.2 From f624d2893de78ff9c849ecbd1f56a20fbe95a4b7 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 6 Apr 2023 17:22:20 +0200 Subject: [PATCH 04/11] Add catch for IOException --- .../steamwar/bungeecore/util/SchematicSearch.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index b458223..9f9fbd9 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -149,7 +149,7 @@ public class SchematicSearch { task = BungeeCore.get().getProxy().getScheduler().runAsync(BungeeCore.get(), () -> { try { NodeData data = NodeData.get(node); - if(!data.getNodeFormat()) { + if (!data.getNodeFormat()) { Message.send("SCHEMATIC_SEARCH_NOT_SUPPORTED", player); end(); return; @@ -163,17 +163,15 @@ public class SchematicSearch { while (!process.waitFor(100, TimeUnit.MILLISECONDS)) { String s = readInputStream(stderr); - if(s.length() > 0) { - if(s.contains("s[")) { + if (s.length() > 0) { + if (s.contains("s[")) { s = s.substring(s.lastIndexOf("s[") + 1); } BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + s.replace("█", "§e█§7")); } } - - String s = readInputStream(stderr); - if(s.contains("s[")) { + if (s.contains("s[")) { s = s.substring(s.lastIndexOf("s[") + 1); } BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + s.replace("█", "§e█§7")); @@ -228,6 +226,8 @@ public class SchematicSearch { Message.send("SCHEMATIC_SEARCH_TOO_MANY_RESULTS", player); } + end(); + } catch (IOException | InterruptedException e) { end(); } catch (Exception e) { end(); @@ -240,9 +240,11 @@ public class SchematicSearch { if(process != null && process.isAlive()) { process.destroy(); } + if(pattern != null) { pattern.delete(); } + if(task != null) { task.cancel(); } -- 2.39.2 From badb5e5e1ee387cae1daea2ac4aa4cb31d221b07 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 6 Apr 2023 17:30:18 +0200 Subject: [PATCH 05/11] Stop the dog --- .../steamwar/bungeecore/util/SchematicSearch.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index 9f9fbd9..c9d8c85 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -36,14 +36,15 @@ public class SchematicSearch { private static void startQueueWatchdog() { watchdog = BungeeCore.get().getProxy().getScheduler().schedule(BungeeCore.get(), () -> { synchronized (searchQueue) { + if(searchQueue.isEmpty()) { + watchdog.cancel(); + watchdog = null; + return; + } + if(currentSearch == null) { - if(!searchQueue.isEmpty()) { - currentSearch = searchQueue.remove(0); - currentSearch.start(); - } else { - watchdog.cancel(); - watchdog = null; - } + currentSearch = searchQueue.remove(0); + currentSearch.start(); } } }, 0, 1, TimeUnit.SECONDS); -- 2.39.2 From b7d42a767ac920119b8e51e8b5d715e5a69fcdce Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 6 Apr 2023 20:42:05 +0200 Subject: [PATCH 06/11] idk --- src/de/steamwar/bungeecore/util/SchematicSearch.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index c9d8c85..4b4dcfb 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -162,14 +162,17 @@ public class SchematicSearch { InputStream stdout = process.getInputStream(); InputStream stderr = process.getErrorStream(); - while (!process.waitFor(100, TimeUnit.MILLISECONDS)) { + String bar = ""; + while (!process.waitFor(200, TimeUnit.MILLISECONDS)) { String s = readInputStream(stderr); if (s.length() > 0) { if (s.contains("s[")) { s = s.substring(s.lastIndexOf("s[") + 1); } - BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + s.replace("█", "§e█§7")); + s = s.replace("█", "§e█§7"); + bar = s; } + BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + bar); } String s = readInputStream(stderr); if (s.contains("s[")) { -- 2.39.2 From a6dbf41febfcd37dc6c3e5d351ce7af8e7c789e7 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Wed, 12 Apr 2023 02:05:23 +0200 Subject: [PATCH 07/11] Finish it up --- .../commands/SchemSearchTestCommand.java | 40 ---------------- .../bungeecore/commands/StatCommand.java | 2 + .../network/BungeeNetworkHandler.java | 1 + .../SchematicSearchRequestHandler.java | 28 +++++++++++ .../bungeecore/util/SchematicSearch.java | 46 ++++++++++++++++--- .../steamwar/messages/BungeeCore.properties | 1 + 6 files changed, 71 insertions(+), 47 deletions(-) delete mode 100644 src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java create mode 100644 src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java diff --git a/src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java b/src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java deleted file mode 100644 index 595f97d..0000000 --- a/src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.steamwar.bungeecore.commands; - -import de.steamwar.bungeecore.util.SchematicSearch; -import de.steamwar.command.PreviousArguments; -import de.steamwar.command.SWCommand; -import de.steamwar.command.TypeMapper; -import de.steamwar.sql.SchematicNode; -import de.steamwar.sql.SteamwarUser; -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.connection.ProxiedPlayer; - -import java.util.Collection; - -public class SchemSearchTestCommand extends SWCommand { - public SchemSearchTestCommand() { - super("schemsearch"); - } - - - @Register - public void genericCommand(ProxiedPlayer player, SchematicNode node) { - SchematicSearch.queueSearch(player, node); - } - - @ClassMapper(SchematicNode.class) - public TypeMapper getSchematicNodeMapper() { - return new TypeMapper() { - - @Override - public SchematicNode map(CommandSender commandSender, String[] previousArguments, String s) { - return SchematicNode.getNodeFromPath(SteamwarUser.get(commandSender.getName()), s); - } - - @Override - public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { - return SchematicNode.getNodeTabcomplete(SteamwarUser.get(sender.getName()), s); - } - }; - } -} diff --git a/src/de/steamwar/bungeecore/commands/StatCommand.java b/src/de/steamwar/bungeecore/commands/StatCommand.java index 33c3653..71a72b2 100644 --- a/src/de/steamwar/bungeecore/commands/StatCommand.java +++ b/src/de/steamwar/bungeecore/commands/StatCommand.java @@ -21,6 +21,7 @@ package de.steamwar.bungeecore.commands; import de.steamwar.bungeecore.Message; import de.steamwar.bungeecore.Node; +import de.steamwar.bungeecore.util.SchematicSearch; import de.steamwar.command.SWCommand; import net.md_5.bungee.api.CommandSender; @@ -37,6 +38,7 @@ public class StatCommand extends SWCommand { @Register public void genericCommand(CommandSender sender) { + Message.send("STAT_SEARCH_QUEUE", sender, SchematicSearch.getQueueSize()); Map serverCount = new HashMap<>(); try { Process process = new ProcessBuilder("ps", "x").start(); diff --git a/src/de/steamwar/bungeecore/network/BungeeNetworkHandler.java b/src/de/steamwar/bungeecore/network/BungeeNetworkHandler.java index 1cc4854..ab7a2b3 100644 --- a/src/de/steamwar/bungeecore/network/BungeeNetworkHandler.java +++ b/src/de/steamwar/bungeecore/network/BungeeNetworkHandler.java @@ -31,5 +31,6 @@ public class BungeeNetworkHandler { new ImALobbyHandler().register(); new InventoryCallbackHandler().register(); new PrepareSchemHandler().register(); + new SchematicSearchRequestHandler().register(); } } diff --git a/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java b/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java new file mode 100644 index 0000000..5078245 --- /dev/null +++ b/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java @@ -0,0 +1,28 @@ +package de.steamwar.bungeecore.network.handlers; + +import de.steamwar.bungeecore.BungeeCore; +import de.steamwar.bungeecore.util.SchematicSearch; +import de.steamwar.network.packets.PacketHandler; +import de.steamwar.network.packets.client.RequestSchematicSearchPacket; +import de.steamwar.sql.SchematicNode; +import de.steamwar.sql.SteamwarUser; +import net.md_5.bungee.api.connection.ProxiedPlayer; + +public class SchematicSearchRequestHandler extends PacketHandler { + + @Handler + public void handle(RequestSchematicSearchPacket packet) { + SteamwarUser user = SteamwarUser.get(packet.getPlayerId()); + SchematicNode node = SchematicNode.getSchematicNode(packet.getSchematicId()); + ProxiedPlayer proxiedPlayer = BungeeCore.get().getProxy().getPlayer(user.getUUID()); + if(proxiedPlayer == null) { + return; + } + SchematicSearch.SchematicSearchBehavior behavior = SchematicSearch.SchematicSearchBehavior.builder() + .airAsAny(packet.isAirAsAny()) + .ignoreAir(packet.isIgnoreAir()) + .ignoreBlockData(packet.isIgnoreBlockData()) + .build(); + SchematicSearch.queueSearch(proxiedPlayer, node, behavior); + } +} diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index 4b4dcfb..2810984 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -10,6 +10,8 @@ import de.steamwar.sql.NodeData; import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.connection.ProxiedPlayer; @@ -50,13 +52,13 @@ public class SchematicSearch { }, 0, 1, TimeUnit.SECONDS); } - public static void queueSearch(ProxiedPlayer player, SchematicNode node ) { + public static void queueSearch(ProxiedPlayer player, SchematicNode node, SchematicSearchBehavior behavior) { SteamwarUser user = SteamwarUser.get(player.getUniqueId()); synchronized (searchQueue) { if(user.getUserGroup().isAdminGroup()) { - searchQueue.add(0, new SchematicSearch(player, node)); + searchQueue.add(0, new SchematicSearch(player, node, behavior)); } else { - searchQueue.add(new SchematicSearch(player, node)); + searchQueue.add(new SchematicSearch(player, node, behavior)); } } if(watchdog == null) { @@ -79,8 +81,25 @@ public class SchematicSearch { } } - private static List constructArguments(SteamwarUser user, File pattern) { - return Arrays.asList(searchBinary, "-T", "2", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50", pattern.getAbsolutePath()); + public static int getQueueSize() { + synchronized (searchQueue) { + return searchQueue.size(); + } + } + + private static List constructArguments(SteamwarUser user, File pattern, SchematicSearchBehavior behavior) { + List args = new ArrayList<>(Arrays.asList(searchBinary, "-T", "2", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50")); + if(behavior.isAirAsAny()) { + args.add("-A"); + } + if(behavior.isIgnoreAir()) { + args.add("-a"); + } + if(behavior.isIgnoreBlockData()) { + args.add("-d"); + } + args.add(pattern.getAbsolutePath()); + return args; } public static boolean removeFromQueue(ProxiedPlayer player) { @@ -120,13 +139,15 @@ public class SchematicSearch { private final ProxiedPlayer player; private final SchematicNode node; + private final SchematicSearchBehavior behavior; private File pattern; private ScheduledTask task; private Process process; - private SchematicSearch(ProxiedPlayer player, SchematicNode node) { + private SchematicSearch(ProxiedPlayer player, SchematicNode node, SchematicSearchBehavior behavior) { this.player = player; this.node = node; + this.behavior = behavior; } private static String readInputStream(InputStream is) throws IOException { @@ -157,7 +178,7 @@ public class SchematicSearch { } Message.send("SCHEMATIC_SEARCH_STARTED", player, node.getName()); pattern = schematicNodeToTempFile(data); - ProcessBuilder builder = new ProcessBuilder(constructArguments(SteamwarUser.get(player.getUniqueId()), pattern)); + ProcessBuilder builder = new ProcessBuilder(constructArguments(SteamwarUser.get(player.getUniqueId()), pattern, behavior)); process = builder.start(); InputStream stdout = process.getInputStream(); InputStream stderr = process.getErrorStream(); @@ -268,4 +289,15 @@ public class SchematicSearch { float percent; String name; } + + @Builder + @Getter + public static class SchematicSearchBehavior { + @Builder.Default + boolean airAsAny = false; + @Builder.Default + boolean ignoreAir = false; + @Builder.Default + boolean ignoreBlockData = false; + } } diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index cf71a79..ac5db50 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -121,6 +121,7 @@ MOD_USE_MODSENDER=§cPlease use the §c§lFabricModSender§c (https://steamwar.d #Various commands ALERT=§f{0} STAT_SERVER=§7Server §e{0}§8: §7Below limit §e{1} §7Server count §e{2} +STAT_SEARCH_QUEUE=§7Schematic search queue: §e{0} #Ban&Mute-Command PUNISHMENT_USAGE=§8/§7{0} §8[§eplayer§8] [§edd§8.§emm§8.§eyyyy §7or §edd§8.§emm§8.§eyyyy§8_§ehh§8:§emm §7or §eperma§8] [§ereason§8] -- 2.39.2 From 3bd18531f5ac42a1ca7efe03ffc910c030d72a9c Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Wed, 12 Apr 2023 02:08:09 +0200 Subject: [PATCH 08/11] Update CC --- CommonCore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CommonCore b/CommonCore index 595df40..e8d964d 160000 --- a/CommonCore +++ b/CommonCore @@ -1 +1 @@ -Subproject commit 595df40e1e9078c310cbbb4bbded07744c361f15 +Subproject commit e8d964d8333f353d9995e8319694077e0f8e063b -- 2.39.2 From ec0c06227fc5ec490ef513e7e64d2b3098543817 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Wed, 12 Apr 2023 02:08:59 +0200 Subject: [PATCH 09/11] Remove TestCommand --- src/de/steamwar/bungeecore/BungeeCore.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/de/steamwar/bungeecore/BungeeCore.java b/src/de/steamwar/bungeecore/BungeeCore.java index 0076c06..5307714 100644 --- a/src/de/steamwar/bungeecore/BungeeCore.java +++ b/src/de/steamwar/bungeecore/BungeeCore.java @@ -172,7 +172,6 @@ public class BungeeCore extends Plugin { new CalendarListener(); new ModCommand(); - new SchemSearchTestCommand(); // Punishment Commands: new PunishmentCommand("ban", Punishment.PunishmentType.Ban); -- 2.39.2 From 1c54b1c60f04437231743ff04bcd6e353138ce7b Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Thu, 13 Apr 2023 19:02:48 +0200 Subject: [PATCH 10/11] Add Copyright --- .../listeners/SchematicSearchListener.java | 19 +++++++++++++++++++ .../SchematicSearchRequestHandler.java | 19 +++++++++++++++++++ .../bungeecore/util/SchematicSearch.java | 19 +++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java b/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java index 103ebe0..65da859 100644 --- a/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java +++ b/src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package de.steamwar.bungeecore.listeners; import de.steamwar.bungeecore.Message; diff --git a/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java b/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java index 5078245..45f6834 100644 --- a/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java +++ b/src/de/steamwar/bungeecore/network/handlers/SchematicSearchRequestHandler.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package de.steamwar.bungeecore.network.handlers; import de.steamwar.bungeecore.BungeeCore; diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index 2810984..a3b8971 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -1,3 +1,22 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package de.steamwar.bungeecore.util; import com.google.gson.Gson; -- 2.39.2 From 3b0e4aa5aad96d5fc5af4f6eebd6f7ef3ab315c4 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 1 May 2023 16:12:45 +0200 Subject: [PATCH 11/11] Fix SchematicSearch --- .../bungeecore/util/SchematicSearch.java | 57 +++++++------------ .../steamwar/messages/BungeeCore.properties | 10 ++-- .../messages/BungeeCore_de.properties | 14 ++++- 3 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/de/steamwar/bungeecore/util/SchematicSearch.java b/src/de/steamwar/bungeecore/util/SchematicSearch.java index a3b8971..6d189ea 100644 --- a/src/de/steamwar/bungeecore/util/SchematicSearch.java +++ b/src/de/steamwar/bungeecore/util/SchematicSearch.java @@ -19,7 +19,6 @@ package de.steamwar.bungeecore.util; -import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -42,49 +41,37 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.zip.GZIPOutputStream; public class SchematicSearch { - - private static final Gson gson = new Gson(); - private static final String searchBinary = "/home/chaoscaot/schemsearch/target/release/schemsearch-cli"; - private static final List searchQueue = new ArrayList<>(); + private static final String SEARCH_BINARY = "/home/chaoscaot/schemsearch/target/release/schemsearch-cli"; + private static final LinkedBlockingQueue searchQueue = new LinkedBlockingQueue<>(); private static SchematicSearch currentSearch; - private static ScheduledTask watchdog; - private static void startQueueWatchdog() { - watchdog = BungeeCore.get().getProxy().getScheduler().schedule(BungeeCore.get(), () -> { - synchronized (searchQueue) { - if(searchQueue.isEmpty()) { - watchdog.cancel(); - watchdog = null; - return; - } + private static void startNext() { + if(currentSearch != null) { + return; + } - if(currentSearch == null) { - currentSearch = searchQueue.remove(0); - currentSearch.start(); - } + synchronized (searchQueue) { + if(searchQueue.isEmpty()) { + return; } - }, 0, 1, TimeUnit.SECONDS); + currentSearch = searchQueue.poll(); + currentSearch.start(); + } } public static void queueSearch(ProxiedPlayer player, SchematicNode node, SchematicSearchBehavior behavior) { - SteamwarUser user = SteamwarUser.get(player.getUniqueId()); synchronized (searchQueue) { - if(user.getUserGroup().isAdminGroup()) { - searchQueue.add(0, new SchematicSearch(player, node, behavior)); - } else { - searchQueue.add(new SchematicSearch(player, node, behavior)); + searchQueue.add(new SchematicSearch(player, node, behavior)); + startNext(); + if (!searchQueue.isEmpty()) { + Message.send("SCHEMATIC_SEARCH_QUEUED", player); } } - if(watchdog == null) { - startQueueWatchdog(); - } else { - Message.send("SCHEMATIC_SEARCH_QUEUED", player); - } } public static void abortAll() { @@ -107,7 +94,7 @@ public class SchematicSearch { } private static List constructArguments(SteamwarUser user, File pattern, SchematicSearchBehavior behavior) { - List args = new ArrayList<>(Arrays.asList(searchBinary, "-T", "2", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50")); + List args = new ArrayList<>(Arrays.asList(SEARCH_BINARY, "-T", "2", "-s", "-u", String.valueOf(user.getId()), "-o", "json:std", "-m", "50")); if(behavior.isAirAsAny()) { args.add("-A"); } @@ -269,13 +256,10 @@ public class SchematicSearch { if (matches.size() >= 49) { Message.send("SCHEMATIC_SEARCH_TOO_MANY_RESULTS", player); } - - end(); - } catch (IOException | InterruptedException e) { - end(); } catch (Exception e) { - end(); throw new SecurityException(e); + } finally { + end(); } }); } @@ -298,6 +282,7 @@ public class SchematicSearch { currentSearch = null; } } + startNext(); } @AllArgsConstructor diff --git a/src/de/steamwar/messages/BungeeCore.properties b/src/de/steamwar/messages/BungeeCore.properties index e43a398..4bc9401 100644 --- a/src/de/steamwar/messages/BungeeCore.properties +++ b/src/de/steamwar/messages/BungeeCore.properties @@ -696,11 +696,11 @@ MOD_ITEM_BACK=§7Back SCHEMATIC_SEARCH_QUEUED=§7Your search has been queued and will be executed shortly. SCHEMATIC_SEARCH_STARTED=§7Your search for "§e{0}§7" has started. SCHEMATIC_SEARCH_NO_RESULTS=§cNo results found. -SCHEMATIC_SEARCH_RESULT_HEADER=§7Searched in §e{0} §7schematics in§e{1}. +SCHEMATIC_SEARCH_RESULT_HEADER=§7Searched §e{0} §7schematics in§e{1}. SCHEMATIC_SEARCH_RESULT=§7{0}: §e{1}§7% §8(§e{2}§7,§e{3},§e{4}§8) -SCHEMATIC_SEARCH_RESULT_HOVER=§7Click to get more info about {0}. -SCHEMATIC_SEARCH_NOT_SUPPORTED=§cThis schematic is not supported by the schematic search. +SCHEMATIC_SEARCH_RESULT_HOVER=§7Click for more info about {0}. +SCHEMATIC_SEARCH_NOT_SUPPORTED=§cThis schematic is not supported because it is on an old format. SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE=§cYour search has been removed from the queue because you switched servers. -SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE_SOFTRELOAD=§cYour search has been removed from the queue because of an update. -SCHEMATIC_SEARCH_CANCELED_SOFTRELOAD=§cYour search has been canceled because of an update. +SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE_SOFTRELOAD=§cYour search has been removed from the queue because of a software update. +SCHEMATIC_SEARCH_CANCELED_SOFTRELOAD=§cYour search has been cancelled because of a software update. SCHEMATIC_SEARCH_TOO_MANY_RESULTS=§cToo many results found. Please be more specific. \ No newline at end of file diff --git a/src/de/steamwar/messages/BungeeCore_de.properties b/src/de/steamwar/messages/BungeeCore_de.properties index 18c043f..8ef29df 100644 --- a/src/de/steamwar/messages/BungeeCore_de.properties +++ b/src/de/steamwar/messages/BungeeCore_de.properties @@ -647,4 +647,16 @@ ADVENT_CALENDAR_TITLE=§eAdventskalender ADVENT_CALENDAR_DAY=§7Tag§8: §e{0} ADVENT_CALENDAR_MESSAGE=§eHast du heute schon dein Geschenk geholt? ADVENT_CALENDAR_MESSAGE_HOVER=§eKlicken zum öffnen! -ADVENT_CALENDAR_OPEN=§7Du hast §e{0}§7 aus dem Adventskalender erhalten! \ No newline at end of file +ADVENT_CALENDAR_OPEN=§7Du hast §e{0}§7 aus dem Adventskalender erhalten! + +#Schematic Search +SCHEMATIC_SEARCH_QUEUED=§7Deine Suche wurde in die Warteschlange eingereiht. +SCHEMATIC_SEARCH_STARTED=§7Deine Suche nach §e{0} §7wurde gestartet. +SCHEMATIC_SEARCH_NO_RESULTS=§cEs wurden keine Ergebnisse gefunden. +SCHEMATIC_SEARCH_RESULT_HEADER=§e{0} §7Schematics in §e{1} §7durchsucht +SCHEMATIC_SEARCH_RESULT_HOVER=§7Klicke für mehr Informationen +SCHEMATIC_SEARCH_NOT_SUPPORTED=§cDiese Schematic ist in einem alten Format und kann nicht genutzt werden. +SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE=§cDeine Suche wurde aus der Warteschlange entfernt, weil du den Server gewechselt hast. +SCHEMATIC_SEARCH_REMOVED_FROM_QUEUE_SOFTRELOAD=§cWegen eines Software Updates wurde deine Suche aus der Warteschlange entfernt. +SCHEMATIC_SEARCH_CANCELED_SOFTRELOAD=§cDeine Suche wurde wegen eines Software Updates abgebrochen. +SCHEMATIC_SEARCH_TOO_MANY_RESULTS=§cEs wurden zu viele Ergebnisse gefunden. Bitte spezifiziere deine Suche. \ No newline at end of file -- 2.39.2