diff --git a/SchematicSystem/SchematicSystem_21/build.gradle.kts b/SchematicSystem/SchematicSystem_21/build.gradle.kts
new file mode 100644
index 00000000..2654c5b7
--- /dev/null
+++ b/SchematicSystem/SchematicSystem_21/build.gradle.kts
@@ -0,0 +1,37 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2025 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.paperapi21) {
+ attributes {
+ // Very Hacky, but it works
+ attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
+ }
+ }
+
+ compileOnly(libs.nms21)
+ compileOnly(libs.fawe21)
+}
diff --git a/SchematicSystem/SchematicSystem_21/src/de/steamwar/schematicsystem/autocheck/AutoChecker21.java b/SchematicSystem/SchematicSystem_21/src/de/steamwar/schematicsystem/autocheck/AutoChecker21.java
new file mode 100644
index 00000000..e2b43bce
--- /dev/null
+++ b/SchematicSystem/SchematicSystem_21/src/de/steamwar/schematicsystem/autocheck/AutoChecker21.java
@@ -0,0 +1,137 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 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.core.Core;
+import de.steamwar.sql.GameModeConfig;
+import org.bukkit.Material;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class AutoChecker21 implements AutoChecker.IAutoChecker {
+
+ public AutoChecker.BlockScanResult scan(Clipboard clipboard, GameModeConfig type) {
+ 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 (AutoCheckerItems.impl.getInventoryMaterials().contains(material)) {
+ checkInventory(result, block, material, new BlockPos(x, y, z), type);
+ }
+
+ if (x == min.getBlockX() || x == max.getBlockX() || y == max.getBlockY() || z == min.getBlockZ() || z == max.getBlockZ()) {
+ 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));
+ AutoCheckerItems.impl.getAllowedMaterialsInInventory().forEach(material -> itemsInInv.put(material, AutoCheckerItems.impl.getInventoryMaterials()));
+ }
+
+ private void checkInventory(AutoChecker.BlockScanResult result, BaseBlock block, Material material, BlockPos pos, GameModeConfig type) {
+ 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;
+ int windChargeCount = 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(type.Schematic.Type.equals("wargearseason26") && material == Material.DISPENSER && itemType == Material.WIND_CHARGE) {
+ windChargeCount += item.getInt("count");
+ }
+ else 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 += Core.getVersion() >= 21 ? item.getInt("count") : item.getByte("Count");
+ }
+ if (item.containsKey("tag")) {
+ result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
+ }
+ }
+ result.getDispenserItems().put(pos, counter);
+ result.getWindChargeCount().put(pos, windChargeCount);
+ }
+
+ @Override
+ public AutoCheckerResult check(Clipboard clipboard, GameModeConfig type) {
+ return AutoCheckerResult.builder().type(type).height(clipboard.getDimensions().getBlockY()).width(clipboard.getDimensions().getBlockX())
+ .depth(clipboard.getDimensions().getBlockZ()).blockScanResult(scan(clipboard, type))
+ .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, GameModeConfig type) {
+ return AutoCheckerResult.builder().type(type).height(clipboard.getDimensions().getBlockY()).width(clipboard.getDimensions().getBlockX())
+ .depth(clipboard.getDimensions().getBlockZ()).build();
+ }
+}
diff --git a/SchematicSystem/SchematicSystem_21/src/de/steamwar/schematicsystem/autocheck/AutoCheckerItems21.java b/SchematicSystem/SchematicSystem_21/src/de/steamwar/schematicsystem/autocheck/AutoCheckerItems21.java
new file mode 100644
index 00000000..af50a3fa
--- /dev/null
+++ b/SchematicSystem/SchematicSystem_21/src/de/steamwar/schematicsystem/autocheck/AutoCheckerItems21.java
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2025 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 org.bukkit.Material;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+public class AutoCheckerItems21 implements AutoCheckerItems {
+
+ 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(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, Material.DIAMOND_HORSE_ARMOR, Material.IRON_HORSE_ARMOR,
+ Material.GOLDEN_HORSE_ARMOR, Material.LEATHER_HORSE_ARMOR, Material.HONEY_BOTTLE, Material.LILAC, Material.ROSE_BUSH, Material.PEONY,
+ Material.TALL_GRASS, Material.LARGE_FERN);
+
+ @Override
+ public Set getInventoryMaterials() {
+ return INVENTORY;
+ }
+
+ @Override
+ public Set getAllowedMaterialsInInventory() {
+ return FLOWERS;
+ }
+}
diff --git a/SchematicSystem/SchematicSystem_Core/src/SchematicSystem.properties b/SchematicSystem/SchematicSystem_Core/src/SchematicSystem.properties
index 0ff05daa..da86e45c 100644
--- a/SchematicSystem/SchematicSystem_Core/src/SchematicSystem.properties
+++ b/SchematicSystem/SchematicSystem_Core/src/SchematicSystem.properties
@@ -262,6 +262,8 @@ 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}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Forbidden block: §c{0}
+AUTO_CHECKER_RESULT_WIND_CHARGES=§7Windcharges: §c{0}§7, Max: §e2048
+AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER=§7Dispenser: §c[{0}, {1}, {2}]§7, Windcharges: §c{3}§7
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_DESIGN_BLOCK=§7{0} in Design: [{1}, {2}, {3}]
diff --git a/SchematicSystem/SchematicSystem_Core/src/SchematicSystem_de.properties b/SchematicSystem/SchematicSystem_Core/src/SchematicSystem_de.properties
index 12030a2f..aa72936e 100644
--- a/SchematicSystem/SchematicSystem_Core/src/SchematicSystem_de.properties
+++ b/SchematicSystem/SchematicSystem_Core/src/SchematicSystem_de.properties
@@ -242,6 +242,8 @@ 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_WIND_CHARGES=§7Windcharges: §c{0}§7, Max: §e2048
+AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER=§7Werfer: §c[{0}, {1}, {2}]§7, Windcharges: §c{3}§7
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_DESIGN_BLOCK=§7{0} im Design: [{1}, {2}, {3}]
diff --git a/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java b/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java
index eb464553..7d17e8f9 100644
--- a/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java
+++ b/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java
@@ -55,6 +55,7 @@ public class AutoChecker {
private final List records = new ArrayList<>();
private final Map> designBlocks = new EnumMap<>(Material.class);
private final Map dispenserItems = new HashMap<>();
+ private final Map windChargeCount = new HashMap<>();
private final Map> forbiddenItems = new HashMap<>();
private final Map> forbiddenNbt = new HashMap<>();
}
diff --git a/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java b/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java
index e6ba4728..d96a9557 100644
--- a/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java
+++ b/SchematicSystem/SchematicSystem_Core/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java
@@ -52,6 +52,7 @@ public class AutoCheckerResult {
isBlockCountOk() &&
isLimitedBlocksOK() &&
isDispenserItemsOK() &&
+ isWindchargeCountOK() &&
!type.isAfterDeadline() &&
entities.isEmpty() &&
isDesignBlastResistanceOK();
@@ -62,8 +63,18 @@ public class AutoCheckerResult {
!type.isAfterDeadline();
}
+ public boolean isWindchargeCountOK() {
+ if(type.Schematic.Type.equals("wargearseason26")) {
+ int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
+ return windChargesCount <= 2048;
+ }
+ else {
+ return true;
+ }
+ }
+
public boolean isDispenserItemsOK() {
- return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems);
+ return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems);
}
public boolean hasWarnings() {
@@ -127,6 +138,19 @@ public class AutoCheckerResult {
}
});
}
+
+ if(!isWindchargeCountOK()) {
+ int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
+ SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES", p, windChargesCount, 2048);
+ blockScanResult.getWindChargeCount().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > 0).forEach(blockVector3IntegerEntry -> {
+ SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()),
+ blockVector3IntegerEntry.getKey().getBlockX(),
+ blockVector3IntegerEntry.getKey().getBlockY(),
+ blockVector3IntegerEntry.getKey().getBlockZ(),
+ blockVector3IntegerEntry.getValue());
+ });
+ }
+
blockScanResult.getDispenserItems().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > type.Schematic.MaxDispenserItems).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(),
@@ -135,6 +159,7 @@ public class AutoCheckerResult {
blockVector3IntegerEntry.getValue(),
type.Schematic.MaxDispenserItems);
});
+
blockScanResult.getRecords().forEach(blockVector3 -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_RECORD", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ());
});
diff --git a/SchematicSystem/build.gradle.kts b/SchematicSystem/build.gradle.kts
index 65613802..947812e3 100644
--- a/SchematicSystem/build.gradle.kts
+++ b/SchematicSystem/build.gradle.kts
@@ -32,4 +32,5 @@ dependencies {
implementation(project(":SchematicSystem:SchematicSystem_15"))
implementation(project(":SchematicSystem:SchematicSystem_19"))
implementation(project(":SchematicSystem:SchematicSystem_20"))
+ implementation(project(":SchematicSystem:SchematicSystem_21"))
}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 5f364d5b..ea47eac1 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -101,6 +101,7 @@ dependencyResolutionManagement {
library("hamcrest", "org.hamcrest:hamcrest:2.2")
library("classindex", "org.atteo.classindex:classindex:3.13")
+
library("spigotapi", "org.spigotmc:spigot-api:1.20-R0.1-SNAPSHOT")
library("spigotannotations", "org.spigotmc:plugin-annotations:1.2.3-SNAPSHOT")
library("paperapi", "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT")
@@ -222,6 +223,7 @@ include(
"SchematicSystem:SchematicSystem_15",
"SchematicSystem:SchematicSystem_19",
"SchematicSystem:SchematicSystem_20",
+ "SchematicSystem:SchematicSystem_21",
"SchematicSystem:SchematicSystem_Core"
)