13
0

Usable™ AutoChecker and AutoFixer™
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful

Dieser Commit ist enthalten in:
Chaoscaot 2022-11-26 10:34:35 +01:00
Ursprung 315a59bc91
Commit 06f1df7fc9
10 geänderte Dateien mit 266 neuen und 10 gelöschten Zeilen

Datei anzeigen

@ -57,7 +57,6 @@ public class AutoChecker15 implements AutoChecker.IAutoChecker {
Material.WHITE_TULIP,
Material.PINK_TULIP,
Material.OXEYE_DAISY,
Material.CORNFLOWER,
Material.LILY_OF_THE_VALLEY,
Material.WITHER_ROSE,
Material.SUNFLOWER,

Datei anzeigen

@ -0,0 +1,119 @@
package de.steamwar.schematicsystem.commands;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.jnbt.CompoundTagBuilder;
import com.sk89q.jnbt.ListTag;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.schematicsystem.CheckSchemType;
import de.steamwar.schematicsystem.autocheck.AutoCheckerResult;
import de.steamwar.schematicsystem.autocheck.BlockPos;
import org.bukkit.Material;
import java.util.*;
import java.util.stream.Collectors;
public class SchematicCommand15 implements SchematicCommand.ISchematicCommand {
@Override
public Clipboard fixClipboard(Clipboard clipboard, AutoCheckerResult result, CheckSchemType type) throws Exception {
for (BlockPos blockPos : result.getRecords()) {
BlockVector3 vector = BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ());
clipboard.setBlock(vector, clipboard.getFullBlock(vector).toBaseBlock(new CompoundTag(Collections.emptyMap())));
}
Map<BlockPos, Set<Material>> toBeCheckedInvs = new HashMap<>();
toBeCheckedInvs.putAll(result.getForbiddenItems());
toBeCheckedInvs.putAll(result.getForbiddenNbt());
for (Map.Entry<BlockPos, Set<Material>> entry: toBeCheckedInvs.entrySet()) {
BlockPos pos = entry.getKey();
Set<Material> materials = entry.getValue();
BlockVector3 vector = BlockVector3.at(pos.getX(), pos.getY(), pos.getZ());
BaseBlock block = clipboard.getFullBlock(vector);
CompoundTag tag = block.getNbtData();
CompoundTagBuilder builder = CompoundTagBuilder.create();
List<CompoundTag> list = new ArrayList<>();
for (CompoundTag items : tag.getList("Items", CompoundTag.class)) {
if(materials.contains(Material.matchMaterial(items.getString("id")))) {
continue;
}
if(items.containsKey("tag")) {
continue;
}
list.add(items);
}
builder.put("Items", new ListTag(CompoundTag.class, list));
clipboard.setBlock(vector, block.toBaseBlock(builder.build()));
}
if(type.getMaxDispenserItems() > 0 ) {
for (Map.Entry<BlockPos, Integer> entry : result.getDispenserItems().entrySet()) {
if(entry.getValue() <= type.getMaxDispenserItems()) {
continue;
}
BlockPos pos = entry.getKey();
BlockVector3 vector = BlockVector3.at(pos.getX(), pos.getY(), pos.getZ());
BaseBlock block = clipboard.getFullBlock(vector);
CompoundTag tag = block.getNbtData();
CompoundTagBuilder builder = tag.createBuilder();
List<CompoundTag> items = tag.getList("Items", CompoundTag.class);
Collections.reverse(items); // To let the first item be in the Dispenser
List<CompoundTag> list = new ArrayList<>();
int diff = entry.getValue() - type.getMaxDispenserItems();
for (CompoundTag item : items) {
if(item == null) {
continue;
}
if(diff == 0) {
list.add(item);
continue;
}
if(diff > item.getByte("Count")) {
diff -= item.getByte("Count");
continue;
}
item = item.createBuilder().putByte("Count", (byte) (item.getByte("Count") - diff)).build();
diff = 0;
list.add(item);
}
builder.put("Items", new ListTag(CompoundTag.class, list));
clipboard.setBlock(vector, block.toBaseBlock(builder.build()));
}
}
if(!result.isLimitedBlocksOK()) {
Set<Material> toReplace = type.getLimits().entrySet().stream()
.filter(setIntegerEntry -> setIntegerEntry.getValue() == 0)
.flatMap(setIntegerEntry -> setIntegerEntry.getKey().stream())
.map(Material::matchMaterial)
.collect(Collectors.toSet());
BlockState replaceType = Objects.requireNonNull(toReplace.contains(Material.END_STONE) ? BlockTypes.IRON_BLOCK : BlockTypes.END_STONE).getDefaultState();
BlockVector3 min = clipboard.getMinimumPoint();
BlockVector3 max = clipboard.getMaximumPoint();
for (int i = min.getBlockX(); i <= max.getBlockX(); i++) {
for (int j = min.getBlockY(); j <= max.getBlockY(); j++) {
for (int k = min.getBlockZ(); k <= max.getBlockZ(); k++) {
BlockVector3 vector = BlockVector3.at(i, j, k);
BaseBlock block = clipboard.getFullBlock(vector);
if(toReplace.contains(Material.matchMaterial(block.getBlockType().getId()))) {
clipboard.setBlock(vector, replaceType.toBaseBlock());
}
}
}
}
}
return clipboard;
}
}

