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" )