WIP: Add SchemSearch #467
@ -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);
|
||||
|
40
src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java
Normale Datei
40
src/de/steamwar/bungeecore/commands/SchemSearchTestCommand.java
Normale Datei
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
22
src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java
Normale Datei
22
src/de/steamwar/bungeecore/listeners/SchematicSearchListener.java
Normale Datei
@ -0,0 +1,22 @@
|
||||
package de.steamwar.bungeecore.listeners;
|
||||
Lixfel markierte diese Unterhaltung als gelöst
Veraltet
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
252
src/de/steamwar/bungeecore/util/SchematicSearch.java
Normale Datei
252
src/de/steamwar/bungeecore/util/SchematicSearch.java
Normale Datei
@ -0,0 +1,252 @@
|
||||
package de.steamwar.bungeecore.util;
|
||||
Lixfel markierte diese Unterhaltung als gelöst
Veraltet
Lixfel
hat
. .
|
||||
|
||||
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 ) {
|
||||
Lixfel
hat
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));
|
||||
Lixfel
hat
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
Lixfel
hat
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);
|
||||
Lixfel
hat
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
Lixfel
hat
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;
|
||||
}
|
||||
}
|
||||
Lixfel
hat
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
Lixfel
hat
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];
|
||||
Lixfel
hat
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;
|
||||
}
|
||||
}
|
@ -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.
|
In neuem Issue referenzieren
Einen Benutzer sperren
License header