Datei anzeigen

@ -82,6 +82,8 @@ UTIL_SUBMIT_DIRECT=§eSubmit directly
UTIL_SUBMIT_DIRECT_DONE=§aThe Schematic will be reviewed in a timely manner
UTIL_SUBMIT_EXTEND=§eExtend Schematic
UTIL_SUBMIT_EXTEND_DONE=§aThe preparation server is starting
UTIL_CHECK_TYPE_NOT_FOUND=§cThe type {0} was not found
UTIL_CHECK_SUCCESS=§aThe schematic was checked successfully
COMMAND_INVALID_NODE=§cInvalid Schematic
COMMAND_NOT_OWN=§cYou can only use this command on your own Schematic
@ -130,6 +132,14 @@ COMMAND_DELETE_MEMBER=§aYou have removed yourself from the Schematic
COMMAND_DELETE_DIR=§aThe folder §e{0}§a is deleted...
COMMAND_DELETE_DIR_FULL=§cThe folder must be empty to delete it
COMMAND_DELETE_SCHEM=§aThe Schematic §e{0}§a is deleted...
COMMAND_CHECK_SELECTION_INCOMPLETE=§cThe selection is incomplete
COMMAND_CHECK_CLIPBOARD_EMPTY=§cThe clipboard is empty
COMMAND_FIX_OK=§aThe schematic is already fixed
COMMAND_FIX_DONE=§aThe schematic has been fixed
COMMAND_FIX_COULD_NOT_FIX=§cCould not fix this in the schematic
COMMAND_FIX_MANUAL=manually fix
COMMAND_FIX_ERROR=§cError while fixing the schematic, please contact a developer
COMMAND_FIX_WRONG_VERSION=§cThis feature is only available for version 1.15 and greater
HELP_HEADER=§e§lSchematicSystem §8§lHelp
HELP_VIEW=Find & Load
@ -141,6 +151,7 @@ HELP_VIEW_4=§8/§7schem §esearch §8[§7keyword§8] - §7Searches for matching
HELP_VIEW_5=§8/§7schem §eload §8[§7schematic§8] - §7Loads a schematic
HELP_VIEW_6=§8/§7schem §edownload §8[§7schematic§8] - §7Gives you a download link (valid for 1 min)
HELP_VIEW_7=§8/§7download §8- §7Gives you a download link for your current clipboard (valid for 1 min)
HELP_VIEW_8=§8/§7schem §echeck §8[§7schematic§8|§7selection§8|§7clipboard§8] [§7schematictype§8] - §7Checks the schematic for errors
HELP_EDIT=Save & Edit
HELP_EDIT_HOVER=Modification of schematics and folders
HELP_EDIT_1=§8/§7schem §esave §8[§7schematic§8] - §7Saves your clipboard as a schematic
@ -151,6 +162,7 @@ HELP_EDIT_5=§8/§7schem §echangetype §8[§7schematic§8] - §7Changes the typ
HELP_EDIT_6=§8/§7schem §elockreplay §8[§7schematic§8] - §7Locks replays of the schematic
HELP_EDIT_7=§8/§7schem §ereplacecolor §8[§7schematic§8] - §7Changes color substitution in the arena
HELP_EDIT_8=§8/§7schem §edelete §8[§7schematic§8] - §7Deletes a schematic
HELP_EDIT_9=§8/§7schem §efix §8[§7schematictype§8] - §7Tries to fix the schematic in your clipboard
HELP_SHARE=Ownership
HELP_SHARE_HOVER=Share Schematics with others
HELP_SHARE_1=§8/§7schem §eaddmember §8[§7schematic§8] §8[§7Spieler§8] - §7Adds a player to a schematic
@ -216,7 +228,8 @@ AUTO_CHECKER_RESULT_LENGTH=§7Length: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_HEIGHT=§7Height: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_BLOCKS=§7Blocks: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unknown block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: {1}{2}§7, Max: §e{3}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Forbidden block: §c{0}
AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Forbidden Item: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Defunct NBT: §7[{0}, {1}, {2}]
AUTO_CHECKER_RESULT_RECORD=§7Record: §c[{0}, {1}, {2}]

