SteamWar/BungeeCore
Archiviert
13
2

WIP: Add SchemSearch #467

Entwurf
Chaoscaot möchte 14 Commits von schemsearch nach master mergen
8 geänderte Dateien mit 428 neuen und 1 gelöschten Zeilen

Datei anzeigen

@ -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;
@ -122,6 +123,7 @@ public class BungeeCore extends Plugin {
new Fabric();
new SubserverProtocolFixer();
new PingListener();
new SchematicSearchListener();
local = new Node.LocalNode();
if(MAIN_SERVER) {
@ -233,6 +235,7 @@ public class BungeeCore extends Plugin {
tablistManager.disable();
errorLogger.unregister();
Statement.closeAll();
SchematicSearch.abortAll();
}
public static BungeeCore get() {

Datei anzeigen

@ -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<String, Integer> serverCount = new HashMap<>();
try {
Process process = new ProcessBuilder("ps", "x").start();

Datei anzeigen

@ -0,0 +1,41 @@
/*
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

License header

License header
* 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 <https://www.gnu.org/licenses/>.
*/
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());
}
}

Datei anzeigen

@ -31,5 +31,6 @@ public class BungeeNetworkHandler {
new ImALobbyHandler().register();
new InventoryCallbackHandler().register();
new PrepareSchemHandler().register();
new SchematicSearchRequestHandler().register();
}
}

Datei anzeigen

@ -0,0 +1,47 @@
/*
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

.

.
* 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 <https://www.gnu.org/licenses/>.
*/
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);
}
}

Datei anzeigen

