diff --git a/SchematicSystem/SchematicSystem_19/build.gradle.kts b/SchematicSystem/SchematicSystem_19/build.gradle.kts
new file mode 100644
index 00000000..d1baf622
--- /dev/null
+++ b/SchematicSystem/SchematicSystem_19/build.gradle.kts
@@ -0,0 +1,32 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2024 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 .
+ */
+
+plugins {
+ steamwar.java
+}
+
+dependencies {
+ compileOnly(project(":SpigotCore", "default"))
+ compileOnly(project(":SchematicSystem:SchematicSystem_Core", "default"))
+
+ compileOnly(libs.spigotapi)
+
+ compileOnly(libs.nms19)
+ compileOnly(libs.fawe18)
+}
diff --git a/SchematicSystem/SchematicSystem_19/src/de/steamwar/schematicsystem/autocheck/AutoChecker19.java b/SchematicSystem/SchematicSystem_19/src/de/steamwar/schematicsystem/autocheck/AutoChecker19.java
new file mode 100644
index 00000000..92b06c0d
--- /dev/null
+++ b/SchematicSystem/SchematicSystem_19/src/de/steamwar/schematicsystem/autocheck/AutoChecker19.java
@@ -0,0 +1,213 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2023 SteamWar.de-Serverteam
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+
+package de.steamwar.schematicsystem.autocheck;
+
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.worldedit.entity.Entity;
+import com.sk89q.worldedit.extent.clipboard.Clipboard;
+import com.sk89q.worldedit.math.BlockVector3;
+import com.sk89q.worldedit.world.block.BaseBlock;
+import de.steamwar.schematicsystem.CheckSchemType;
+import org.bukkit.Material;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class AutoChecker19 implements AutoChecker.IAutoChecker {
+ private static final Set INVENTORY = EnumSet.of(
+ Material.BARREL,
+ Material.BLAST_FURNACE,
+ Material.BREWING_STAND,
+ Material.CAMPFIRE,
+ Material.CHEST,
+ Material.DISPENSER,
+ Material.DROPPER,
+ Material.FURNACE,
+ Material.HOPPER,
+ Material.JUKEBOX,
+ Material.SHULKER_BOX,
+ Material.WHITE_SHULKER_BOX,
+ Material.ORANGE_SHULKER_BOX,
+ Material.MAGENTA_SHULKER_BOX,
+ Material.LIGHT_BLUE_SHULKER_BOX,
+ Material.YELLOW_SHULKER_BOX,
+ Material.LIME_SHULKER_BOX,
+ Material.PINK_SHULKER_BOX,
+ Material.GRAY_SHULKER_BOX,
+ Material.LIGHT_GRAY_SHULKER_BOX,
+ Material.CYAN_SHULKER_BOX,
+ Material.PURPLE_SHULKER_BOX,
+ Material.BLUE_SHULKER_BOX,
+ Material.BROWN_SHULKER_BOX,
+ Material.GREEN_SHULKER_BOX,
+ Material.RED_SHULKER_BOX,
+ Material.BLACK_SHULKER_BOX,
+ Material.SMOKER,
+ Material.TRAPPED_CHEST);
+
+ private static final Set FLOWERS = EnumSet.of(
+ // 64-stackable Items
+ Material.CORNFLOWER,
+ Material.POPPY,
+ Material.FERN,
+ Material.DANDELION,
+ Material.BLUE_ORCHID,
+ Material.ALLIUM,
+ Material.AZURE_BLUET,
+ Material.RED_TULIP,
+ Material.ORANGE_TULIP,
+ Material.WHITE_TULIP,
+ Material.PINK_TULIP,
+ Material.OXEYE_DAISY,
+ Material.LILY_OF_THE_VALLEY,
+ Material.WITHER_ROSE,
+ Material.SUNFLOWER,
+ // 16-stackable Items
+ Material.HONEY_BOTTLE,
+ // Non-stackable items
+ Material.DIAMOND_HORSE_ARMOR,
+ Material.IRON_HORSE_ARMOR,
+ Material.GOLDEN_HORSE_ARMOR,
+ // Disks
+ Material.MUSIC_DISC_11,
+ Material.MUSIC_DISC_13,
+ Material.MUSIC_DISC_CAT,
+ Material.MUSIC_DISC_BLOCKS,
+ Material.MUSIC_DISC_CHIRP,
+ Material.MUSIC_DISC_FAR,
+ Material.MUSIC_DISC_MALL,
+ Material.MUSIC_DISC_MELLOHI,
+ Material.MUSIC_DISC_STAL,
+ Material.MUSIC_DISC_STRAD,
+ Material.MUSIC_DISC_WAIT,
+ Material.MUSIC_DISC_WARD,
+ Material.MUSIC_DISC_OTHERSIDE,
+ Material.MUSIC_DISC_PIGSTEP,
+ Material.MUSIC_DISC_RELIC,
+ Material.MUSIC_DISC_5
+ );
+
+ public AutoChecker.BlockScanResult scan(Clipboard clipboard) {
+ AutoChecker.BlockScanResult result = new AutoChecker.BlockScanResult();
+ BlockVector3 min = clipboard.getMinimumPoint();
+ BlockVector3 max = clipboard.getMaximumPoint();
+ for(int x = min.getBlockX(); x <= max.getBlockX(); x++){
+ for(int y = min.getBlockY(); y <= max.getBlockY(); y++) {
+ for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
+ final BaseBlock block = clipboard.getFullBlock(BlockVector3.at(x, y, z));
+ final Material material = Material.matchMaterial(block.getBlockType().getId());
+ if(material == null) {
+ continue;
+ }
+
+ result.getBlockCounts().merge(material, 1, Integer::sum);
+
+ if(INVENTORY.contains(material)) {
+ checkInventory(result, block, material, new BlockPos(x, y, z));
+ }
+
+ if(x == 0 || x == max.getBlockX() - 1 || y == max.getBlockY() - 1 || z == 0 || z == max.getBlockZ() - 1) {
+ result.getDesignBlocks().computeIfAbsent(material, m -> new ArrayList<>()).add(new BlockPos(x, y, z));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private static final Map> itemsInInv = new EnumMap<>(Material.class);
+
+ static {
+ itemsInInv.put(Material.BUCKET, EnumSet.of(Material.DISPENSER));
+ itemsInInv.put(Material.TNT, EnumSet.of(
+ Material.CHEST, Material.BARREL, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX,
+ Material.BLUE_SHULKER_BOX, Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX,
+ Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX,
+ Material.LIGHT_GRAY_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX,
+ Material.ORANGE_SHULKER_BOX, Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX,
+ Material.RED_SHULKER_BOX, Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX
+ ));
+ itemsInInv.put(Material.FIRE_CHARGE, EnumSet.of(Material.DISPENSER));
+ itemsInInv.put(Material.ARROW, EnumSet.of(Material.DISPENSER));
+ FLOWERS.forEach(material -> itemsInInv.put(material, INVENTORY));
+ }
+
+ private void checkInventory(AutoChecker.BlockScanResult result, BaseBlock block, Material material, BlockPos pos) {
+ CompoundTag nbt = block.getNbtData();
+ if(nbt == null) {
+ result.getDefunctNbt().add(pos);
+ return;
+ }
+
+
+ if(material == Material.JUKEBOX && nbt.getValue().containsKey("RecordItem")){
+ result.getRecords().add(pos);
+ return;
+ }
+
+ List items = nbt.getList("Items", CompoundTag.class);
+ if(items.isEmpty())
+ return; //Leeres Inventar
+
+ int counter = 0;
+ for(CompoundTag item : items){
+ if(!item.containsKey("id")){
+ result.getDefunctNbt().add(pos);
+ continue;
+ }
+
+ Material itemType = Material.matchMaterial(item.getString("id"));
+ if(itemType == null) //Leere Slots
+ continue;
+
+ if (!itemsInInv.getOrDefault(itemType, EnumSet.noneOf(Material.class)).contains(material)) {
+ result.getForbiddenItems().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
+ } else if(material == Material.DISPENSER && (itemType == Material.ARROW || itemType == Material.FIRE_CHARGE)) {
+ counter += item.getByte("Count");
+ }
+ if (item.containsKey("tag")) {
+ result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
+ }
+ }
+ result.getDispenserItems().put(pos, counter);
+ }
+
+ @Override
+ public AutoCheckerResult check(Clipboard clipboard, CheckSchemType type) {
+ return AutoCheckerResult.builder()
+ .type(type)
+ .height(clipboard.getDimensions().getBlockY())
+ .width(clipboard.getDimensions().getBlockX())
+ .depth(clipboard.getDimensions().getBlockZ())
+ .blockScanResult(scan(clipboard))
+ .entities(clipboard.getEntities().stream().map(Entity::getLocation).map(blockVector3 -> new BlockPos(blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ())).collect(Collectors.toList()))
+ .build();
+ }
+
+ @Override
+ public AutoCheckerResult sizeCheck(Clipboard clipboard, CheckSchemType type) {
+ return AutoCheckerResult.builder()
+ .type(type)
+ .height(clipboard.getDimensions().getBlockY())
+ .width(clipboard.getDimensions().getBlockX())
+ .depth(clipboard.getDimensions().getBlockZ())
+ .build();
+ }
+}
diff --git a/SchematicSystem/SchematicSystem_19/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand19.java b/SchematicSystem/SchematicSystem_19/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand19.java
new file mode 100644
index 00000000..2fecb87d
--- /dev/null
+++ b/SchematicSystem/SchematicSystem_19/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand19.java
@@ -0,0 +1,147 @@
+/*
+ This file is a part of the SteamWar software.
+
+ Copyright (C) 2023 SteamWar.de-Serverteam
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+
+package de.steamwar.schematicsystem.commands.schematiccommand;
+
+import com.sk89q.jnbt.CompoundTag;
+import com.sk89q.jnbt.CompoundTagBuilder;
+import com.sk89q.jnbt.ListTag;
+import com.sk89q.worldedit.EditSession;
+import com.sk89q.worldedit.WorldEditException;
+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.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 SchematicCommand19 implements SchematicCommand.ISchematicCommand {
+ @Override
+ public Clipboard fixClipboard(Clipboard clipboard, AutoCheckerResult result, CheckSchemType type) throws Exception {
+ for (BlockPos blockPos : result.getBlockScanResult().getRecords()) {
+ BlockVector3 vector = BlockVector3.at(blockPos.getX(), blockPos.getY(), blockPos.getZ());
+ clipboard.setBlock(vector, clipboard.getFullBlock(vector).toBaseBlock(new CompoundTag(Collections.emptyMap())));
+ }
+
+ Map> toBeCheckedInvs = new HashMap<>();
+
+ toBeCheckedInvs.putAll(result.getBlockScanResult().getForbiddenItems());
+ toBeCheckedInvs.putAll(result.getBlockScanResult().getForbiddenNbt());
+
+ for (Map.Entry> entry: toBeCheckedInvs.entrySet()) {
+ BlockPos pos = entry.getKey();
+ Set 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 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 entry : result.getBlockScanResult().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 items = tag.getList("Items", CompoundTag.class);
+ Collections.reverse(items); // To let the first item be in the Dispenser
+ List 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 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;
+ }
+
+ @Override
+ public void createCopy(EditSession editSession, Clipboard clipboard) throws WorldEditException {
+ Operations.complete(new ForwardExtentCopy(editSession, clipboard.getRegion(), clipboard, clipboard.getMinimumPoint()));
+ }
+}
diff --git a/SchematicSystem/build.gradle.kts b/SchematicSystem/build.gradle.kts
index f9871adc..813550b2 100644
--- a/SchematicSystem/build.gradle.kts
+++ b/SchematicSystem/build.gradle.kts
@@ -30,4 +30,5 @@ dependencies {
implementation(project(":SchematicSystem:SchematicSystem_Core"))
implementation(project(":SchematicSystem:SchematicSystem_8"))
implementation(project(":SchematicSystem:SchematicSystem_15"))
+ implementation(project(":SchematicSystem:SchematicSystem_19"))
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index dc94bc0c..cd2550dd 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -218,6 +218,7 @@ include(
"SchematicSystem",
"SchematicSystem:SchematicSystem_8",
"SchematicSystem:SchematicSystem_15",
+ "SchematicSystem:SchematicSystem_19",
"SchematicSystem:SchematicSystem_Core"
)