Datei anzeigen

@ -117,6 +117,14 @@ COMMAND_DELETE_MEMBER=§aDu hast dich von der Schematic entfernt
COMMAND_DELETE_DIR=§aDer Ordner §e{0}§a wird gelöscht...
COMMAND_DELETE_DIR_FULL=§cDer Ordner muss leer sein, um ihn zu löschen
COMMAND_DELETE_SCHEM=§aDie Schematic §e{0}§a wird gelöscht...
COMMAND_CHECK_SELECTION_INCOMPLETE=§cDeine Auswahl ist unvollständig
COMMAND_CHECK_CLIPBOARD_EMPTY=§cDein Clipboard ist leer
COMMAND_FIX_OK=§aDie Schematic ist bereits gefixt
COMMAND_FIX_DONE=§aDie Schematic wurde repariert
COMMAND_FIX_COULD_NOT_FIX=§cKonnte diese nicht sachen in der Schematic reparieren
COMMAND_FIX_MANUAL=Manuel Fixen
COMMAND_FIX_ERROR=§cFehler beim Fixen der Schematic, bitte kontaktiere einen Developer
COMMAND_FIX_WRONG_VERSION=§cDiese Funktion ist nur für Version 1.15 und höher verfügbar
HELP_HEADER=§e§lSchematicSystem §8§lHilfe
HELP_VIEW=Finden & Laden
@ -128,6 +136,7 @@ HELP_VIEW_4=§8/§7schem §esearch §8[§7Stichwort§8] - §7Sucht nach passende
HELP_VIEW_5=§8/§7schem §eload §8[§7Schematic§8] - §7Lädt eine Schematic
HELP_VIEW_6=§8/§7schem §edownload §8[§7Schematic§8] - §7Gibt dir einen Downloadlink (1 min gültig)
HELP_VIEW_7=§8/§7download §8- §7Gibt dir einen Downloadlink von deinem Clipboard (1 min gültig)
HELP_VIEW_8=§8/§7schem §echeck §8[§7Schematic§8|§7selection§8|§7clipboard§8] [§7SchematicTyp§8] - §7Überprüft deine Schematic
HELP_EDIT=Speichern & Bearbeiten
HELP_EDIT_HOVER=Modifizierung von Schematics und Ordnern
HELP_EDIT_1=§8/§7schem §esave §8[§7Schematic§8] - §7Speichert dein Clipboard als Schematic
@ -138,6 +147,7 @@ HELP_EDIT_5=§8/§7schem §echangetype §8[§7Schematic§8] - §7Ändert die Art
HELP_EDIT_6=§8/§7schem §elockreplay §8[§7Schematic§8] - §7Sperrt Replays mit der Schematic
HELP_EDIT_7=§8/§7schem §ereplacecolor §8[§7Schematic§8] - §7Ändert Farbersetzung in der Arena
HELP_EDIT_8=§8/§7schem §edelete §8[§7Schematic§8] - §7Löscht eine Schematic
HELP_EDIT_9=§8/§7schem §efix §8[§7SchematicTyp§8] - §7Versucht die Schematic in deinem Clipboard konform zu machen
HELP_SHARE=Besitzrechte
HELP_SHARE_HOVER=Schematics mit anderen teilen
HELP_SHARE_1=§8/§7schem §eaddmember §8[§7Schematic§8] §8[§7Spieler§8] - §7Fügt einen Spieler zu einer Schematic hinzu
@ -200,6 +210,7 @@ AUTO_CHECKER_RESULT_HEIGHT=§7Höhe: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_BLOCKS=§7Blöcke: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unbekannter Block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Verbotener Block: §c{0}
AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Verbotener gegenstand: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Keine NBT-Daten: §c[{0}, {1}, {2}]
AUTO_CHECKER_RESULT_RECORD=§7Schallplatte: §c[{0}, {1}, {2}]