@ -0,0 +1,307 @@
/*
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

.

.
* 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 <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bungeecore.util;
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 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;
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.*;
import java.util.stream.Collectors;
import java.util.zip.GZIPOutputStream;
public class SchematicSearch {
private static final String SEARCH_BINARY = "/home/chaoscaot/schemsearch/target/release/schemsearch-cli";
private static final LinkedBlockingQueue<SchematicSearch> searchQueue = new LinkedBlockingQueue<>();
private static SchematicSearch currentSearch;
Veraltet
Review

Falscher Pfad.

Falscher Pfad.
private static void startNext() {
if(currentSearch != null) {
return;
}
synchronized (searchQueue) {
Veraltet
Review

Warum den Task ständig starten und stoppen und nicht den Thread ständig offen lassen und einfach eventbasiert auf notifyAll() warten lassen?

Warum den Task ständig starten und stoppen und nicht den Thread ständig offen lassen und einfach eventbasiert auf notifyAll() warten lassen?
if(searchQueue.isEmpty()) {
return;
}
currentSearch = searchQueue.poll();
currentSearch.start();
}
}
public static void queueSearch(ProxiedPlayer player, SchematicNode node, SchematicSearchBehavior behavior) {
synchronized (searchQueue) {
searchQueue.add(new SchematicSearch(player, node, behavior));
startNext();
if (!searchQueue.isEmpty()) {
Message.send("SCHEMATIC_SEARCH_QUEUED", player);
}
}
}
public static void abortAll() {
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

Lass solche Spezialberechtigungen raus.

Lass solche Spezialberechtigungen raus.
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();
}
}
public static int getQueueSize() {
synchronized (searchQueue) {
return searchQueue.size();
}
}
private static List<String> constructArguments(SteamwarUser user, File pattern, SchematicSearchBehavior behavior) {
List<String> 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");
}
if(behavior.isIgnoreAir()) {
args.add("-a");
}
if(behavior.isIgnoreBlockData()) {
args.add("-d");
}
args.add(pattern.getAbsolutePath());
return args;
}
public static boolean removeFromQueue(ProxiedPlayer player) {
boolean removed;
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();
Review

Der Code in der Funktion (und den folgenden Methoden) wirkt sehr C-mäßig geschrieben. Evtl. kann man das nochmal etwas eleganter formulieren.

Der Code in der Funktion (und den folgenden Methoden) wirkt sehr C-mäßig geschrieben. Evtl. kann man das nochmal etwas eleganter formulieren.
is.close();
return f;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
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, SchematicSearchBehavior behavior) {
this.player = player;
this.node = node;
this.behavior = behavior;
}
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;
}
}
Lixfel markierte diese Unterhaltung als gelöst
Review

BufferedInputStream.readline()?

BufferedInputStream.readline()?
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, behavior));
process = builder.start();
InputStream stdout = process.getInputStream();
InputStream stderr = process.getErrorStream();
Review

Warum hier zwei verschiedene Tasks und nicht diesen Task mit dem "watchdog" konsolidieren?

Warum hier zwei verschiedene Tasks und nicht diesen Task mit dem "watchdog" konsolidieren?
String bar = "";
while (!process.waitFor(200, TimeUnit.MILLISECONDS)) {
String s = readInputStream(stderr);
Lixfel markierte diese Unterhaltung als gelöst
Review

Man könnte auch den Grund (altes Format) mit angeben, um dem User zu helfen.

Man könnte auch den Grund (altes Format) mit angeben, um dem User zu helfen.
if (s.length() > 0) {
if (s.contains("s[")) {
s = s.substring(s.lastIndexOf("s[") + 1);
}
s = s.replace("", "§e█§7");
bar = s;
}
BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + bar);
}
String s = readInputStream(stderr);
if (s.contains("s[")) {
s = s.substring(s.lastIndexOf("s[") + 1);
Veraltet
Review

Ich weiß nicht, was das harte Timing hier soll, und wie zuverlässig das hier so ist... (und ob das überhaupt nötig ist)

Ich weiß nicht, was das harte Timing hier soll, und wie zuverlässig das hier so ist... (und ob das überhaupt nötig ist)
}
BungeeCore.send(player, ChatMessageType.ACTION_BAR, "§7" + s.replace("", "§e█§7"));
s = readInputStream(stdout);
String[] outputs = s.split("\n");
List<JsonObject> elements = Arrays.stream(outputs).map(JsonParser::parseString).map(JsonElement::getAsJsonObject).collect(Collectors.toList());
int searchCount = 0;
long searchTime = 0;
List<Match> 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);
}
} catch (Exception e) {
throw new SecurityException(e);
} finally {
end();
}
});
}
public void end() {
if(process != null && process.isAlive()) {
process.destroy();
}
if(pattern != null) {
pattern.delete();
}
if(task != null) {
task.cancel();
Lixfel markierte diese Unterhaltung als gelöst
Review

Wie wäre es mit einem finally { end(); }? Und welche ungenannten Exception können da noch auftreten, dass die nochmal separat in einer SecurityException gewrappt werden müssen?

Wie wäre es mit einem finally { end(); }? Und welche ungenannten Exception können da noch auftreten, dass die nochmal separat in einer SecurityException gewrappt werden müssen?
}
synchronized (searchQueue) {
if(currentSearch == this) {
currentSearch = null;
}
}
startNext();
}
@AllArgsConstructor
private static class Match {
int x;
int y;
int z;
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;
}
}

Datei anzeigen

@ -121,6 +121,7 @@ MOD_USE_MODSENDER=§cPlease use the §c§lFabricModSender§c (https://steamwar.d
#Various commands
Lixfel markierte diese Unterhaltung als gelöst
Review

Deutsche Fassung fehlt noch.

Deutsche Fassung fehlt noch.
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 §enumber§8[§eh§7our|§ed§7ay|§ew§7eek|§em§7onth|§ey§7ear§8] §7or §eperma§8] [§ereason§8]
@ -690,3 +691,16 @@ 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.
Lixfel markierte diese Unterhaltung als gelöst
Review

Fehlt ein Leerzeichen? Und (ka was dabei gesucht wird) ist es grammatikalisch korrekt? (Searched {0} schematics in {1})?

Fehlt ein Leerzeichen? Und (ka was dabei gesucht wird) ist es grammatikalisch korrekt? (Searched {0} schematics in {1})?
SCHEMATIC_SEARCH_STARTED=§7Your search for "§e{0}§7" has started.
SCHEMATIC_SEARCH_NO_RESULTS=§cNo results found.
Lixfel markierte diese Unterhaltung als gelöst
Review

for more info?

for more info?
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 for more info about {0}.
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

software update

software update
SCHEMATIC_SEARCH_NOT_SUPPORTED=§cThis schematic is not supported because it is on an old format.
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

cancelled?

cancelled?
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 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.

Datei anzeigen

@ -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!
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.
Review

Deutsch Softwareupdate statt Software Update

Deutsch Softwareupdate statt Software Update
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.