SteamWar/BungeeCore
Archiviert
13
2

WIP: Add SchemSearch #467

Entwurf
Chaoscaot möchte 14 Commits von schemsearch nach master mergen
5 geänderte Dateien mit 327 neuen und 0 gelöschten Zeilen
Nur Änderungen aus Commit 06d3aeb9d1 werden angezeigt - Alle Commits anzeigen

Datei anzeigen

@ -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);

Datei anzeigen

@ -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<SchematicNode> getSchematicNodeMapper() {
return new TypeMapper<SchematicNode>() {
@Override
public SchematicNode map(CommandSender commandSender, String[] previousArguments, String s) {
return SchematicNode.getNodeFromPath(SteamwarUser.get(commandSender.getName()), s);
}
@Override
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
return SchematicNode.getNodeTabcomplete(SteamwarUser.get(sender.getName()), s);
}
};
}
}

Datei anzeigen

@ -0,0 +1,22 @@
package de.steamwar.bungeecore.listeners;
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

License header

License header
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

@ -0,0 +1,252 @@
package de.steamwar.bungeecore.util;
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

.

.
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<SchematicSearch> 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 ) {
Veraltet
Review

Falscher Pfad.

Falscher Pfad.
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));
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(watchdog == null) {
startQueueWatchdog();
} else {
Message.send("SCHEMATIC_SEARCH_QUEUED", player);
}
}
private static List<String> 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));
}
Lixfel markierte diese Unterhaltung als gelöst Veraltet
Veraltet
Review

Lass solche Spezialberechtigungen raus.

Lass solche Spezialberechtigungen raus.
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);
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.
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<JsonObject> elements = Arrays.stream(outputs).map(JsonParser::parseString).map(JsonElement::getAsJsonObject).collect(Collectors.toList());
int searchCount = 0;
Lixfel markierte diese Unterhaltung als gelöst
Review

BufferedInputStream.readline()?

BufferedInputStream.readline()?
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;
}
}
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?
Message.send("SCHEMATIC_SEARCH_RESULT_HEADER", player, searchCount, DurationFormatUtils.formatDuration(searchTime, "' 'm'm 's's 'S'ms'")
.replace(" 0ms", "")
.replace(" 0s", "")
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.
.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];
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)
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;
}
}

Datei anzeigen

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