Datei anzeigen

@ -5,6 +5,7 @@ import de.steamwar.core.VersionDependent;
import de.steamwar.schematicsystem.CheckSchemType;
import de.steamwar.schematicsystem.SchematicSystem;
import lombok.Getter;
import lombok.ToString;
import org.bukkit.Material;
import java.util.*;
@ -43,6 +44,7 @@ public class AutoChecker {
}
@Getter
@ToString
public static class BlockScanResult {
private final Map<Material, Integer> blockCounts = new EnumMap<>(Material.class);
private final List<BlockPos> defunctNbt = new ArrayList<>();

Datei anzeigen

@ -94,16 +94,18 @@ public class AutoCheckerResult {
if(!isLimitedBlocksOK()) {
type.getLimits().forEach((strings, integer) -> {
for (String string : strings) {
Material mat = Material.getMaterial(string);
if(mat == null) {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL", p, string);
} else if(blockCounts.getOrDefault(mat, 0) > integer) {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_BLOCK", p, mat.name());
Material mat = Material.matchMaterial(string);
if(mat != null && blockCounts.getOrDefault(mat, 0) > integer) {
if(integer == 0) {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK", p, mat.name());
} else {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_BLOCK", p, mat.name(), blockCounts.getOrDefault(mat, 0), integer);
}
}
}
});
}
dispenserItems.entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > type.getMaxBlocks()).forEach(blockVector3IntegerEntry -> {
dispenserItems.entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > type.getMaxDispenserItems()).forEach(blockVector3IntegerEntry -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_DISPENSER_ITEMS", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()),
blockVector3IntegerEntry.getKey().getBlockX(),
blockVector3IntegerEntry.getKey().getBlockY(),

Datei anzeigen

@ -1,13 +1,17 @@
package de.steamwar.schematicsystem.autocheck;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
/*
* Can be removed with 1.12 support removal
*/
@Getter
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class BlockPos {
private int x;
private int y;

Datei anzeigen

@ -19,8 +19,17 @@
package de.steamwar.schematicsystem.commands;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.bukkit.BukkitPlayer;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.command.*;
import de.steamwar.core.Core;
import de.steamwar.core.VersionDependent;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.inventory.SchematicSelector;
import de.steamwar.providers.BauServerInfo;
@ -28,6 +37,7 @@ import de.steamwar.schematicsystem.CheckSchemType;
import de.steamwar.schematicsystem.SafeSchematicNode;
import de.steamwar.schematicsystem.SchematicSystem;
import de.steamwar.schematicsystem.autocheck.AutoChecker;
import de.steamwar.schematicsystem.autocheck.AutoCheckerResult;
import de.steamwar.sql.*;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ClickEvent;
@ -561,6 +571,79 @@ public class SchematicCommand extends SWCommand {
}
}
@Register("check")
public void checkCommand(Player player, @Validator("isOwnerSchematicValidator") SchematicNode node, SchematicType type) {
try {
check(player, new SchematicData(node).load(), type, node.getName(), false);
} catch (IOException e) {
SchematicSystem.MESSAGE.send("UTIL_LOAD_ERROR", player);
}
}
@Register(value = {"check", "clipboard"})
public void checkClipboardCommand(Player player, SchematicType type) {
try {
check(player, WorldEdit.getInstance().getSessionManager().get(new BukkitPlayer(player)).getClipboard().getClipboard(), type, "clipboard", false);
} catch (EmptyClipboardException e) {
SchematicSystem.MESSAGE.send("COMMAND_CHECK_CLIPBOARD_EMPTY", player);
}
}
@Register(value = {"check", "selection"})
public void checkSelectionCommand(Player player, SchematicType type) {
try {
Clipboard clipboard = new BlockArrayClipboard(WorldEdit.getInstance().getSessionManager().get(new BukkitPlayer(player)).getSelection(new BukkitWorld(player.getWorld())));
EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(player.getWorld()), -1);
Operations.complete(new ForwardExtentCopy(editSession, clipboard.getRegion(), clipboard, clipboard.getMinimumPoint()));
check(player, clipboard, type, "selection", false);
} catch (IncompleteRegionException e) {
SchematicSystem.MESSAGE.send("COMMAND_CHECK_SELECTION_INCOMPLETE", player);
} catch (WorldEditException e) {
SchematicSystem.MESSAGE.send("COMMAND_SAVE_ERROR", player);
}
}
@Register("fix")
public void fixSchematicCommand(Player player, SchematicType type) {
if(Core.getVersion() < 15) {
SchematicSystem.MESSAGE.send("COMMAND_FIX_WRONG_VERSION", player);
return;
}
Clipboard clipboard;
try {
clipboard = WorldEdit.getInstance().getSessionManager().get(new BukkitPlayer(player)).getClipboard().getClipboard();
} catch (EmptyClipboardException e) {
SchematicSystem.MESSAGE.send("COMMAND_CHECK_CLIPBOARD_EMPTY", player);
return;
}
CheckSchemType checkSchemType = CheckSchemType.get(type);
if (checkSchemType == null) {
SchematicSystem.MESSAGE.send("UTIL_CHECK_TYPE_NOT_FOUND", player, type.name());
return;
}
AutoCheckerResult result = AutoChecker.check(clipboard, checkSchemType);
if(result.isOk()) {
SchematicSystem.MESSAGE.send("COMMAND_FIX_OK", player);
return;
}
try {
clipboard = impl.fixClipboard(clipboard, result, checkSchemType);
WorldEdit.getInstance().getSessionManager().get(new BukkitPlayer(player)).setClipboard(new ClipboardHolder(clipboard));
AutoCheckerResult after = AutoChecker.check(clipboard, checkSchemType);
if(after.isOk()) {
SchematicSystem.MESSAGE.send("COMMAND_FIX_DONE", player);
} else {
after.sendErrorMessage(player, SchematicSystem.MESSAGE.parse("COMMAND_FIX_MANUAL", player));
SchematicSystem.MESSAGE.send("COMMAND_FIX_COULD_NOT_FIX", player);
}
} catch (Exception e) {
SchematicSystem.MESSAGE.send("COMMAND_FIX_ERROR", player);
e.printStackTrace();
}
}
@Register(value = "page", noTabComplete = true)
public void pageCommand(Player player, int page) {
cachedSchemList(player, page);
@ -797,4 +880,10 @@ public class SchematicCommand extends SWCommand {
AUSFAHREN,
NORMAL
}
private static final ISchematicCommand impl = VersionDependent.getVersionImpl(SchematicSystem.getInstance());
public static interface ISchematicCommand {
Clipboard fixClipboard(Clipboard clipboard, AutoCheckerResult result, CheckSchemType type) throws Exception;
}
}

Datei anzeigen

@ -56,7 +56,8 @@ public class SchematicCommandHelp {
"HELP_VIEW_4",
"HELP_VIEW_5",
"HELP_VIEW_6",
"HELP_VIEW_7"
"HELP_VIEW_7",
"HELP_VIEW_8"
}),
BEARBEITUNG("HELP_EDIT", "HELP_EDIT_HOVER", new String[]{
"HELP_EDIT_1",
@ -66,7 +67,8 @@ public class SchematicCommandHelp {
"HELP_EDIT_5",
"HELP_EDIT_6",
"HELP_EDIT_7",
"HELP_EDIT_8"
"HELP_EDIT_8",
"HELP_EDIT_9"
}),
MEMBER("HELP_SHARE", "HELP_SHARE_HOVER", new String[]{
"HELP_SHARE_1",

Datei anzeigen

@ -19,6 +19,7 @@
package de.steamwar.schematicsystem.commands;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.network.NetworkSender;
@ -304,6 +305,20 @@ public class SchematicCommandUtils {
}
}
public static void check(Player player, Clipboard clipboard, SchematicType type, String schemName, boolean gui) {
CheckSchemType checkSchemType = CheckSchemType.get(type);
if(checkSchemType == null) {
SchematicSystem.MESSAGE.send("UTIL_CHECK_TYPE_NOT_FOUND", player, type.name());
return;
}
AutoCheckerResult result = AutoChecker.check(clipboard, checkSchemType);
if(!result.isOk()) {
result.sendErrorMessage(player, schemName);
} else {
SchematicSystem.MESSAGE.send("UTIL_CHECK_SUCCESS", player, schemName);
}
}
public static SchematicNode mkdirs(String[] layers, SteamwarUser user, int minus) {
SchematicNode currentNode = null;
for (int i = 0; i < layers.length - minus; i++) {