diff --git a/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java b/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java index 96b135b..f23e7fc 100644 --- a/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java +++ b/SpigotCore_14/src/de/steamwar/core/WorldEditWrapper14.java @@ -29,10 +29,9 @@ import com.sk89q.worldedit.world.registry.LegacyMapper; import de.steamwar.sql.NoClipboardException; import org.bukkit.entity.Player; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.util.*; +import java.util.logging.Level; import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkNotNull; @@ -43,7 +42,7 @@ public class WorldEditWrapper14 implements WorldEditWrapper.IWorldEditWrapper { private static final ClipboardFormat SCHEM = BuiltInClipboardFormat.SPONGE_SCHEMATIC; @Override - public byte[] getPlayerClipboard(Player player, boolean schemFormat) { + public InputStream getPlayerClipboard(Player player, boolean schemFormat) { ClipboardHolder clipboardHolder; try { clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard(); @@ -55,21 +54,36 @@ public class WorldEditWrapper14 implements WorldEditWrapper.IWorldEditWrapper { if(clipboard == null) throw new NoClipboardException(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - try{ - if(schemFormat){ - ClipboardWriter writer = SCHEM.getWriter(outputStream); - writer.write(clipboard); - writer.close(); - }else{ - SCHEMATIC.getWriter(outputStream).write(clipboard); - } + PipedOutputStream outputStream = new PipedOutputStream(); + PipedInputStream inputStream; + try { + inputStream = new PipedInputStream(outputStream, 4096); }catch(NullPointerException e){ throw new RuntimeException(e.getMessage(), new IOException(e)); } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); + throw new SecurityException("Could not init piped input stream", e); } - return outputStream.toByteArray(); + + new Thread(() -> { + try{ + if(schemFormat){ + ClipboardWriter writer = SCHEM.getWriter(outputStream); + writer.write(clipboard); + writer.close(); + }else{ + SCHEMATIC.getWriter(outputStream).write(clipboard); + } + }catch(NullPointerException | IOException e) { + Core.getInstance().getLogger().log(Level.SEVERE, "Could not write schematic", e); + } + try { + outputStream.close(); + } catch (IOException e) { + Core.getInstance().getLogger().log(Level.SEVERE, "Could not close schem writer", e); + } + }, "SchemWriter").start(); + + return inputStream; } @Override diff --git a/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java b/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java index 3e243bd..2b1cb0e 100644 --- a/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java +++ b/SpigotCore_8/src/de/steamwar/core/WorldEditWrapper8.java @@ -19,19 +19,18 @@ import com.sk89q.worldedit.world.registry.WorldData; import de.steamwar.sql.NoClipboardException; import org.bukkit.entity.Player; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.logging.Level; import java.util.stream.Collectors; public class WorldEditWrapper8 implements WorldEditWrapper.IWorldEditWrapper { @Override - public byte[] getPlayerClipboard(Player player, boolean schemFormat) { + public InputStream getPlayerClipboard(Player player, boolean schemFormat) { ClipboardHolder clipboardHolder; try { clipboardHolder = WorldEditWrapper.getWorldEditPlugin().getSession(player).getClipboard(); @@ -43,13 +42,28 @@ public class WorldEditWrapper8 implements WorldEditWrapper.IWorldEditWrapper { if(clipboard == null) throw new NoClipboardException(); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PipedOutputStream outputStream = new PipedOutputStream(); + PipedInputStream inputStream; try { - ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData()); + inputStream = new PipedInputStream(outputStream, 4096); } catch (IOException e) { - throw new RuntimeException(e.getMessage(), e); + throw new SecurityException("Could not init piped input stream", e); } - return outputStream.toByteArray(); + + new Thread(() -> { + try { + ClipboardFormat.SCHEMATIC.getWriter(outputStream).write(clipboard, clipboardHolder.getWorldData()); + } catch (IOException e) { + Core.getInstance().getLogger().log(Level.SEVERE, "Could not write schematic", e); + } + try { + outputStream.close(); + } catch (IOException e) { + Core.getInstance().getLogger().log(Level.SEVERE, "Could not close schem writer", e); + } + }, "SchemWriter").start(); + + return inputStream; } @Override diff --git a/SpigotCore_Main/build.gradle b/SpigotCore_Main/build.gradle index 59c9f89..7a48ea5 100644 --- a/SpigotCore_Main/build.gradle +++ b/SpigotCore_Main/build.gradle @@ -46,6 +46,11 @@ dependencies { compileOnly files("${project.rootDir}/lib/Spigot-1.15.jar") compileOnly files("${project.rootDir}/lib/WorldEdit-1.12.jar") implementation 'net.wesjd:anvilgui:1.4.0-SNAPSHOT' + + compileOnly 'org.projectlombok:lombok:1.18.6' + testCompileOnly 'org.projectlombok:lombok:1.18.6' + annotationProcessor 'org.projectlombok:lombok:1.18.6' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.6' } processResources { diff --git a/SpigotCore_Main/src/SpigotCore.properties b/SpigotCore_Main/src/SpigotCore.properties new file mode 100644 index 0000000..e36969f --- /dev/null +++ b/SpigotCore_Main/src/SpigotCore.properties @@ -0,0 +1,55 @@ +# +# This file is a part of the SteamWar software. +# +# Copyright (C) 2021 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 . +# + +SCHEM_SELECTOR_TITLE={0} auswählen: {1} +SCHEM_SELECTOR_BACK=§eZurück +SCHEM_SELECTOR_DIR=§9Ordner +SCHEM_SELECTOR_RANK=§8Rang {0} +SCHEM_SELECTOR_OWN=§7Eigene Schematics +SCHEM_SELECTOR_PUB=§7Public Schematics +SCHEM_SELECTOR_SEL_DIR=§7Ordner auswählen +SCHEM_SELECTOR_NEW_DIR=§7Neuer Ordner +SCHEM_SELECTOR_FILTER=§7Filter + +SCHEM_SELECTOR_ITEM_NAME=§e{0} +SCHEM_SELECTOR_ITEM_NAME_FILTER=§7{0} +SCHEM_SELECTOR_ITEM_REPLACE=§e{0}§7 +SCHEM_SELECTOR_ITEM_LORE_TYPE=§7{0} + +SCHEM_SELECTOR_CREATE_DIR_TITLE=Ordner erstellen + +SCHEM_SELECTOR_FILTER_TITLE=Filter +SCHEM_SELECTOR_FILTER_ENTER_NAME=Name eingeben +SCHEM_SELECTOR_FILTER_NAME=§7Nach Namen suchen... +SCHEM_SELECTOR_FILTER_NAME_SEARCH=§7Suchbegriff: §e{0} +SCHEM_SELECTOR_FILTER_ENTER_OWNER=Besitzer eingeben +SCHEM_SELECTOR_FILTER_OWNER=§7Nach Besitzer suchen... +SCHEM_SELECTOR_FILTER_OWNER_SEARCH=§7Besitzer: §e{0} +SCHEM_SELECTOR_FILTER_SEL_TYPE=Typ wählen... +SCHEM_SELECTOR_FILTER_TYPE=§7Nach Typ filtern... +SCHEM_SELECTOR_FILTER_TYPE_SEARCH=§7Typ: §e{0} +SCHEM_SELECTOR_FILTER_MAT=§7Nach Item filtern... +SCHEM_SELECTOR_FILTER_MAT_SEARCH=§7Item: §e{0} +SCHEM_SELECTOR_CANCEL=§eAbbrechen +SCHEM_SELECTOR_GO=§eSuchen... +SCHEM_SELECTOR_SCHEMATIC=Schematic +SCHEM_SELECTOR_DIRECTORY=Ordner +SCHEM_SELECTOR_SCHEMATIC_NODE=Schematic/Ordner + +MATERIAL_SELECTOR_TITLE=Material auswählen \ No newline at end of file diff --git a/SpigotCore_Main/src/SpigotCore_en.properties b/SpigotCore_Main/src/SpigotCore_en.properties new file mode 100644 index 0000000..e69de29 diff --git a/SpigotCore_Main/src/de/steamwar/command/CommandNoHelpException.java b/SpigotCore_Main/src/de/steamwar/command/CommandNoHelpException.java new file mode 100644 index 0000000..41b487b --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/CommandNoHelpException.java @@ -0,0 +1,26 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.command; + +class CommandNoHelpException extends RuntimeException { + + CommandNoHelpException() { + } +} diff --git a/SpigotCore_Main/src/de/steamwar/command/GuardCheckType.java b/SpigotCore_Main/src/de/steamwar/command/GuardCheckType.java new file mode 100644 index 0000000..0f023b8 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/GuardCheckType.java @@ -0,0 +1,26 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.command; + +public enum GuardCheckType { + COMMAND, + HELP_COMMAND, + TAB_COMPLETE +} diff --git a/SpigotCore_Main/src/de/steamwar/command/GuardChecker.java b/SpigotCore_Main/src/de/steamwar/command/GuardChecker.java new file mode 100644 index 0000000..e8f3a1f --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/GuardChecker.java @@ -0,0 +1,30 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.command; + +import org.bukkit.command.CommandSender; + +@FunctionalInterface +public interface GuardChecker { + /** + * While guarding the first parameter of the command the parameter s of this method is {@code null} + */ + GuardResult guard(CommandSender commandSender, GuardCheckType guardCheckType, String[] previousArguments, String s); +} diff --git a/SpigotCore_Main/src/de/steamwar/command/GuardResult.java b/SpigotCore_Main/src/de/steamwar/command/GuardResult.java new file mode 100644 index 0000000..9ffbf77 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/command/GuardResult.java @@ -0,0 +1,26 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.command; + +public enum GuardResult { + ALLOWED, + DENIED_WITH_HELP, + DENIED +} diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java index e6926da..258ab5c 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommand.java @@ -19,14 +19,19 @@ package de.steamwar.command; +import de.steamwar.message.Message; +import lombok.Setter; +import net.md_5.bungee.api.chat.ClickEvent; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import java.lang.annotation.*; import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.function.IntPredicate; import java.util.logging.Level; @@ -39,6 +44,11 @@ public abstract class SWCommand { private final List commandList = new ArrayList<>(); private final List commandHelpList = new ArrayList<>(); private final Map> localTypeMapper = new HashMap<>(); + private final Map localGuardChecker = new HashMap<>(); + + @Setter + private Message message = null; + private List defaultHelpMessages = new ArrayList<>(); protected SWCommand(String command) { this(command, new String[0]); @@ -51,8 +61,13 @@ public abstract class SWCommand { if (!initialized) { createMapping(); } - if (commandList.stream().anyMatch(s -> s.invoke(sender, args))) return false; - commandHelpList.stream().anyMatch(s -> s.invoke(sender, args)); + try { + if (!commandList.stream().anyMatch(s -> s.invoke(sender, args))) { + commandHelpList.stream().anyMatch(s -> s.invoke(sender, args)); + } + } catch (CommandNoHelpException e) { + // Ignored + } return false; } @@ -76,7 +91,7 @@ public abstract class SWCommand { } private synchronized void createMapping() { - Method[] methods = getClass().getDeclaredMethods(); + List methods = methods(); for (Method method : methods) { addMapper(Mapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value(), typeMapper); @@ -84,19 +99,26 @@ public abstract class SWCommand { addMapper(ClassMapper.class, method, i -> i == 0, false, TypeMapper.class, (anno, typeMapper) -> { (anno.local() ? localTypeMapper : SWCommandUtils.MAPPER_FUNCTIONS).putIfAbsent(anno.value().getTypeName(), typeMapper); }); + addGuard(Guard.class, method, i -> i == 0, false, GuardChecker.class, (anno, guardChecker) -> { + (anno.local() ? localGuardChecker : SWCommandUtils.GUARD_FUNCTIONS).putIfAbsent(anno.value(), guardChecker); + }); + addGuard(ClassGuard.class, method, i -> i == 0, false, GuardChecker.class, (anno, guardChecker) -> { + (anno.local() ? localGuardChecker : SWCommandUtils.GUARD_FUNCTIONS).putIfAbsent(anno.value().getTypeName(), guardChecker); + }); + add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> { if (!anno.help()) return; if (parameters.length != 2) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking parameters or has too many"); + Bukkit.getLogger().log(Level.WARNING, () -> "The method '" + method.toString() + "' is lacking parameters or has too many"); } if (!parameters[parameters.length - 1].isVarArgs()) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument"); + Bukkit.getLogger().log(Level.WARNING, () -> "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument"); } if (parameters[parameters.length - 1].getType().getComponentType() != String.class) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument"); + Bukkit.getLogger().log(Level.WARNING, () -> "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument"); return; } - commandHelpList.add(new SubCommand(this, method, anno.value(), new HashMap<>())); + commandHelpList.add(new SubCommand(this, method, anno.value(), new HashMap<>(), localGuardChecker, true, null)); }); } for (Method method : methods) { @@ -114,11 +136,11 @@ public abstract class SWCommand { } String name = mapper != null ? mapper.value() : clazz.getTypeName(); if (!SWCommandUtils.MAPPER_FUNCTIONS.containsKey(name) && !localTypeMapper.containsKey(name)) { - Bukkit.getLogger().log(Level.WARNING, "The parameter '" + parameter.toString() + "' is using an unsupported Mapper of type '" + name + "'"); + Bukkit.getLogger().log(Level.WARNING, () -> "The parameter '" + parameter.toString() + "' is using an unsupported Mapper of type '" + name + "'"); return; } } - commandList.add(new SubCommand(this, method, anno.value(), localTypeMapper)); + commandList.add(new SubCommand(this, method, anno.value(), localTypeMapper, localGuardChecker, false, anno.description())); }); this.commandList.sort((o1, o2) -> { @@ -130,7 +152,15 @@ public abstract class SWCommand { o2.varArgType != null ? Integer.MAX_VALUE : o2.arguments.length); } }); - commandHelpList.sort(Comparator.comparingInt(o -> -o.subCommand.length)); + commandHelpList.sort((o1, o2) -> { + int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length); + if (compare != 0) { + return compare; + } else { + return Integer.compare(o1.method.getDeclaringClass() == SWCommand.class ? 1 : 0, + o2.method.getDeclaringClass() == SWCommand.class ? 1 : 0); + } + }); } initialized = true; } @@ -141,15 +171,15 @@ public abstract class SWCommand { Parameter[] parameters = method.getParameters(); if (!parameterTester.test(parameters.length)) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking parameters or has too many"); + Bukkit.getLogger().log(Level.WARNING, () -> "The method '" + method.toString() + "' is lacking parameters or has too many"); return; } if (firstParameter && !CommandSender.class.isAssignableFrom(parameters[0].getType())) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the first parameter of type '" + CommandSender.class.getTypeName() + "'"); + Bukkit.getLogger().log(Level.WARNING, () -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + CommandSender.class.getTypeName() + "'"); return; } if (returnType != null && method.getReturnType() != returnType) { - Bukkit.getLogger().log(Level.WARNING, "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); + Bukkit.getLogger().log(Level.WARNING, () -> "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'"); return; } Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters)); @@ -166,6 +196,27 @@ public abstract class SWCommand { }); } + private void addGuard(Class annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class returnType, BiConsumer consumer) { + add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> { + try { + method.setAccessible(true); + consumer.accept(anno, (GuardChecker) method.invoke(this)); + } catch (Exception e) { + throw new SecurityException(e.getMessage(), e); + } + }); + } + + public void addDefaultHelpMessage(String message) { + defaultHelpMessages.add(message); + } + + private List methods() { + List methods = new ArrayList<>(Arrays.asList(getClass().getDeclaredMethods())); + methods.addAll(Arrays.asList(SWCommand.class.getDeclaredMethods())); + return methods; + } + public void unregister() { SWCommandUtils.knownCommandMap.remove(command.getName()); command.getAliases().forEach(SWCommandUtils.knownCommandMap::remove); @@ -176,6 +227,56 @@ public abstract class SWCommand { SWCommandUtils.commandMap.register("steamwar", this.command); } + @Register(help = true) + private void internalHelp(Player p, String... args) { + if (message == null) { + return; + } + try { + message.sendPrefixless("COMMAND_HELP_HEAD", p, command.getName()); + defaultHelpMessages.forEach(s -> message.sendPrefixless(s, p)); + } catch (Exception e) { + Bukkit.getLogger().log(Level.WARNING, "Failed to send help message", e); + return; + } + AtomicInteger atomicInteger = new AtomicInteger(); + if (args.length != 0) { + commandList.forEach(subCommand -> { + List tabCompletes = subCommand.tabComplete(p, args); + if (tabCompletes == null || tabCompletes.isEmpty()) { + atomicInteger.incrementAndGet(); + return; + } + boolean hasTabCompletes = tabCompletes.stream() + .anyMatch(s -> s.toLowerCase().startsWith(args[args.length - 1].toLowerCase())); + if (hasTabCompletes) { + send(p, subCommand); + } else { + atomicInteger.incrementAndGet(); + } + }); + } + if (args.length == 0 || atomicInteger.get() == commandList.size()) { + commandList.forEach(subCommand -> { + if (subCommand.guardChecker == null || subCommand.guardChecker.guard(p, GuardCheckType.TAB_COMPLETE, new String[0], null) == GuardResult.ALLOWED) { + send(p, subCommand); + } + }); + } + } + + private void send(Player p, SubCommand subCommand) { + try { + for (String s : subCommand.description) { + String hover = "§8/§e" + command.getName() + " " + String.join(" ", subCommand.subCommand); + String suggest = "/" + command.getName() + " " + String.join(" ", subCommand.subCommand); + message.sendPrefixless(s, p, hover, new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, suggest)); + } + } catch (Exception e) { + Bukkit.getLogger().log(Level.WARNING, "Failed to send description of registered method '" + subCommand.method + "' with description '" + subCommand.description + "'", e); + } + } + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Repeatable(Register.Registeres.class) @@ -184,6 +285,8 @@ public abstract class SWCommand { boolean help() default false; + String[] description() default {}; + @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @interface Registeres { @@ -206,4 +309,20 @@ public abstract class SWCommand { boolean local() default false; } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER, ElementType.METHOD}) + protected @interface Guard { + String value() default ""; + + boolean local() default false; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + protected @interface ClassGuard { + Class value(); + + boolean local() default false; + } } diff --git a/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java b/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java index 64c735d..1d7110f 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java +++ b/SpigotCore_Main/src/de/steamwar/command/SWCommandUtils.java @@ -19,6 +19,7 @@ package de.steamwar.command; +import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SteamwarUser; import org.bukkit.Bukkit; import org.bukkit.GameMode; @@ -44,6 +45,7 @@ public class SWCommandUtils { } static final Map> MAPPER_FUNCTIONS = new HashMap<>(); + static final Map GUARD_FUNCTIONS = new HashMap<>(); static final TypeMapper ERROR_FUNCTION = createMapper(s -> { throw new SecurityException(); @@ -71,6 +73,17 @@ public class SWCommandUtils { return null; }, s -> Arrays.asList("s", "survival", "0", "c", "creative", "1", "sp", "spectator", "3", "a", "adventure", "2"))); MAPPER_FUNCTIONS.put(SteamwarUser.class.getTypeName(), createMapper(SteamwarUser::get, s -> Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList()))); + MAPPER_FUNCTIONS.put(SchematicNode.class.getTypeName(), new TypeMapper() { + @Override + public List tabCompletes(CommandSender commandSender, String[] strings, String s) { + return SchematicNode.getNodeTabcomplete(SteamwarUser.get(((Player) commandSender).getUniqueId()), s); + } + + @Override + public SchematicNode map(CommandSender commandSender, String[] previousArguments, String s) { + return SchematicNode.getNodeFromPath(SteamwarUser.get(((Player) commandSender).getUniqueId()), s); + } + }); } private static void addMapper(Class clazz, Class alternativeClazz, TypeMapper mapper) { @@ -100,7 +113,7 @@ public class SWCommandUtils { } } - static Object[] generateArgumentArray(CommandSender commandSender, TypeMapper[] parameters, String[] args, Class varArgType, String[] subCommand) throws CommandParseException { + static Object[] generateArgumentArray(CommandSender commandSender, TypeMapper[] parameters, GuardChecker[] guards, String[] args, Class varArgType, String[] subCommand) throws CommandParseException { Object[] arguments = new Object[parameters.length + 1]; int index = 0; while (index < subCommand.length) { @@ -142,6 +155,14 @@ public class SWCommandUtils { MAPPER_FUNCTIONS.putIfAbsent(name, mapper); } + public static void addGuard(Class clazz, GuardChecker guardChecker) { + addGuard(clazz.getTypeName(), guardChecker); + } + + public static void addGuard(String name, GuardChecker guardChecker) { + GUARD_FUNCTIONS.putIfAbsent(name, guardChecker); + } + public static TypeMapper createMapper(Function mapper, Function> tabCompleter) { return createMapper(mapper, (commandSender, s) -> tabCompleter.apply(s)); } diff --git a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java index 15dad9a..e18b5f8 100644 --- a/SpigotCore_Main/src/de/steamwar/command/SubCommand.java +++ b/SpigotCore_Main/src/de/steamwar/command/SubCommand.java @@ -19,6 +19,7 @@ package de.steamwar.command; +import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import java.lang.reflect.InvocationTargetException; @@ -27,29 +28,38 @@ import java.lang.reflect.Parameter; import java.util.*; import java.util.function.Function; import java.util.function.Predicate; +import java.util.logging.Level; import static de.steamwar.command.SWCommandUtils.*; class SubCommand { private SWCommand swCommand; - private Method method; + Method method; + String[] description; String[] subCommand; TypeMapper[] arguments; + GuardChecker[] guards; private Predicate commandSenderPredicate; private Function commandSenderFunction; + GuardChecker guardChecker; Class varArgType = null; + private boolean help; - SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper) { + SubCommand(SWCommand swCommand, Method method, String[] subCommand, Map> localTypeMapper, Map localGuardChecker, boolean help, String[] description) { this.swCommand = swCommand; this.method = method; + this.help = help; + this.description = description; Parameter[] parameters = method.getParameters(); commandSenderPredicate = sender -> parameters[0].getType().isAssignableFrom(sender.getClass()); commandSenderFunction = sender -> parameters[0].getType().cast(sender); this.subCommand = subCommand; + guardChecker = getGuardChecker(parameters[0], localGuardChecker); arguments = new TypeMapper[parameters.length - 1]; + guards = new GuardChecker[parameters.length - 1]; for (int i = 1; i < parameters.length; i++) { Parameter parameter = parameters[i]; Class clazz = parameter.getType(); @@ -76,9 +86,29 @@ class SubCommand { arguments[i - 1] = localTypeMapper.containsKey(name) ? localTypeMapper.get(name) : MAPPER_FUNCTIONS.getOrDefault(name, ERROR_FUNCTION); + guards[i - 1] = getGuardChecker(parameter, localGuardChecker); } } + private GuardChecker getGuardChecker(Parameter parameter, Map localGuardChecker) { + SWCommand.Guard guard = parameter.getAnnotation(SWCommand.Guard.class); + if (guard != null) { + if (guard.value() == null || guard.value().isEmpty()) { + String s = parameter.getType().getTypeName(); + if (parameter.isVarArgs()) { + s = parameter.getType().getComponentType().getTypeName(); + } + return localGuardChecker.getOrDefault(s, GUARD_FUNCTIONS.getOrDefault(s, null)); + } + GuardChecker current = localGuardChecker.getOrDefault(guard.value(), GUARD_FUNCTIONS.getOrDefault(guard.value(), null)); + if (current == null) { + Bukkit.getLogger().log(Level.WARNING, () -> "The guard checker with name '" + guard.value() + "' is neither a local guard checker nor a global one"); + } + return current; + } + return null; + } + boolean invoke(CommandSender commandSender, String[] args) { if (args.length < arguments.length + subCommand.length - (varArgType != null ? 1 : 0)) { return false; @@ -90,10 +120,38 @@ class SubCommand { if (!commandSenderPredicate.test(commandSender)) { return false; } - Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, args, varArgType, subCommand); + Object[] objects = SWCommandUtils.generateArgumentArray(commandSender, arguments, guards, args, varArgType, subCommand); objects[0] = commandSenderFunction.apply(commandSender); + for (int i = subCommand.length; i < args.length; i++) { + GuardChecker current; + if (i == subCommand.length) { + current = guardChecker; + } else { + if (i >= objects.length + subCommand.length) { + current = guards[guards.length - 1]; + } else { + current = guards[i - 1 - subCommand.length]; + } + } + if (current != null) { + GuardResult guardResult; + if (i == 0) { + guardResult = current.guard(commandSender, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND, new String[0], null); + } else { + guardResult = current.guard(commandSender, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND, Arrays.copyOf(args, i - 1), args[i - 1]); + } + if (guardResult != GuardResult.ALLOWED) { + if (guardResult == GuardResult.DENIED) { + throw new CommandNoHelpException(); + } + return false; + } + } + } method.setAccessible(true); method.invoke(swCommand, objects); + } catch (CommandNoHelpException e) { + throw e; } catch (IllegalAccessException | RuntimeException | InvocationTargetException e) { throw new SecurityException(e.getMessage(), e); } catch (CommandParseException e) { @@ -106,6 +164,9 @@ class SubCommand { if (varArgType == null && args.length > arguments.length + subCommand.length) { return null; } + if (guardChecker != null && guardChecker.guard(commandSender, GuardCheckType.TAB_COMPLETE, new String[0], null) != GuardResult.ALLOWED) { + return null; + } int index = 0; List argsList = new LinkedList<>(Arrays.asList(args)); for (String value : subCommand) { @@ -114,12 +175,19 @@ class SubCommand { if (!value.equalsIgnoreCase(s)) return null; index++; } + int guardIndex = 0; for (TypeMapper argument : arguments) { String s = argsList.remove(0); if (argsList.isEmpty()) { + if (guards[guardIndex] != null && guards[guardIndex].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, args.length - 1), s) != GuardResult.ALLOWED) { + return null; + } return argument.tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); } try { + if (guards[guardIndex] != null && guards[guardIndex].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, index), s) != GuardResult.ALLOWED) { + return null; + } if (argument.map(commandSender, Arrays.copyOf(args, index), s) == null) { return null; } @@ -127,14 +195,21 @@ class SubCommand { return null; } index++; + guardIndex++; } if (varArgType != null && !argsList.isEmpty()) { while (!argsList.isEmpty()) { String s = argsList.remove(0); if (argsList.isEmpty()) { + if (guards[guards.length - 1] != null && guards[guards.length - 1].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, args.length - 1), s) != GuardResult.ALLOWED) { + return null; + } return arguments[arguments.length - 1].tabCompletes(commandSender, Arrays.copyOf(args, args.length - 1), s); } try { + if (guards[guards.length - 1] != null && guards[guards.length - 1].guard(commandSender, GuardCheckType.TAB_COMPLETE, Arrays.copyOf(args, index), s) != GuardResult.ALLOWED) { + return null; + } if (arguments[arguments.length - 1].map(commandSender, Arrays.copyOf(args, index), s) == null) { return null; } diff --git a/SpigotCore_Main/src/de/steamwar/comms/packets/PrepareSchemPacket.java b/SpigotCore_Main/src/de/steamwar/comms/packets/PrepareSchemPacket.java index 99f2ecc..8c0968d 100644 --- a/SpigotCore_Main/src/de/steamwar/comms/packets/PrepareSchemPacket.java +++ b/SpigotCore_Main/src/de/steamwar/comms/packets/PrepareSchemPacket.java @@ -22,21 +22,29 @@ package de.steamwar.comms.packets; import com.google.common.io.ByteArrayDataOutput; import de.steamwar.comms.PacketIdManager; import de.steamwar.sql.Schematic; +import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicType; import de.steamwar.sql.SteamwarUser; public class PrepareSchemPacket extends SpigotPacket{ private final SteamwarUser user; - private final Schematic schematic; + private final SchematicNode schematic; private final SchematicType schematicType; - public PrepareSchemPacket(SteamwarUser user, Schematic schematic, SchematicType schematicType){ + public PrepareSchemPacket(SteamwarUser user, SchematicNode schematic, SchematicType schematicType) { this.user = user; this.schematic = schematic; this.schematicType = schematicType; } + @Deprecated + public PrepareSchemPacket(SteamwarUser user, Schematic schematic, SchematicType schematicType) { + this.user = user; + this.schematic = schematic.getNode(); + this.schematicType = schematicType; + } + @Override public int getName() { return PacketIdManager.PREPARE_SCHEM; @@ -45,7 +53,7 @@ public class PrepareSchemPacket extends SpigotPacket{ @Override public void writeVars(ByteArrayDataOutput byteArrayDataOutput) { byteArrayDataOutput.writeInt(user.getId()); - byteArrayDataOutput.writeInt(schematic.getSchemID()); + byteArrayDataOutput.writeInt(schematic.getId()); byteArrayDataOutput.writeUTF(schematicType.toDB()); } } diff --git a/SpigotCore_Main/src/de/steamwar/core/Core.java b/SpigotCore_Main/src/de/steamwar/core/Core.java index 4c28502..b0fc18f 100644 --- a/SpigotCore_Main/src/de/steamwar/core/Core.java +++ b/SpigotCore_Main/src/de/steamwar/core/Core.java @@ -25,6 +25,7 @@ import de.steamwar.core.events.ChattingEvent; import de.steamwar.core.events.ChunkListener; import de.steamwar.core.events.PlayerJoinedEvent; import de.steamwar.core.events.WorldLoadEvent; +import de.steamwar.message.Message; import de.steamwar.sql.SQL; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -39,6 +40,7 @@ public class Core extends JavaPlugin{ private static Core instance; private static final int version; + public static Message MESSAGE; static{ String packageName = Bukkit.getServer().getClass().getPackage().getName(); @@ -79,6 +81,8 @@ public class Core extends JavaPlugin{ } catch (IOException e) { throw new SecurityException("Could not load Hostname", e); } + + MESSAGE = new Message("SpigotCore", getClassLoader()); } @Override diff --git a/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java b/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java index 920bc9f..0b860ee 100644 --- a/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java +++ b/SpigotCore_Main/src/de/steamwar/core/WorldEditWrapper.java @@ -14,7 +14,7 @@ public class WorldEditWrapper { public static final IWorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance()); public interface IWorldEditWrapper { - byte[] getPlayerClipboard(Player player, boolean schemFormat); + InputStream getPlayerClipboard(Player player, boolean schemFormat); void setPlayerClipboard(Player player, InputStream is, boolean schemFormat); Clipboard getClipboard(InputStream is, boolean schemFormat) throws IOException; diff --git a/SpigotCore_Main/src/de/steamwar/inventory/SWListInv.java b/SpigotCore_Main/src/de/steamwar/inventory/SWListInv.java index 43c294f..d0d9c22 100644 --- a/SpigotCore_Main/src/de/steamwar/inventory/SWListInv.java +++ b/SpigotCore_Main/src/de/steamwar/inventory/SWListInv.java @@ -20,6 +20,7 @@ package de.steamwar.inventory; import de.steamwar.sql.Schematic; +import de.steamwar.sql.SchematicNode; import de.steamwar.sql.SchematicType; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -27,6 +28,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; import java.util.*; +import java.util.stream.Collectors; public class SWListInv extends SWInventory { @@ -115,26 +117,32 @@ public class SWListInv extends SWInventory { return onlinePlayers; } - public static List> getSchemList(SchematicType type, int steamwarUserId){ - List> schemList = new ArrayList<>(); + public static List> getSchemnodeList(SchematicType type, int steamwarUserId){ + List> schemList = new ArrayList<>(); - List schems; + List schems; if(type == null) - schems = Schematic.getSchemsAccessibleByUser(steamwarUserId); + schems = SchematicNode.getAllSchematicsAccessibleByUser(steamwarUserId); else - schems = Schematic.getSchemsOfType(steamwarUserId, type); + schems = SchematicNode.getAllAccessibleSchematicsOfType(steamwarUserId, type.toDB()); - for(Schematic s : schems){ + for(SchematicNode s : schems){ Material m; if(s.getItem().isEmpty()) m = SWItem.getMaterial("CAULDRON_ITEM"); else m = SWItem.getMaterial(s.getItem()); - SWItem item = new SWItem(m,"§e" + s.getSchemName()); + SWItem item = new SWItem(m,"§e" + s.getName()); + item.setEnchanted(s.isDir()); schemList.add(new SWListEntry<>(item, s)); } return schemList; } + + @Deprecated + public static List> getSchemList(SchematicType type, int steamwarUserId){ + return getSchemnodeList(type, steamwarUserId).stream().map(schematicNodeSWListEntry -> new SWListEntry(schematicNodeSWListEntry.getItem(), Schematic.wrap(schematicNodeSWListEntry.getObject()))).collect(Collectors.toList()); + } private boolean sizeBiggerMax(){ return dynamicSize ? elements.size() > 54 : elements.size() > 45; diff --git a/SpigotCore_Main/src/de/steamwar/inventory/SchematicSelector.java b/SpigotCore_Main/src/de/steamwar/inventory/SchematicSelector.java new file mode 100644 index 0000000..83e8ef8 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/inventory/SchematicSelector.java @@ -0,0 +1,429 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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.inventory; + +import de.steamwar.core.Core; +import de.steamwar.sql.SchematicNode; +import de.steamwar.sql.SchematicType; +import de.steamwar.sql.SteamwarUser; +import lombok.*; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; + +public class SchematicSelector { + + @Getter + private final Player player; + @Getter + private SteamwarUser user; + @Getter + private final Consumer callback; + private final SelectorTarget target; + @Getter + private final SelectorFilter filter = new SelectorFilter(); + private SchematicSelectorInjectable injectable = SchematicSelectorInjectable.DEFAULT; + @Setter + @Getter + private PublicMode publicMode = PublicMode.ALL; + @Setter + @Getter + private boolean singleDirOpen; + private boolean sdoTrigger = false; + + public SchematicSelector(Player player, SelectorTarget target, Consumer callback) { + this.player = player; + this.user = SteamwarUser.get(player.getUniqueId()); + this.target = target; + this.callback = callback; + this.singleDirOpen = !target.target.dirs; + } + + public SchematicSelector(Player player, SelectorTarget target, SchematicSelectorInjectable injectable, Consumer callback) { + this(player, target, callback); + this.injectable = injectable; + } + + public void open() { + injectable.onSelectorCreate(this); + if(publicMode == PublicMode.PUBLIC_ONLY) { + this.user = SteamwarUser.get(0); + } + openList(null); + } + + private void openList(SchematicNode parent) { + List nodes = filter.isFilter()?getFilteredSchematics():getSchematicList(parent); + + if(sdoTrigger) { + sdoTrigger = false; + return; + } + + List> list = new ArrayList<>(); + + if(parent != null) { + list.add(new SWListInv.SWListEntry<>(new SWItem(Material.ARROW, Core.MESSAGE.parse("SCHEM_SELECTOR_BACK", player), clickType -> {}), null)); + } + + for (SchematicNode node : nodes) { + if(node.getName().equals("//copy")) continue; + list.add(renderItem(node)); + } + + SWListInv inv = new SWListInv<>(player, MessageFormat.format(injectable.createTitle(player), target.target.getName(player), filter.getName() == null?(parent == null?"/":parent.generateBreadcrumbs(user)):filter.getName()), false, list, (clickType, node) -> handleClick(node, parent)); + if(publicMode == PublicMode.ALL) { + if(user.getId() == 0) { + inv.setItem(48, Material.BUCKET, Core.MESSAGE.parse("SCHEM_SELECTOR_OWN", player), clickType -> { + this.user = SteamwarUser.get(player.getUniqueId()); + openList(null); + }); + } else { + inv.setItem(48, Material.GLASS, Core.MESSAGE.parse("SCHEM_SELECTOR_PUB", player), clickType -> { + this.user = SteamwarUser.get(0); + openList(null); + }); + } + } + if(target.target.dirs) { + inv.setItem(49, SWItem.getDye(10), Core.MESSAGE.parse("SCHEM_SELECTOR_SEL_DIR", player), clickType -> { + player.closeInventory(); + callback.accept(parent); + }); + } + if(user.getId() != 0) { + inv.setItem(50, Material.CHEST, Core.MESSAGE.parse("SCHEM_SELECTOR_NEW_DIR", player), clickType -> createFolderIn(parent)); + } + inv.setItem(51, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER", player), clickType -> openFilter()); + + injectable.onListRender(this, inv, parent); + inv.open(); + } + + private void handleClick(SchematicNode node, SchematicNode parent) { + if(node == null) { + openList(getParent(parent)); + return; + } + if(node.isDir()) { + if(filter.isFilter() && target.target.dirs) { + player.closeInventory(); + callback.accept(node); + return; + } + filter.reset(); + openList(node); + return; + } + player.closeInventory(); + callback.accept(node); + } + + private SWListInv.SWListEntry renderItem(SchematicNode node) { + Material m = SWItem.getMaterial(node.getItem()); + + String name = Core.MESSAGE.parse(filter.name == null?"SCHEM_SELECTOR_ITEM_NAME":"SCHEM_SELECTOR_ITEM_NAME_FILTER", player, node.getName()); + + if(filter.getName() != null) { + name = name.replace(filter.getName(), Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_REPLACE", player, filter.getName())); + } + + SWItem item = new SWItem(m, name, Collections.singletonList(node.isDir() ? (Core.MESSAGE.parse("SCHEM_SELECTOR_DIR", player)) : Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_LORE_TYPE", player, node.getSchemtype().name())), !node.isDir() && !node.getSchemtype().writeable(), click -> { + }); + if(!node.isDir() && node.getRank() > 0) { + item.setLore(Arrays.asList(Core.MESSAGE.parse("SCHEM_SELECTOR_ITEM_LORE_TYPE", player, node.getSchemtype().name()), Core.MESSAGE.parse("SCHEM_SELECTOR_RANK", player))); + } + return new SWListInv.SWListEntry<>(item, node); + } + + private void createFolderIn(SchematicNode parent) { + SWAnvilInv inv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_CREATE_DIR_TITLE", player)); + inv.setItem(Material.CHEST); + inv.setCallback(s -> { + if(injectable.onFolderCreate(this, s)) { + SchematicNode.createSchematicDirectory(user.getId(), s, parent==null?0:parent.getId()); + openList(parent); + } + }); + inv.open(); + } + + private void openFilter() { + SWInventory inv = new SWInventory(player, 9, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TITLE", player)); + InvCallback nameCallback = clickType -> { + if(clickType.isRightClick()) { + filter.setName(null); + openFilter(); + } else { + SWAnvilInv swAnvilInv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_ENTER_NAME", player)); + swAnvilInv.setItem(Material.NAME_TAG); + swAnvilInv.setCallback(s -> { + filter.setName(s); + openFilter(); + }); + swAnvilInv.open(); + } + }; + if(filter.getName() == null) { + inv.setItem(0, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME", player), nameCallback); + } else { + inv.setItem(0, Material.NAME_TAG, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_NAME_SEARCH", player, filter.getName())), true, nameCallback); + } + + InvCallback ownerCallback = clickType -> { + if(clickType.isRightClick()) { + filter.setOwner(null); + openFilter(); + } else { + SWAnvilInv swAnvilInv = new SWAnvilInv(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_ENTER_OWNER", player)); + swAnvilInv.setItem(Material.PLAYER_HEAD); + swAnvilInv.setCallback(s -> { + filter.setOwner(SteamwarUser.get(s).getId()); + openFilter(); + }); + swAnvilInv.open(); + } + }; + if(filter.getOwner() == null) { + inv.setItem(1, Material.PLAYER_HEAD, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER", player), ownerCallback); + } else { + SteamwarUser tUser = SteamwarUser.get(filter.getOwner()); + SWItem item = SWItem.getPlayerSkull(user.getUserName()); + item.setName(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER", player)); + item.setEnchanted(true); + item.setLore(Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_OWNER_SEARCH", player, tUser.getUserName()))); + item.setCallback(ownerCallback); + inv.setItem(1, item); + } + + if(target.target != Target.SCHEMATIC_TYPE) { + InvCallback schemTypeCallback = clickType -> { + if(clickType.isRightClick()) { + filter.setType(null); + openFilter(); + } else { + List> types = new ArrayList<>(); + SchematicType.values().forEach(schematicType -> { + types.add(new SWListInv.SWListEntry<>(new SWItem(SWItem.getMaterial("STONE_BUTTON"), "§e" + schematicType.name(), Collections.emptyList(), schematicType.fightType(), n -> {}), schematicType)); + }); + SWListInv listInv = new SWListInv<>(player, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_SEL_TYPE", player), types, (clickType1, schematicType) -> { + filter.setType(schematicType); + openFilter(); + }); + listInv.open(); + } + }; + + if(filter.getType() == null) { + inv.setItem(2, SWItem.getMaterial("STONE_BUTTON"), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE", player), schemTypeCallback); + } else { + inv.setItem(2, SWItem.getMaterial("STONE_BUTTON"), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_TYPE_SEARCH", player, filter.getType().name())), true, schemTypeCallback); + } + } + + InvCallback materialCallback = clickType -> { + if(clickType.isRightClick()) { + filter.setItem(null); + openFilter(); + } else { + UtilGui.openMaterialSelector(player, material -> { + filter.setItem(material); + openFilter(); + }); + } + }; + + final int iSlot = target.target == Target.SCHEMATIC_TYPE?2:3; + + if(filter.getItem() == null) { + inv.setItem(iSlot, Material.STONE, Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT", player), materialCallback); + } else { + inv.setItem(iSlot, filter.getItem(), Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT", player), Collections.singletonList(Core.MESSAGE.parse("SCHEM_SELECTOR_FILTER_MAT_SEARCH", player, filter.getItem().name())), true, materialCallback); + } + + inv.setItem(7, SWItem.getDye(1), Core.MESSAGE.parse("SCHEM_SELECTOR_CANCEL", player), clickType -> { + filter.reset(); + openList(null); + }); + inv.setItem(8, SWItem.getDye(10), Core.MESSAGE.parse("SCHEM_SELECTOR_GO", player), clickType -> { + filter.setFilter(true); + injectable.onFilterApply(this); + openList(null); + }); + + injectable.onFilterRender(this, inv); + + inv.open(); + } + + private List getFilteredSchematics() { + List nodes = new ArrayList<>(SchematicNode.getAllSchematicsAccessibleByUser(user.getId())); + nodes.removeIf(node -> { + injectable.onNodeFilter(this, node); + return !filter.matches(node); + }); + if(target.target == Target.DIRECTORY) { + nodes.removeIf(node -> !node.isDir()); + } + if(target.target == Target.SCHEMATIC_TYPE) { + nodes.removeIf(node -> node.isDir() || !node.getType().equals(target.type.toDB())); + } + return nodes; + } + + private List getSchematicList(SchematicNode parent) { + List nodes = new ArrayList<>(); + switch (target.target) { + case DIRECTORY: + if(parent == null) { + nodes.addAll(SchematicNode.getSchematicsAccessibleByUser(user.getId(), null)); + nodes.removeIf(node -> !node.isDir()); + } else { + nodes.addAll(SchematicNode.getSchematicDirectoryInNode(parent.getId())); + } + break; + case SCHEMATIC_TYPE: + nodes.addAll(SchematicNode.getAccessibleSchematicsOfTypeInParent(user.getId(), target.type.toDB(), parent==null?0:parent.getId())); + if(target.rank >= 0) { + nodes.removeIf(node -> node.getRank() > target.rank); + } + break; + default: + nodes.addAll(SchematicNode.getSchematicsAccessibleByUser(user.getId(), parent == null?0:parent.getId())); + } + + if(singleDirOpen && nodes.size() == 1 && nodes.get(0).isDir()) { + openList(nodes.get(0)); + sdoTrigger = true; + } + return nodes; + } + + private static SchematicNode getParent(SchematicNode node) { + if(node.getParent() == null) { + return null; + } + return node.getParentNode(); + } + + public static SelectorTarget selectSchematic() { + return new SelectorTarget(Target.SCHEMATIC, null, -1); + } + + public static SelectorTarget selectDirectory() { + return new SelectorTarget(Target.DIRECTORY, null, -1); + } + + public static SelectorTarget selectSchematicNode() { + return new SelectorTarget(Target.SCHEMATIC_NODE, null, -1); + } + + public static SelectorTarget selectSchematicType(SchematicType type) { + return new SelectorTarget(Target.SCHEMATIC_TYPE, type, -1); + } + + public static SelectorTarget selectSchematicTypeWithRank(SchematicType type, int rank) { + return new SelectorTarget(Target.SCHEMATIC_TYPE, type, rank); + } + + @AllArgsConstructor + static class SelectorTarget { + + private final Target target; + private final SchematicType type; + private final int rank; + } + + @AllArgsConstructor + private enum Target { + SCHEMATIC("SCHEM_SELECTOR_SCHEMATIC", false), + DIRECTORY("SCHEM_SELECTOR_DIRECTORY", true), + SCHEMATIC_NODE("SCHEM_SELECTOR_SCHEMATIC_NODE", true), + SCHEMATIC_TYPE("SCHEM_SELECTOR_SCHEMATIC", false); + + private String rawName; + private boolean dirs; + + private String getName(Player player) { + return Core.MESSAGE.parse(rawName, player); + } + } + + @NoArgsConstructor + @Getter + @Setter + public static class SelectorFilter { + + private boolean filter; + + private String name = null; + private Integer owner = null; + private SchematicType type = null; + private Material item = null; + + public void reset() { + name = null; + owner = null; + type = null; + item = null; + filter = false; + } + + public boolean matches(SchematicNode node) { + boolean matches = true; + if(name != null && !node.getName().contains(name)) { + matches = false; + } + + if(owner != null && node.getOwner() != owner) { + matches = false; + } + + if(type != null && (node.isDir() || !node.getSchemtype().equals(type))) { + matches = false; + } + + if(item != null) { + String i; + if(node.getItem().isEmpty()) { + i = node.isDir()?"CHEST":"CAULDRON"; + } else { + i = node.getItem(); + } + if(!item.name().equals(i)) { + matches = false; + } + } + return matches; + } + } + + public enum PublicMode { + ALL, + PRIVATE_ONLY, + PUBLIC_ONLY + } +} diff --git a/SpigotCore_Main/src/de/steamwar/inventory/SchematicSelectorInjectable.java b/SpigotCore_Main/src/de/steamwar/inventory/SchematicSelectorInjectable.java new file mode 100644 index 0000000..7c4d718 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/inventory/SchematicSelectorInjectable.java @@ -0,0 +1,45 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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.inventory; + +import de.steamwar.core.Core; +import de.steamwar.sql.SchematicNode; +import org.bukkit.entity.Player; + +public interface SchematicSelectorInjectable { + + SchematicSelectorInjectable DEFAULT = new SchematicSelectorInjectable() {}; + + default String createTitle(Player player) { + return Core.MESSAGE.parse("SCHEM_SELECTOR_TITLE", player); + } + + default void onSelectorCreate(SchematicSelector selector) {} + + default void onListRender(SchematicSelector selector, SWListInv inv, SchematicNode parent) {} + + default void onFilterRender(SchematicSelector selector, SWInventory inventory) {} + + default void onFilterApply(SchematicSelector selector) {} + + default boolean onFolderCreate(SchematicSelector selector, String name) {return true;} + + default void onNodeFilter(SchematicSelector selector, SchematicNode node) {} +} diff --git a/SpigotCore_Main/src/de/steamwar/inventory/UtilGui.java b/SpigotCore_Main/src/de/steamwar/inventory/UtilGui.java new file mode 100644 index 0000000..298d0c0 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/inventory/UtilGui.java @@ -0,0 +1,54 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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.inventory; + +import de.steamwar.core.Core; +import de.steamwar.inventory.SWItem; +import de.steamwar.inventory.SWListInv; +import lombok.experimental.UtilityClass; +import org.bukkit.Material; +import org.bukkit.entity.Player; + +import java.util.LinkedList; +import java.util.List; +import java.util.function.Consumer; + +@UtilityClass +public class UtilGui { + + public static void openMaterialSelector(Player player, Consumer callback) { + openMaterialSelector(player, Core.MESSAGE.parse("MATERIAL_SELECTOR_TITLE", player), callback); + } + + public static void openMaterialSelector(Player player, String title, Consumer callback) { + List> materials = new LinkedList<>(); + for(Material material : Material.values()){ + if(material.name().startsWith(Material.LEGACY_PREFIX)) + continue; + SWItem item = new SWItem(material, "§7" + material.name()); + if(item.getItemMeta() != null && material.isItem()) { + materials.add(new SWListInv.SWListEntry<>(item, material)); + } + } + + SWListInv swListInv = new SWListInv<>(player, title, materials, (clickType3, material) -> callback.accept(material)); + swListInv.open(); + } +} diff --git a/SpigotCore_Main/src/de/steamwar/sql/CheckedSchematic.java b/SpigotCore_Main/src/de/steamwar/sql/CheckedSchematic.java index db8df93..0411239 100644 --- a/SpigotCore_Main/src/de/steamwar/sql/CheckedSchematic.java +++ b/SpigotCore_Main/src/de/steamwar/sql/CheckedSchematic.java @@ -1,6 +1,6 @@ -/* +/* This file is a part of the SteamWar software. - + Copyright (C) 2020 SteamWar.de-Serverteam This program is free software: you can redistribute it and/or modify @@ -24,72 +24,86 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; - public class CheckedSchematic { - private static final SQL.Statement checkHistory = new SQL.Statement("SELECT * FROM CheckedSchematic WHERE SchemOwner = ? AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); + private static final SQL.Statement checkHistory = new SQL.Statement("SELECT * FROM CheckedSchematic WHERE NodeId IN (SELECT NodeId FROM SchematicNode WHERE NodeOwner = ?) AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' AND NodeId is not NULL ORDER BY EndTime DESC"); + private static final SQL.Statement nodeHistory = new SQL.Statement("SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC"); + private static final SQL.Statement insert = new SQL.Statement("INSERT INTO CheckedSchematic (NodeId, NodeName, NodeOwner, Validator, StartTime, EndTime, DeclineReason) VALUES (?, ?, ?, ?, ?, ?, ?)"); + private static final SQL.Statement setNodeNull = new SQL.Statement("UPDATE CheckedSchematic SET NodeId = NULL WHERE NodeId = ?"); - private final String schemName; - private final int schemOwner; + private final Integer node; private final int validator; private final Timestamp startTime; private final Timestamp endTime; private final String declineReason; - private CheckedSchematic(String schemName, int schemOwner, int validator, Timestamp startTime, Timestamp endTime, String declineReason, boolean insertDB){ - this.schemName = schemName; - this.schemOwner = schemOwner; + private CheckedSchematic(int node, int validator, Timestamp startTime, Timestamp endTime, String declineReason, boolean insertDB){ + this.node = node; this.validator = validator; this.startTime = startTime; this.endTime = endTime; this.declineReason = declineReason; - if(insertDB) + if(insertDB) { insertDB(); + } } @Deprecated public CheckedSchematic(String schemName, int schemOwner, int validator, Timestamp startTime, Timestamp endTime, String declineReason){ - this(schemName, schemOwner, validator, startTime, endTime, declineReason, true); + this(Schematic.getSchemFromDB(schemName, schemOwner).getSchemID(), validator, startTime, endTime, declineReason, true); } @Deprecated public CheckedSchematic(String schemName, UUID schemOwner, UUID validator, Timestamp startTime, Timestamp endTime, String declineReason){ - this(schemName, SteamwarUser.get(schemOwner).getId(), SteamwarUser.get(validator).getId(), startTime, endTime, declineReason, true); + this(Schematic.getSchemFromDB(schemName, schemOwner).getSchemID(), SteamwarUser.get(validator).getId(), startTime, endTime, declineReason, true); } - @Deprecated - private void insertDB(){ - SQL.update("INSERT INTO CheckedSchematic" + - " (SchemName, SchemOwner, Validator, StartTime, EndTime, DeclineReason) VALUES (?, ?, ?, ?, ?, ?)", - schemName, schemOwner, validator, startTime, endTime, declineReason); + public CheckedSchematic(int node, int validator, Timestamp startTime, Timestamp endTime, String declineReason){ + this(node, validator, startTime, endTime, declineReason, true); } - public static List getLastDeclined(UUID schemOwner){ - return getLastDelined(SteamwarUser.get(schemOwner).getId()); + public CheckedSchematic(int node, UUID validator, Timestamp startTime, Timestamp endTime, String declineReason){ + this(node, SteamwarUser.get(validator).getId(), startTime, endTime, declineReason, true); + } + + private void insertDB() { + SchematicNode sNode = SchematicNode.getSchematicNode(node); + String nodeName = sNode.getName(); + int nodeOwner = sNode.getOwner(); + insert.update(node, nodeName, nodeOwner, validator, startTime, endTime, declineReason); + } + + public static List getLastDeclinedOfNode(SchematicNode node){ + return getLastDeclinedOfNode(node.getId()); + } + + public static List getLastDeclinedOfNode(int node){ + return nodeHistory.select(rs -> { + List lastDeclined = new ArrayList<>(); + while(rs.next()){ + int validator = rs.getInt("Validator"); + Timestamp startTime = rs.getTimestamp("StartTime"); + Timestamp endTime = rs.getTimestamp("EndTime"); + String declineReason = rs.getString("DeclineReason"); + lastDeclined.add(new CheckedSchematic(node, validator, startTime, endTime, declineReason, false)); + } + return lastDeclined; + }, node); + } + + public static List getLastDeclined(UUID uuid){ + return getLastDelined(SteamwarUser.get(uuid).getId()); } public static List getLastDelined(int schemOwner){ return checkHistory.select(rs -> { List history = new ArrayList<>(); while(rs.next()) - history.add(new CheckedSchematic(rs.getString("SchemName"), schemOwner, rs.getInt("Validator"), rs.getTimestamp("StartTime"), rs.getTimestamp("EndTime"), rs.getString("DeclineReason"), false)); + history.add(new CheckedSchematic(rs.getInt("NodeId"), rs.getInt("Validator"), rs.getTimestamp("StartTime"), rs.getTimestamp("EndTime"), rs.getString("DeclineReason"), false)); return history; }, schemOwner); } - @Deprecated - public void remove() { - SQL.update("DELETE FROM CheckedSchematic WHERE SchemOwner = ? AND SchemName = ?", schemOwner, schemName); - } - - public String getSchemName() { - return schemName; - } - - public int getSchemOwner() { - return schemOwner; - } - public int getValidator() { return validator; } @@ -105,4 +119,20 @@ public class CheckedSchematic { public String getDeclineReason() { return declineReason; } + + public int getNode() { + return node; + } + + public String getSchemName() { + return SchematicNode.getSchematicNode(node).getName(); + } + + public int getSchemOwner() { + return SchematicNode.getSchematicNode(node).getId(); + } + + public void remove() { + setNodeNull.update(node); + } } diff --git a/SpigotCore_Main/src/de/steamwar/sql/DownloadSchematic.java b/SpigotCore_Main/src/de/steamwar/sql/DownloadSchematic.java index 104f952..64812f9 100644 --- a/SpigotCore_Main/src/de/steamwar/sql/DownloadSchematic.java +++ b/SpigotCore_Main/src/de/steamwar/sql/DownloadSchematic.java @@ -19,30 +19,11 @@ package de.steamwar.sql; -import com.google.common.io.BaseEncoding; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.time.Instant; - public class DownloadSchematic { private DownloadSchematic(){} - private static final SQL.Statement createLink = new SQL.Statement("INSERT INTO SchemDownload (SchemID, Link) VALUES (?, ?) ON DUPLICATE KEY UPDATE Link = VALUES(Link)"); - - private static final String BASE = "https://steamwar.de/download.php?schem="; - + @Deprecated public static String getLink(Schematic schem){ - MessageDigest digest; - try { - digest = MessageDigest.getInstance("SHA-1"); - } catch (NoSuchAlgorithmException e) { - throw new SecurityException(e); - } - digest.reset(); - digest.update((Instant.now().toString() + schem.getSchemOwner() + schem.getSchemID()).getBytes()); - String hash = BaseEncoding.base16().encode(digest.digest()); - createLink.update(schem.getSchemID(), hash); - return BASE + hash; + return NodeDownload.getLink(schem.getNode()); } } diff --git a/SpigotCore_Main/src/de/steamwar/sql/NodeDownload.java b/SpigotCore_Main/src/de/steamwar/sql/NodeDownload.java new file mode 100644 index 0000000..acc7a11 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/sql/NodeDownload.java @@ -0,0 +1,49 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2021 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.sql; + +import com.google.common.io.BaseEncoding; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.Instant; + +public class NodeDownload { + + private static final SQL.Statement createLink = new SQL.Statement("INSERT INTO NodeDownload (NodeId, Link) VALUES (?, ?) ON DUPLICATE KEY UPDATE Link = VALUES(Link)"); + + private static final String BASE = "https://steamwar.de/download.php?schem="; + + public static String getLink(SchematicNode schem){ + if(schem.isDir()) + throw new SecurityException("Can not Download Directorys"); + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new SecurityException(e); + } + digest.reset(); + digest.update((Instant.now().toString() + schem.getOwner() + schem.getId()).getBytes()); + String hash = BaseEncoding.base16().encode(digest.digest()); + createLink.update(schem.getId(), hash); + return BASE + hash; + } +} diff --git a/SpigotCore_Main/src/de/steamwar/sql/NodeMember.java b/SpigotCore_Main/src/de/steamwar/sql/NodeMember.java new file mode 100644 index 0000000..f2f1d90 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/sql/NodeMember.java @@ -0,0 +1,85 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.sql; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashSet; +import java.util.Set; + +public class NodeMember { + + private static final SQL.Statement getNodeMember = new SQL.Statement("SELECT * FROM NodeMember WHERE NodeId = ? AND UserId = ?"); + private static final SQL.Statement getNodeMembers = new SQL.Statement("SELECT * FROM NodeMember WHERE NodeId = ?"); + private static final SQL.Statement getSchematics = new SQL.Statement("SELECT * FROM NodeMember WHERE UserId = ?"); + private static final SQL.Statement createNodeMember = new SQL.Statement("INSERT INTO NodeMember (NodeId, UserId) VALUES (?, ?)"); + private static final SQL.Statement deleteNodeMember = new SQL.Statement("DELETE FROM NodeMember WHERE NodeId = ? AND UserId = ?"); + + public static NodeMember getNodeMember(int node, int member) { + return getNodeMember.select(rs -> { + if(!rs.next()) + return null; + return new NodeMember(rs); + }, node, member); + } + + public static Set getNodeMembers(int node) { + return getNodeMembers.select(rs -> { + Set members = new HashSet<>(); + while (rs.next()) + members.add(new NodeMember(rs)); + return members; + }, node); + } + + public static Set getSchematics(int member) { + return getSchematics.select(rs -> { + Set members = new HashSet<>(); + while (rs.next()) + members.add(new NodeMember(rs)); + return members; + }, member); + } + + public static NodeMember createNodeMember(int node, int member) { + createNodeMember.update(node, member); + return getNodeMember(node, member); + } + + final int node; + final int member; + + private NodeMember(ResultSet set) throws SQLException { + node = set.getInt("NodeId"); + member = set.getInt("UserId"); + } + + public int getNode() { + return node; + } + + public int getMember() { + return member; + } + + public void delete() { + deleteNodeMember.update(node, member); + } +} diff --git a/SpigotCore_Main/src/de/steamwar/sql/SQL.java b/SpigotCore_Main/src/de/steamwar/sql/SQL.java index 74e68b9..fc8c7a2 100644 --- a/SpigotCore_Main/src/de/steamwar/sql/SQL.java +++ b/SpigotCore_Main/src/de/steamwar/sql/SQL.java @@ -19,6 +19,7 @@ package de.steamwar.sql; +import com.mysql.jdbc.exceptions.jdbc4.CommunicationsException; import de.steamwar.core.Core; import org.bukkit.configuration.file.YamlConfiguration; @@ -33,27 +34,27 @@ public class SQL { private SQL(){} private static Connection con; - private static String url; - private static String user; - private static String password; + private static final String URL; + private static final String USER; + private static final String PASSWORD; - static{ + static { File file = new File(Core.getInstance().getDataFolder(), "MySQL.yml"); YamlConfiguration config = YamlConfiguration.loadConfiguration(file); if(!file.exists()) throw new SecurityException("SQL-ConfigFile not found!"); - url = "jdbc:mysql://" + config.getString("HOST") + ":" + config.getString("PORT") + "/" + config.getString("DATABASE"); - user = config.getString("USER"); - password = config.getString("PASSWORD"); + URL = "jdbc:mysql://" + config.getString("HOST") + ":" + config.getString("PORT") + "/" + config.getString("DATABASE"); + USER = config.getString("USER"); + PASSWORD = config.getString("PASSWORD"); connect(); } private static void connect() { try { - con = DriverManager.getConnection(url + "?autoReconnect=true&useServerPrepStmts=true", user, password); + con = DriverManager.getConnection(URL + "?useServerPrepStmts=true", USER, PASSWORD); } catch (SQLException e) { throw new SecurityException("Could not start SQL connection", e); } @@ -95,7 +96,8 @@ public class SQL { return false; } } - + + @Deprecated static void update(String qry, Object... objects) { try { prepare(qry, objects).executeUpdate(); @@ -105,7 +107,8 @@ public class SQL { } } - static ResultSet select(String qry, Object... objects){ + @Deprecated + static ResultSet select(String qry, Object... objects) { try { return prepare(qry, objects).executeQuery(); } catch (SQLException e) { @@ -114,7 +117,8 @@ public class SQL { } } - static Blob blob(){ + @Deprecated + static Blob blob() { try { return con.createBlob(); } catch (SQLException e) { @@ -123,7 +127,8 @@ public class SQL { } } - private static PreparedStatement prepare(String qry, Object... objects) throws SQLException{ + @Deprecated + private static PreparedStatement prepare(String qry, Object... objects) throws SQLException { PreparedStatement st = con.prepareStatement(qry); for(int i = 0; i < objects.length; i++){ st.setObject(i+1, objects[i]); @@ -166,10 +171,15 @@ public class SQL { private synchronized T prepare(SQLRunnable runnable, Object... objects) { try { - setObjects(objects); - return runnable.run(); + try { + setObjects(objects); + return runnable.run(); + } catch (CommunicationsException e) { + reset(); + setObjects(objects); + return runnable.run(); + } } catch (SQLException e) { - reset(); throw new SecurityException("Could not execute SQL statement", e); } } diff --git a/SpigotCore_Main/src/de/steamwar/sql/Schematic.java b/SpigotCore_Main/src/de/steamwar/sql/Schematic.java index b364072..ac3cd46 100644 --- a/SpigotCore_Main/src/de/steamwar/sql/Schematic.java +++ b/SpigotCore_Main/src/de/steamwar/sql/Schematic.java @@ -1,6 +1,6 @@ -/* +/* This file is a part of the SteamWar software. - + Copyright (C) 2020 SteamWar.de-Serverteam This program is free software: you can redistribute it and/or modify @@ -26,232 +26,179 @@ import org.bukkit.entity.Player; import java.io.IOException; import java.io.InputStream; -import java.sql.Blob; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; -import java.util.zip.GZIPInputStream; +import java.util.stream.Collectors; +@Deprecated public class Schematic { - private static final SQL.Statement getSchemsOfType = new SQL.Statement("SELECT DISTINCT s.SchemID, s.SchemName, s.SchemOwner, s.Item, s.SchemType, s.Rank, s.SchemFormat FROM Schematic s LEFT JOIN SchemMember sm ON sm.SchemName = s.SchemName AND sm.SchemOwner = s.SchemOwner WHERE s.SchemType = ? AND (s.SchemOwner = ? OR sm.Member = ?) ORDER BY s.SchemName"); + private static final SQL.Statement getSchemsOfType = new SQL.Statement("SELECT DISTINCT s.SchemID, s.SchemName, s.SchemOwner, s.Item, s.SchemType, s.Rank, s.SchemFormat FROM Schematic s LEFT JOIN SchemMember sm ON sm.SchemName = s.SchemName AND sm.SchemOwner = s.SchemOwner WHERE s.SchemType = ? AND (s.SchemOwner = ? OR sm.Member = ?) ORDER BY s.SchemName"); - private final int schemID; - private final String schemName; - private final int schemOwner; - private boolean schemFormat; - private String item; - private int rank; - private SchematicType schemType; + private final SchematicNode node; - private Schematic(ResultSet rs) throws SQLException { - this.schemID = rs.getInt("SchemID"); - this.schemName = rs.getString("SchemName"); - this.schemOwner = rs.getInt("SchemOwner"); - this.item = rs.getString("Item"); - this.rank = rs.getInt("Rank"); - this.schemType = SchematicType.fromDB(rs.getString("SchemType")); - this.schemFormat = rs.getBoolean("SchemFormat"); - } + private Schematic(SchematicNode node) { + this.node = node; + } - private void updateDB(){ - createSchem(schemName, schemOwner, item, schemType); - } + public static Schematic wrap(SchematicNode node) { + return new Schematic(node); + } - public static void createSchem(String schemName, UUID schemOwner, String item, SchematicType schemType){ - createSchem(schemName, SteamwarUser.get(schemOwner).getId(), item, schemType); - } + public static void createSchem(String schemName, UUID schemOwner, String item, SchematicType schemType){ + createSchem(schemName, SteamwarUser.get(schemOwner).getId(), item, schemType); + } - public static void createSchem(String schemName, int schemOwner, String item, SchematicType schemType){ - SQL.update("INSERT INTO Schematic (SchemName, SchemOwner, Item, SchemType) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE Item = VALUES(Item), SchemType = VALUES(SchemType)", - schemName, schemOwner, item, schemType.toDB()); - } + public static void createSchem(String schemName, int schemOwner, String item, SchematicType schemType){ + SchematicNode.createSchematicNode(schemOwner, schemName, null, schemType.toDB(), item); + } - public static Schematic getSchemFromDB(String schemName, UUID schemOwner){ - return getSchemFromDB(schemName, SteamwarUser.get(schemOwner).getId()); - } + public static Schematic getSchemFromDB(String schemName, UUID schemOwner){ + return getSchemFromDB(schemName, SteamwarUser.get(schemOwner).getId()); + } - public static Schematic getSchemFromDB(String schemName, int schemOwner){ - ResultSet schematic = SQL.select("SELECT SchemID, SchemName, SchemOwner, Item, SchemType, Rank, SchemFormat FROM Schematic WHERE SchemName = ? AND SchemOwner = ?", schemName, schemOwner); - try { - if(schematic == null || !schematic.next()){ - SchematicMember member = SchematicMember.getMemberBySchematic(schemName, schemOwner); - if(member == null){ - return null; - } - return getSchemFromDB(schemName, member.getSchemOwner()); - } - return new Schematic(schematic); - } catch (SQLException e) { - throw new SecurityException("Failed loading schematic", e); - } - } + public static Schematic getSchemFromDB(String schemName, int schemOwner){ + SchematicNode node = SchematicNode.getSchematicNode(schemOwner, schemName, 0); + if(node != null) { + return new Schematic(node); + } else { + Optional n = SchematicNode.getSchematicsAccessibleByUser(schemOwner, 0).stream().filter(node1 -> node1.getName().equals(schemName)).findAny(); + if(n.isPresent()) { + return new Schematic(n.get()); + } + } + return null; + } - public static Schematic getSchemFromDB(int schemID){ - ResultSet schematic = SQL.select("SELECT SchemID, SchemName, SchemOwner, Item, SchemType, Rank, SchemFormat FROM Schematic WHERE SchemID = ?", schemID); - try { - if(!schematic.next()) - throw new SecurityException("Failed loading schematic " + schemID); - return new Schematic(schematic); - } catch (SQLException e) { - throw new SecurityException("Failed loading schematic", e); - } - } + public static Schematic getSchemFromDB(int schemID){ + SchematicNode node = SchematicNode.getSchematicNode(schemID); + if(node != null) { + return new Schematic(node); + } else { + throw new SecurityException("Failed to load Schematics"); + } + } - public static List getSchemsAccessibleByUser(UUID schemOwner){ - return getSchemsAccessibleByUser(SteamwarUser.get(schemOwner).getId()); - } + public static List getSchemsAccessibleByUser(UUID schemOwner){ + return getSchemsAccessibleByUser(SteamwarUser.get(schemOwner).getId()); + } - public static List getSchemsAccessibleByUser(int schemOwner){ - try{ - ResultSet schematic = SQL.select("SELECT DISTINCT s.SchemID, s.SchemName, s.SchemOwner, s.Item, s.SchemType, s.Rank, s.SchemFormat FROM Schematic s LEFT JOIN SchemMember sm ON sm.SchemName = s.SchemName AND sm.SchemOwner = s.SchemOwner WHERE s.SchemOwner = ? OR sm.Member = ? ORDER BY s.SchemName", schemOwner, schemOwner); - List schematics = new ArrayList<>(); - while(schematic.next()) - schematics.add(new Schematic(schematic)); - return schematics; - }catch(SQLException e){ - throw new SecurityException("Failed listing schematics", e); - } - } + public static List getSchemsAccessibleByUser(int schemOwner){ + List schematics = new ArrayList<>(); + SchematicNode.getSchematicsAccessibleByUser(schemOwner, null) + .forEach(node1 -> { + if (!node1.isDir()) schematics.add(new Schematic(node1)); + }); + return schematics; + } - public static List getSchemsOfType(UUID schemOwner, SchematicType schemType){ - return getSchemsOfType(SteamwarUser.get(schemOwner).getId(), schemType); - } + public static List getSchemsOfType(UUID schemOwner, SchematicType schemType){ + return getSchemsOfType(SteamwarUser.get(schemOwner).getId(), schemType); + } - public static List getSchemsOfType(int schemOwner, SchematicType schemType){ - return getSchemsOfType.select(rs -> { - List schematics = new ArrayList<>(); - while(rs.next()) - schematics.add(new Schematic(rs)); - return schematics; - }, schemType.toDB(), schemOwner, schemOwner); - } + public static List getSchemsOfType(int schemOwner, SchematicType schemType){ + List schematics = new ArrayList<>(); + SchematicNode.getAllAccessibleSchematicsOfType(schemOwner, schemType.toDB()) + .forEach(node1 -> { + if (!node1.isDir()) schematics.add(new Schematic(node1)); + }); + return schematics; + } - public static List getAllSchemsOfType(SchematicType schemType){ - try{ - ResultSet schematic = SQL.select("SELECT SchemID, SchemName, SchemOwner, Item, SchemType, Rank, SchemFormat FROM Schematic WHERE SchemType = ?", schemType.toDB()); - List schematics = new ArrayList<>(); - while(schematic.next()){ - schematics.add(new Schematic(schematic)); - } - return schematics; - }catch(SQLException e){ - throw new SecurityException("Failed loading all schems of type", e); - } - } + public static List getAllSchemsOfType(SchematicType schemType){ + List schematics = new ArrayList<>(); + SchematicNode.getAllSchematicsOfType(schemType.toDB()) + .forEach(node1 -> schematics.add(new Schematic(node1))); + return schematics; + } - public int getSchemID() { - return schemID; - } + public int getSchemID() { + return node.getId(); + } - public String getSchemName() { - return schemName; - } + public String getSchemName() { + return node.getName(); + } - public int getSchemOwner() { - return schemOwner; - } + public int getSchemOwner() { + return node.getOwner(); + } - public int getRank(){ - return rank; - } + public int getRank(){ + return node.getRank(); + } - public String getItem() { - return item; - } + public String getItem() { + return node.getItem(); + } - public void setItem(String item) { - this.item = item; - updateDB(); - } + public void setItem(String item) { + node.setItem(item); + } - public void setRank(int rank){ - this.rank = rank; - SQL.update("UPDATE Schematic SET Rank = ? WHERE SchemID = ?", rank, schemID); - } + public void setRank(int rank){ + node.setRank(rank); + } - public SchematicType getSchemType() { - return schemType; - } + public SchematicType getSchemType() { + return node.getSchemtype(); + } - public void setSchemType(SchematicType schemType) { - this.schemType = schemType; - updateDB(); - } + public void setSchemType(SchematicType schemType) { + node.setType(schemType.toDB()); + } - public boolean availible(){ - return true; - } + public boolean availible(){ + return true; + } - public InputStream schemData() throws IOException { - ResultSet rs = SQL.select("SELECT SchemData FROM Schematic WHERE SchemID = ?", schemID); - try { - rs.next(); - Blob schemData = rs.getBlob("SchemData"); - if(schemData == null) - throw new IOException("SchemData is null"); - return new GZIPInputStream(schemData.getBinaryStream()); - } catch (SQLException e) { - throw new IOException(e); - } - } + public InputStream schemData() throws IOException { + return node.schemData(); + } - public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) { - try { - return WorldEditWrapper.impl.getClipboard(is, schemFormat); - } catch (IOException e) { - throw new SecurityException("Could not read schem", e); - } - } + public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) { + try { + return WorldEditWrapper.impl.getClipboard(is, schemFormat); + } catch (IOException e) { + throw new SecurityException("Could not read schem", e); + } + } - public Clipboard load() throws IOException, NoClipboardException { - return clipboardFromStream(schemData(), schemFormat); - } + public Clipboard load() throws IOException, NoClipboardException { + return clipboardFromStream(schemData(), node.getSchemFormat()); + } - public void loadToPlayer(Player player) throws IOException, NoClipboardException { - InputStream is = schemData(); - WorldEditWrapper.impl.setPlayerClipboard(player, is, schemFormat); - } + public void loadToPlayer(Player player) throws IOException, NoClipboardException { + InputStream is = schemData(); + WorldEditWrapper.impl.setPlayerClipboard(player, is, node.getSchemFormat()); + } - public void saveOldFormatFromPlayer(Player player) throws IOException, NoClipboardException { - saveFromPlayer(player, false); - } + public void saveOldFormatFromPlayer(Player player) throws IOException, NoClipboardException { + saveFromPlayer(player, false); + } - public void saveFromPlayer(Player player) throws IOException, NoClipboardException { - saveFromPlayer(player, Core.getVersion() > 12); - } + public void saveFromPlayer(Player player) throws IOException, NoClipboardException { + saveFromPlayer(player, Core.getVersion() > 12); + } - public void saveFromBytes(byte[] bytes, boolean newFormat) { - Blob blob = SQL.blob(); - try { - blob.setBytes(1, bytes); - updateDatabase(blob, newFormat); - } catch (SQLException e) { - throw new SecurityException(e); - } - } + public void saveFromBytes(byte[] bytes, boolean newFormat) { + node.saveFromBytes(bytes, newFormat); + } - private void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException { - Blob blob = SQL.blob(); - try { - blob.setBytes(1, WorldEditWrapper.impl.getPlayerClipboard(player, newFormat)); - } catch (SQLException e) { - throw new SecurityException(e.getMessage(), e); - } - updateDatabase(blob, newFormat); - } + private void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException { + node.saveFromPlayer(player, newFormat); + } - private void updateDatabase(Blob blob, boolean newFormat) { - SQL.update("UPDATE Schematic SET SchemData = ?, SchemFormat = ? WHERE SchemID = ?", blob, newFormat, schemID); - schemFormat = newFormat; - } + public void remove(){ + node.delete(); + } - public void remove(){ - SQL.update("DELETE FROM SchemMember WHERE SchemOwner = ? AND SchemName = ?", schemOwner, schemName); - SQL.update("DELETE FROM Schematic WHERE SchemOwner = ? AND SchemName = ?", schemOwner, schemName); - } + public SchematicNode getNode() { + return node; + } - public static class WrongVersionException extends Exception{} + @Deprecated + public static class WrongVersionException extends Exception{} } diff --git a/SpigotCore_Main/src/de/steamwar/sql/SchematicMember.java b/SpigotCore_Main/src/de/steamwar/sql/SchematicMember.java index 87bf2fb..e84fa54 100644 --- a/SpigotCore_Main/src/de/steamwar/sql/SchematicMember.java +++ b/SpigotCore_Main/src/de/steamwar/sql/SchematicMember.java @@ -1,134 +1,96 @@ -/* +/* This file is a part of the SteamWar software. - - Copyright (C) 2020 SteamWar.de-Serverteam + Copyright (C) 2020 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.sql; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.UUID; +@Deprecated public class SchematicMember { - private final int schemOwner; - private final String schemName; - private final int member; - private SchematicMember(String schemName, int schemOwner, int schemMember, boolean updateDB){ - this.schemOwner = schemOwner; - member = schemMember; - this.schemName = schemName; - if(updateDB) - updateDB(); - } + private final NodeMember member; - public SchematicMember(String schemName, int schemOwner, int schemMember){ - this(schemName, schemOwner, schemMember, true); - } + private SchematicMember(NodeMember member){ + this.member = member; + } - public SchematicMember(String schemName, UUID schemOwner, UUID schemMember){ - this(schemName, SteamwarUser.get(schemOwner).getId(), SteamwarUser.get(schemMember).getId(), true); - } + public SchematicMember(String schemName, int schemOwner, int schemMember){ + this(NodeMember.createNodeMember(Schematic.getSchemFromDB(schemName, schemOwner).getSchemID(), schemMember)); + } - private void updateDB(){ - SQL.update("INSERT INTO SchemMember (SchemName, SchemOwner, Member) VALUES (?, ?, ?)", schemName, schemOwner, member); - } + public SchematicMember(String schemName, UUID schemOwner, UUID schemMember){ + this(NodeMember.createNodeMember(Schematic.getSchemFromDB(schemName, schemOwner).getSchemID(), SteamwarUser.get(schemMember).getId())); + } - public static SchematicMember getSchemMemberFromDB(String schemName, UUID schemOwner, UUID schemMember){ - return getSchemMemberFromDB(schemName, SteamwarUser.get(schemOwner).getId(), SteamwarUser.get(schemMember).getId()); - } + public static SchematicMember getSchemMemberFromDB(String schemName, UUID schemOwner, UUID schemMember){ + return getSchemMemberFromDB(schemName, SteamwarUser.get(schemOwner).getId(), SteamwarUser.get(schemMember).getId()); + } - public static SchematicMember getSchemMemberFromDB(String schemName, int schemOwner, int schemMember){ - ResultSet schematicMember = SQL.select("SELECT * FROM SchemMember WHERE SchemName = ? AND SchemOwner = ? AND Member = ?", schemName, schemOwner, schemMember); - try { - if(schematicMember == null || !schematicMember.next()){ - return null; - } - return new SchematicMember(schemName, schemOwner, schemMember, false); - } catch (SQLException e) { - throw new SecurityException("Could not get schemmember", e); - } - } + public static SchematicMember getSchemMemberFromDB(String schemName, int schemOwner, int schemMember){ + NodeMember member = NodeMember.getNodeMember(Schematic.getSchemFromDB(schemName, schemOwner).getSchemID(), schemMember); + if(member == null) { + return null; + } + return new SchematicMember(member); + } - public static SchematicMember getMemberBySchematic(String schemName, int schemMember){ - ResultSet schematicMember = SQL.select("SELECT * FROM SchemMember WHERE SchemName = ? AND Member = ?", schemName, schemMember); - try { - if(schematicMember == null || !schematicMember.next()){ - return null; - } - int schemOwner = schematicMember.getInt("SchemOwner"); - return new SchematicMember(schemName, schemOwner, schemMember, false); - } catch (SQLException e) { - throw new SecurityException("Could not get member", e); - } - } + public static SchematicMember getMemberBySchematic(String schemName, int schemMember){ + Optional nodeMember = NodeMember.getSchematics(schemMember) + .stream().filter(member1 -> SchematicNode.getSchematicNode(member1.getNode()).getName().equalsIgnoreCase(schemName)).findFirst(); + return nodeMember.map(SchematicMember::new).orElse(null); + } - public static List getSchemMembers(String schemName, UUID schemOwner){ - return getSchemMembers(schemName, SteamwarUser.get(schemOwner).getId()); - } + public static List getSchemMembers(String schemName, UUID schemOwner){ + return getSchemMembers(schemName, SteamwarUser.get(schemOwner).getId()); + } - public static List getSchemMembers(String schemName, int schemOwner){ - ResultSet schematicMember = SQL.select("SELECT * FROM SchemMember WHERE SchemName = ? AND SchemOwner = ?", schemName, schemOwner); - try { - List schematicMembers = new ArrayList<>(); - while(schematicMember.next()){ - int schemMember = schematicMember.getInt("Member"); - schematicMembers.add(new SchematicMember(schemName, schemOwner, schemMember, false)); - } - return schematicMembers; - } catch (SQLException e) { - throw new SecurityException("Could not get schemmembers", e); - } - } + public static List getSchemMembers(String schemName, int schemOwner){ + List members = new ArrayList<>(); + NodeMember.getNodeMembers(Schematic.getSchemFromDB(schemName, schemOwner).getSchemID()) + .forEach(member1 -> members.add(new SchematicMember(member1))); + return members; + } - public static List getAccessibleSchems(UUID schemMember){ - return getAccessibleSchems(SteamwarUser.get(schemMember).getId()); - } + public static List getAccessibleSchems(UUID schemMember){ + return getAccessibleSchems(SteamwarUser.get(schemMember).getId()); + } - public static List getAccessibleSchems(int schemMember){ - ResultSet schematicMember = SQL.select("SELECT * FROM SchemMember WHERE Member = ?", schemMember); - try { - List schematicMembers = new ArrayList<>(); - while(schematicMember.next()){ - String schemName = schematicMember.getString("SchemName"); - int schemOwner = schematicMember.getInt("SchemOwner"); - schematicMembers.add(new SchematicMember(schemName, schemOwner, schemMember, false)); - } - return schematicMembers; - } catch (SQLException e) { - throw new SecurityException("Could not get accessible schems", e); - } - } + public static List getAccessibleSchems(int schemMember){ + List members = new ArrayList<>(); + NodeMember.getSchematics(schemMember) + .forEach(member1 -> members.add(new SchematicMember(member1))); + return members; + } - public int getSchemOwner() { - return schemOwner; - } + public int getSchemOwner() { + return SchematicNode.getSchematicNode(member.getNode()).getOwner(); + } - public String getSchemName() { - return schemName; - } + public String getSchemName() { + return SchematicNode.getSchematicNode(member.getNode()).getName(); + } - public int getMember() { - return member; - } + public int getMember() { + return member.getMember(); + } - public void remove(){ - SQL.update("DELETE FROM SchemMember WHERE SchemOwner = ? AND SchemName = ? AND Member = ?", schemOwner, schemName, member); - } + public void remove(){ + member.delete(); + } } diff --git a/SpigotCore_Main/src/de/steamwar/sql/SchematicNode.java b/SpigotCore_Main/src/de/steamwar/sql/SchematicNode.java new file mode 100644 index 0000000..faea608 --- /dev/null +++ b/SpigotCore_Main/src/de/steamwar/sql/SchematicNode.java @@ -0,0 +1,605 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2020 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.sql; + +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import de.steamwar.core.Core; +import de.steamwar.core.WorldEditWrapper; +import de.steamwar.inventory.SWItem; +import org.bukkit.entity.Player; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.sql.Blob; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.*; +import java.util.function.Predicate; +import java.util.zip.GZIPInputStream; + +public class SchematicNode { + + private static final SQL.Statement createNode = new SQL.Statement("INSERT INTO SchematicNode (NodeName, NodeOwner, ParentNode, NodeType, NodeItem) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE NodeName = VALUES(NodeName), ParentNode = VALUES(ParentNode), NodeItem = VALUES(NodeItem), NodeType = VALUES(NodeType), NodeItem = VALUES(NodeItem)"); + private static final SQL.Statement getSchematicNode_Null = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeOwner = ? AND NodeName = ? AND ParentNode is NULL"); + private static final SQL.Statement getSchematicNode = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeOwner = ? AND NodeName = ? AND ParentNode = ?"); + private static final SQL.Statement getSchematicsInNode_Null = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE ParentNode is NULL ORDER BY NodeName"); + private static final SQL.Statement getSchematicsInNode = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE ParentNode = ? ORDER BY NodeName"); + private static final SQL.Statement getDirsInNode_Null = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE ParentNode is NULL AND NodeType is NULL ORDER BY NodeName"); + private static final SQL.Statement getDirsInNode = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE ParentNode = ? AND NodeType is NULL ORDER BY NodeName"); + private static final SQL.Statement getSchematicDirectory_Null = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeName = ? AND ParentNode is NULL ORDER BY NodeName"); + private static final SQL.Statement getSchematicDirectory = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeName = ? AND ParentNode = ? ORDER BY NodeName"); + private static final SQL.Statement getSchematicNodeO_Null = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeName = ? AND ParentNode is NULL "); + private static final SQL.Statement getSchematicNodeO = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeName = ? AND ParentNode = ? ORDER BY NodeName"); + private static final SQL.Statement getSchematicNodeId = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeId = ?"); + private static final SQL.Statement getAllSchemsOfTypeOwner = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName"); + private static final SQL.Statement getAllSchemsOfType = new SQL.Statement("SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeType = ? ORDER BY NodeName"); + private static final SQL.Statement getAccessibleByUser = new SQL.Statement("SELECT s.NodeId, s.NodeName, s.NodeOwner, s.NodeItem, s.NodeType, s.ParentNode, s.NodeRank, s.NodeFormat, s.LastUpdate FROM SchematicNode s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) AND ((s.NodeOwner = ? AND s.ParentNode IS NULL) OR NOT s.NodeOwner = ?) GROUP BY s.NodeId ORDER BY s.NodeName"); + private static final SQL.Statement getAccessibleByUserByTypeInNode = new SQL.Statement("WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (SELECT s.NodeId, s.NodeName, s.NodeOwner, s.NodeItem, s.NodeType, s.ParentNode, s.NodeRank, s.NodeFormat, s.LastUpdate FROM SchematicNode s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.NodeItem, SN.NodeType, SN.ParentNode, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.NodeItem, SN.NodeType, SN.ParentNode, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode = ? ORDER BY NodeName"); + private static final SQL.Statement getAccessibleByUserByTypeInNode_Null = new SQL.Statement("WITH RECURSIVE RSNB AS (WITH RECURSIVE RSN as (SELECT s.NodeId, s.NodeName, s.NodeOwner, s.NodeItem, s.NodeType, s.ParentNode, s.NodeRank, s.NodeFormat, s.LastUpdate FROM SchematicNode s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.NodeItem, SN.NodeType, SN.ParentNode, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.NodeItem, SN.NodeType, SN.ParentNode, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode AS SN, RSNB WHERE SN.NodeId = RSNB.ParentNode)SELECT * FROM RSNB WHERE ParentNode is null ORDER BY NodeName"); + private static final SQL.Statement getAccessibleByUserByType = new SQL.Statement("WITH RECURSIVE RSN as (SELECT s.NodeId, s.NodeName, s.NodeOwner, s.NodeItem, s.NodeType, s.ParentNode, s.NodeRank, s.NodeFormat, s.LastUpdate FROM SchematicNode s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.NodeItem, SN.NodeType, SN.ParentNode, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN WHERE NodeType = ? ORDER BY NodeName"); + private static final SQL.Statement getAllSchematicsAccessibleByUser = new SQL.Statement("WITH RECURSIVE RSN as (SELECT s.NodeId, s.NodeName, s.NodeOwner, s.NodeItem, s.NodeType, s.ParentNode, s.NodeRank, s.NodeFormat, s.LastUpdate FROM SchematicNode s LEFT JOIN NodeMember n ON s.NodeId = n.NodeId WHERE (s.NodeOwner = ? OR n.UserId = ?) GROUP BY s.NodeId union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.NodeItem, SN.NodeType, SN.ParentNode, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode AS SN, RSN WHERE SN.ParentNode = RSN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + private static final SQL.Statement isSchematicAccessibleForUser = new SQL.Statement("WITH RECURSIVE RSN AS (SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeId = ? union select SN.NodeId, SN.NodeName, SN.NodeOwner, SN.ParentNode, SN.NodeType, SN.NodeItem, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT COUNT(RSN.NodeId) AS `Accessible` FROM RSN LEFT Join NodeMember NM On NM.NodeId = RSN.NodeId WHERE NodeOwner = ? OR UserId = ? LIMIT 1"); + private static final SQL.Statement getAllParentsOfNode = new SQL.Statement("WITH RECURSIVE RSN AS (SELECT NodeId, NodeName, NodeOwner, ParentNode, NodeType, NodeItem, NodeRank, NodeFormat, LastUpdate FROM SchematicNode WHERE NodeId = ? UNION SELECT SN.NodeId, SN.NodeName, SN.NodeOwner, SN.ParentNode, SN.NodeType, SN.NodeItem, SN.NodeRank, SN.NodeFormat, SN.LastUpdate FROM SchematicNode SN, RSN WHERE RSN.ParentNode = SN.NodeId) SELECT * FROM RSN ORDER BY NodeName"); + private static final SQL.Statement countNodes = new SQL.Statement("SELECT COUNT(NodeId) AS 'count' FROM SchematicNode"); + private static final SQL.Statement updateDB = new SQL.Statement("UPDATE SchematicNode SET NodeName = ?, NodeOwner = ?, ParentNode = ?, NodeItem = ?, NodeType = ?, NodeRank = ? WHERE NodeId = ?"); + private static final SQL.Statement updateDatabase = new SQL.Statement("UPDATE SchematicNode SET NodeData = ?, NodeFormat = ? WHERE NodeId = ?"); + private static final SQL.Statement selSchemData = new SQL.Statement("SELECT NodeData FROM SchematicNode WHERE NodeId = ?"); + private static final SQL.Statement deleteNode = new SQL.Statement("DELETE FROM SchematicNode WHERE NodeId = ?"); + + public static SchematicNode createSchematic(int owner, String name, Integer parent) { + return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), ""); + } + + public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) { + return createSchematicNode(owner, name, parent, null, ""); + } + + public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) { + if (parent != null && parent == 0) + parent = null; + createNode.update(name, owner, parent, type, item); + return getSchematicNode(owner, name, parent); + } + + public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) { + return getSchematicNode(owner, name, parent.getId()); + } + + private SchematicNode(ResultSet set) throws SQLException { + id = set.getInt("NodeId"); + owner = set.getInt("NodeOwner"); + name = set.getString("NodeName"); + parent = set.getInt("ParentNode"); + if(set.wasNull()) { + parent = null; + } + item = set.getString("NodeItem"); + type = set.getString("NodeType"); + lastUpdate = set.getTimestamp("LastUpdate"); + if (type != null) { + isDir = false; + rank = set.getInt("NodeRank"); + schemFormat = set.getBoolean("NodeFormat"); + } else { + isDir = true; + } + } + + public static List getSchematicNodeInNode(SchematicNode parent) { + return getSchematicNodeInNode(parent.getId()); + } + + public static SchematicNode getSchematicDirectory(String name, SchematicNode parent) { + return getSchematicDirectory(name, parent.getId()); + } + + public static SchematicNode getSchematicNode(int owner, String name, Integer parent) { + if (parent != null && parent == 0) { + parent = null; + } + SQL.Statement.ResultSetUser user = rs -> { + if (rs.next()) { + return new SchematicNode(rs); + } + return null; + }; + if(parent == null) { + return getSchematicNode_Null.select(user, owner, name); + } else { + return getSchematicNode.select(user, owner, name, parent); + } + } + + public static List getSchematicNodeInNode(Integer parent) { + if(parent != null && parent == 0) + parent = null; + SQL.Statement.ResultSetUser> user = rs -> { + List nodes = new ArrayList<>(); + while (rs.next()) { + nodes.add(new SchematicNode(rs)); + } + return nodes; + }; + if(parent == null) { + return getSchematicsInNode_Null.select(user); + }else { + return getSchematicsInNode.select(user, parent); + } + } + + public static List getSchematicDirectoryInNode(Integer parent) { + if(parent != null && parent == 0) + parent = null; + SQL.Statement.ResultSetUser> user = rs -> { + List nodes = new ArrayList<>(); + while (rs.next()) + nodes.add(new SchematicNode(rs)); + return nodes; + }; + if(parent == null) { + return getDirsInNode_Null.select(user); + }else { + return getDirsInNode.select(user, parent); + } + } + + public static SchematicNode getSchematicDirectory(String name, Integer parent) { + if(parent != null && parent == 0) + parent = null; + SQL.Statement.ResultSetUser user = rs -> { + while (rs.next()) { + SchematicNode node = new SchematicNode(rs); + if(node.isDir()) + return node; + } + return null; + }; + + if(parent == null) { + return getSchematicDirectory_Null.select(user, name); + }else { + return getSchematicDirectory.select(user, name, parent); + } + } + + public static SchematicNode getSchematicNode(String name, Integer parent) { + if(parent != null && parent == 0) + parent = null; + SQL.Statement.ResultSetUser user = rs -> { + while (rs.next()) { + return new SchematicNode(rs); + } + return null; + }; + if(parent == null) { + return getSchematicNodeO_Null.select(user, name); + }else { + return getSchematicNodeO.select(user, name, parent); + } + } + + public static SchematicNode getSchematicNode(int id) { + return getSchematicNodeId.select(rs -> { + if (!rs.next()) + return null; + return new SchematicNode(rs); + }, id); + } + + public static List getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) { + SQL.Statement.ResultSetUser> user = rs -> { + List nodes = new ArrayList<>(); + while (rs.next()) { + nodes.add(new SchematicNode(rs)); + } + return nodes; + }; + if(parent == null || parent == 0) { + return getAccessibleByUserByTypeInNode_Null.select(user, owner, owner, schemType); + } else { + return getAccessibleByUserByTypeInNode.select(user, owner, owner, schemType, parent); + } + } + + public static List getAllAccessibleSchematicsOfType(int user, String schemType) { + return getAccessibleByUserByType.select(rs -> { + List nodes = new ArrayList<>(); + while (rs.next()) { + nodes.add(new SchematicNode(rs)); + } + return nodes; + }, user, user, schemType); + } + + public static List getAllSchematicsOfType(int owner, String schemType) { + return getAllSchemsOfTypeOwner.select(rs -> { + List nodes = new ArrayList<>(); + while (rs.next()) + nodes.add(new SchematicNode(rs)); + return nodes; + }, owner, schemType); + } + + public static List getAllSchematicsOfType(String schemType) { + return getAllSchemsOfType.select(rs -> { + List nodes = new ArrayList<>(); + while (rs.next()) + nodes.add(new SchematicNode(rs)); + return nodes; + }, schemType); + } + + public static List deepGet(Integer parent, Predicate filter) { + List finalList = new ArrayList<>(); + List nodes = SchematicNode.getSchematicNodeInNode(parent); + nodes.forEach(node -> { + if (node.isDir()) { + finalList.addAll(deepGet(node.getId(), filter)); + } else { + if (filter.test(node)) + finalList.add(node); + } + }); + return finalList; + } + + public static List getSchematicsAccessibleByUser(int user, Integer parent) { + if (parent != null && parent != 0) { + if(Boolean.TRUE.equals(isSchematicAccessibleForUser.select(rs -> { + rs.next(); + return rs.getInt("Accessible") > 0; + }, parent, user, user))) { + return getSchematicNodeInNode(parent); + } + } else { + return getAccessibleByUser.select(rs -> { + List nodes = new ArrayList<>(); + while(rs.next()) + nodes.add(new SchematicNode(rs)); + return nodes; + }, user, user, user, user); + } + return Collections.emptyList(); + } + + public static List getAllSchematicsAccessibleByUser(int user) { + return getAllSchematicsAccessibleByUser.select(rs -> { + List nodes = new ArrayList<>(); + while(rs.next()) { + nodes.add(new SchematicNode(rs)); + } + return nodes; + }, user, user); + } + + public static List getAllParentsOfNode(SchematicNode node) { + return getAllParentsOfNode(node.getId()); + } + + public static List getAllParentsOfNode(int node) { + return getAllParentsOfNode.select(rs -> { + List nodes = new ArrayList<>(); + while(rs.next()) { + nodes.add(new SchematicNode(rs)); + } + return nodes; + }, node); + } + + public static SchematicNode getNodeFromPath(SteamwarUser user, String s) { + if (s.startsWith("/")) { + s = s.substring(1); + } + if (s.isEmpty()) { + return null; + } + if (s.contains("/")) { + String[] layers = s.split("/"); + SchematicNode currentNode = null; + for (int i = 0; i < layers.length; i++) { + int finalI = i; + Optional node; + if (currentNode == null) { + node = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(layers[finalI])).findAny(); + } else { + node = Optional.ofNullable(SchematicNode.getSchematicNode(layers[i], currentNode.getId())); + } + if (!node.isPresent()) { + return null; + } else { + currentNode = node.get(); + if (!currentNode.isDir() && i != layers.length - 1) { + return null; + } + } + } + return currentNode; + } else { + String finalS = s; + return SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0).stream().filter(node1 -> node1.getName().equals(finalS)).findAny().orElse(null); + } + } + + private final int id; + private final int owner; + private String name; + private Integer parent; + private String item; + private String type; + private boolean schemFormat; + private int rank; + private Timestamp lastUpdate; + private final boolean isDir; + private Map brCache = new HashMap<>(); + + public static List filterSchems(int user, Predicate filter) { + List finalList = new ArrayList<>(); + List nodes = SchematicNode.getSchematicsAccessibleByUser(user, null); + nodes.forEach(node -> { + if (node.isDir()) { + finalList.addAll(deepGet(node.getId(), filter)); + } else { + if (filter.test(node)) + finalList.add(node); + } + }); + return finalList; + } + + public static Integer countNodes() { + return countNodes.select(rs -> { + if (rs.next()) { + return rs.getInt("count"); + } + return 0; + }); + } + + public int getId() { + return id; + } + + public int getOwner() { + return owner; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + updateDB(); + } + + public Integer getParent() { + return parent; + } + + public void setParent(Integer parent) { + this.parent = parent; + updateDB(); + } + + public String getItem() { + if (item.isEmpty()) { + return isDir ? "CHEST" : "CAULDRON_ITEM"; + } + return item; + } + + public void setItem(String item) { + this.item = item; + updateDB(); + } + + @Deprecated + public String getType() { + if(isDir) + throw new SecurityException("Node is Directory"); + return type; + } + + @Deprecated + public void setType(String type) { + if(isDir) + throw new SecurityException("Node is Directory"); + this.type = type; + updateDB(); + } + + public boolean isDir() { + return isDir; + } + + public boolean getSchemFormat() { + if(isDir) + throw new SecurityException("Node is Directory"); + return schemFormat; + } + + public int getRank() { + if(isDir) + throw new SecurityException("Node is Directory"); + return rank; + } + + public void setRank(int rank) { + if(isDir) + throw new SecurityException("Node is Directory"); + this.rank = rank; + } + + public SchematicType getSchemtype() { + if(isDir()) + throw new RuntimeException("Is Directory"); + return SchematicType.fromDB(type); + } + + public void setSchemtype(SchematicType type) { + if(isDir()) + throw new RuntimeException("Is Directory"); + this.type = type.toDB(); + updateDB(); + } + + public SchematicNode getParentNode() { + if(parent == null) return null; + return SchematicNode.getSchematicNode(parent); + } + + public boolean accessibleByUser(int user) { + return NodeMember.getNodeMember(id, user) != null; + } + + public Set getMembers() { + return NodeMember.getNodeMembers(id); + } + + public Timestamp getLastUpdate() { + return lastUpdate; + } + + public String generateBreadcrumbs(SteamwarUser user) { + return brCache.computeIfAbsent(user.getId(), integer -> generateBreadcrumbs("/", user)); + } + + public String generateBreadcrumbs(String split, SteamwarUser user) { + StringBuilder builder = new StringBuilder(getName()); + SchematicNode currentNode = this; + if (currentNode.isDir()) builder.append("/"); + while (currentNode.getParentNode() != null) { + currentNode = currentNode.getParentNode(); + builder.insert(0, split) + .insert(0, currentNode.getName()); + if (currentNode.getMembers().stream().anyMatch(member -> member.getMember() == user.getId())) { + break; + } + } + return builder.toString(); + } + + private void updateDB() { + updateDB.update(name, owner, parent, item, type, rank, id); + this.lastUpdate = Timestamp.from(Instant.now()); + this.brCache.clear(); + } + + public void delete() { + if (isDir()) { + getSchematicNodeInNode(getId()).forEach(SchematicNode::delete); + } + deleteNode.update(id); + } + + public InputStream schemData() throws IOException { + try { + return selSchemData.select(rs -> { + rs.next(); + Blob schemData = rs.getBlob("NodeData"); + if(schemData == null) { + throw new SecurityException("SchemData is null"); + } + try { + return new GZIPInputStream(schemData.getBinaryStream()); + } catch (IOException e) { + throw new SecurityException("SchemData is wrong", e); + } + }, id); + } catch (Exception e) { + throw new IOException(e); + } + } + + public Clipboard load() throws IOException, NoClipboardException { + if(isDir) + throw new SecurityException("Node is Directory"); + return WorldEditWrapper.impl.getClipboard(schemData(), schemFormat); + } + + public void loadToPlayer(Player player) throws IOException, NoClipboardException { + if(isDir) + throw new SecurityException("Node is Directory"); + WorldEditWrapper.impl.setPlayerClipboard(player, schemData(), schemFormat); + } + + public void saveFromPlayer(Player player) throws IOException, NoClipboardException { + if(isDir) + throw new SecurityException("Node is Directory"); + saveFromPlayer(player, Core.getVersion() > 12); + } + + public void saveFromBytes(byte[] bytes, boolean newFormat) { + if(isDir) + throw new SecurityException("Node is Directory"); + updateDatabase(new ByteArrayInputStream(bytes), newFormat); + } + + public void saveFromPlayer(Player player, boolean newFormat) throws IOException, NoClipboardException { + if(isDir) + throw new SecurityException("Node is Directory"); + updateDatabase(WorldEditWrapper.impl.getPlayerClipboard(player, newFormat), newFormat); + } + + private void updateDatabase(InputStream blob, boolean newFormat) { + updateDatabase.update(blob, newFormat, id); + schemFormat = newFormat; + } + + public static Clipboard clipboardFromStream(InputStream is, boolean schemFormat) { + try { + return WorldEditWrapper.impl.getClipboard(is, schemFormat); + } catch (IOException e) { + throw new SecurityException("Could not read schem", e); + } + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SchematicNode)) + return false; + + SchematicNode node = (SchematicNode) obj; + return node.getId() == id; + } + + public static List getNodeTabcomplete(SteamwarUser user, String s) { + List list = new ArrayList<>(); + boolean sws = s.startsWith("/"); + if (sws) { + s = s.substring(1); + } + if (s.contains("/")) { + String preTab = s.substring(0, s.lastIndexOf("/") + 1); + SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab); + if (pa == null) return Collections.emptyList(); + List nodes = SchematicNode.getSchematicNodeInNode(pa); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.generateBreadcrumbs(user))); + } else { + List nodes = SchematicNode.getSchematicsAccessibleByUser(user.getId(), 0); + nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : ""))); + } + list.remove("//copy"); + return list; + } +}