diff --git a/CommonCore/SQL/build.gradle.kts b/CommonCore/SQL/build.gradle.kts index 09528759..8706ecdf 100644 --- a/CommonCore/SQL/build.gradle.kts +++ b/CommonCore/SQL/build.gradle.kts @@ -19,6 +19,7 @@ plugins { steamwar.kotlin + kotlin("plugin.lombok") } kotlin { @@ -40,4 +41,8 @@ dependencies { compileOnlyApi(libs.exposedDao) compileOnlyApi(libs.exposedJdbc) compileOnlyApi(libs.exposedTime) + testImplementation(kotlin("test")) } +repositories { + mavenCentral() +} \ No newline at end of file diff --git a/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java b/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java index 87c6bdb1..2676dad5 100644 --- a/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java +++ b/CommonCore/SQL/src/de/steamwar/sql/GameModeConfig.java @@ -688,13 +688,6 @@ public final class GameModeConfig { */ public final int MaxBlocks; - /** - * Maximal amount of items per dispenser - * - * @implSpec {@code 128} by default - */ - public final int MaxDispenserItems; - /** * Maximal blast resistance for the blocks * @@ -715,6 +708,42 @@ public final class GameModeConfig { */ public final Map, Integer> Limited; + /** + * Item amount limits per inventory (container) type.
+ * Key: inventory material (e.g. DISPENSER, CHEST, ...). Value: item material -> maximal + * amount of that item allowed to be stored in a single inventory of that type.
+ * An item is only allowed to be placed in an inventory at all if it appears here for that + * inventory's material and item. Every material returned by + * {@link SQLWrapper#getInventoryMaterials()} is always present as a key - with an empty value + * map if not explicitly configured - so every real container is always scanned by the schematic + * checker for forbidden contents, it just allows no items by default.
+ * Configured via the {@code ItemsInInventory} list, entries consisting of {@code Inventories} + * (list of inventory materials or group names - {@code storage} for + * {@link SQLWrapper#getStorageMaterials()}, {@code all} for every material from + * {@link SQLWrapper#getInventoryMaterials()}), {@code Items} (list of item materials, each + * checked independently against {@code Amount} - not summed) and {@code Amount}. + * + * @implSpec No item is allowed in any inventory unless configured here + */ + public final Map> ItemsInInventory; + + /** + * Resolves one {@code Inventories} entry to the concrete inventory materials it denotes: + * {@code storage} for {@link SQLWrapper#getStorageMaterials()}, {@code all} for + * {@link SQLWrapper#getInventoryMaterials()}, or the material itself otherwise. + */ + private Set expandInventoryGroup(YMLWrapper loader, String name) { + switch (name.toUpperCase()) { + case "STORAGE": + return (Set) SQLWrapper.impl.getStorageMaterials(); + case "ALL": + return (Set) SQLWrapper.impl.getInventoryMaterials(); + default: + M material = loader.materialMapper.apply(name.toUpperCase()); + return material != null ? Collections.singleton(material) : Collections.emptySet(); + } + } + private SchematicConfig(YMLWrapper loader) { loaded = loader.canLoad(); Size = new SizeConfig(loader.with("Size")); @@ -731,7 +760,6 @@ public final class GameModeConfig { IgnorePublicOnly = loader.getBoolean("IgnorePublicOnly", false); UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false); MaxBlocks = loader.getInt("MaxBlocks", 0); - MaxDispenserItems = loader.getInt("MaxDispenserItems", 128); MaxBlastResistance = loader.getDouble("MaxBlastResistance", Double.MAX_VALUE); MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", MaxBlastResistance); @@ -753,6 +781,30 @@ public final class GameModeConfig { }); this.Limited = Collections.unmodifiableMap(Limited); + Map> itemsInInventory = new HashMap<>(); + for (Map entry : loader.getMapList("ItemsInInventory")) { + int amount = (Integer) entry.get("Amount"); + List inventories = (List) entry.get("Inventories"); + List items = (List) entry.get("Items"); + for (String inventoryName : inventories) { + for (M inventoryMaterial : expandInventoryGroup(loader, inventoryName)) { + if (inventoryMaterial == null) continue; + Map itemCaps = itemsInInventory.computeIfAbsent(inventoryMaterial, m -> new HashMap<>()); + for (String itemName : items) { + M itemMaterial = loader.materialMapper.apply(itemName.toUpperCase()); + if (itemMaterial == null) continue; + itemCaps.put(itemMaterial, amount); + } + } + } + } + for (M allMaterial : (Set) SQLWrapper.impl.getInventoryMaterials()) { + if (allMaterial == null) continue; + itemsInInventory.computeIfAbsent(allMaterial, m -> new HashMap<>()); + } + this.ItemsInInventory = Collections.unmodifiableMap(itemsInInventory.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> Collections.unmodifiableMap(e.getValue())))); + this.ReplacementsWithoutBlockUpdates = loader.getMap("ReplacementsWithoutBlockUpdates", loader.materialMapper, loader.materialMapper); this.ReplacementsWithBlockUpdates = loader.getMap("ReplacementsWithBlockUpdates", loader.materialMapper, loader.materialMapper); } diff --git a/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java b/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java index f049f680..be52e8fb 100644 --- a/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java +++ b/CommonCore/SQL/src/de/steamwar/sql/SQLWrapper.java @@ -24,6 +24,7 @@ import de.steamwar.ImplementationProvider; import java.io.File; import java.util.Collections; import java.util.List; +import java.util.Set; public interface SQLWrapper { SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl"); @@ -36,5 +37,21 @@ public interface SQLWrapper { return Collections.emptyList(); } + /** + * Storage-type inventory materials (chests, barrels, shulker boxes of any color) - the + * {@code storage} group usable in {@code GameModeConfig}'s {@code ItemsInInventory}. + */ + default Set getStorageMaterials() { + return Collections.emptySet(); + } + + /** + * Every inventory material - the {@code all} group usable in {@code GameModeConfig}'s + * {@code ItemsInInventory}. Expected to be a superset of {@link #getStorageMaterials()}. + */ + default Set getInventoryMaterials() { + return Collections.emptySet(); + } + void additionalExceptionMetadata(StringBuilder builder); } diff --git a/FightSystem/FightSystem_Core/src/config.yml b/FightSystem/FightSystem_Core/src/config.yml index 185704d5..8987b88a 100644 --- a/FightSystem/FightSystem_Core/src/config.yml +++ b/FightSystem/FightSystem_Core/src/config.yml @@ -107,8 +107,6 @@ Schematic: UnlimitedPrepare: false # defaults to false if missing # Maximal amount of blocks allowed in the schematic MaxBlocks: 0 # defaults to 0 (ignored) if missing - # Maximal amount of items per dispenser - MaxDispenserItems: 128 # defaults to 128 if missing # Maximal blast resistance for the design blocks MaxDesignBlastResistance: 100000000 # defaults to Double.MAX_VALUE if missing # List of limited material (combinations) @@ -116,6 +114,25 @@ Schematic: Limited: - Materials: [ ] Amount: 0 + # Item amount limits per inventory (container) type. + # An item is only allowed to be placed in an inventory at all if it appears here for that + # inventory's material (a small set of decorative items, e.g. flowers, is always allowed + # everywhere regardless of this list) - NOTHING is allowed in any inventory unless listed here. + # Each entry contains Inventories (list of inventory materials, or a group name - 'storage' for + # chests/barrels/shulker boxes of any color, 'all' for every scanned inventory material including + # dispensers, droppers, hoppers, furnaces, etc.), Items (list of item materials, each checked + # independently against Amount - not summed) and Amount (max stored amount of that item per + # single inventory of that type). + ItemsInInventory: # defaults to none (nothing allowed in any inventory) if missing + - Inventories: [ DISPENSER ] + Items: [ BUCKET ] + Amount: 1 + - Inventories: [ DISPENSER ] + Items: [ ARROW, FIRE_CHARGE ] + Amount: 128 + - Inventories: [ storage ] + Items: [ TNT ] + Amount: 1728 # The name of the game mode presented to the players GameName: WarGear # defaults to WarGear if missing diff --git a/SchematicSystem/src/SchematicSystem.properties b/SchematicSystem/src/SchematicSystem.properties index f9931f97..c72bcf96 100644 --- a/SchematicSystem/src/SchematicSystem.properties +++ b/SchematicSystem/src/SchematicSystem.properties @@ -269,7 +269,7 @@ AUTO_CHECKER_RESULT_DEFUNCT_NBT = §7Defunct NBT: §7[{0}, {1}, {2}] AUTO_CHECKER_RESULT_DESIGN_BLOCK = §7{0} in Design: [{1}, {2}, {3}] AUTO_CHECKER_RESULT_ENTITY = §7Entity: §7[{0}, {1}, {2}] AUTO_CHECKER_RESULT_RECORD = §7Record: §c[{0}, {1}, {2}] -AUTO_CHECKER_RESULT_TOO_MANY_DISPENSER_ITEMS = §7Dispenser: §c[{0}, {1}, {2}]§7, §c{3} §7items, Max: §e{4} +AUTO_CHECKER_RESULT_TOO_MANY_INVENTORY_ITEMS = §7{6}: §c[{0}, {1}, {2}]§7, §c{3}x {5}§7, Max: §e{4} AUTO_CHECKER_RESULT_FORBIDDEN_ITEM_NBT = §7Forbidden Item NBT: [{0}, {1}, {2}] -> §c{3} AUTO_CHECKER_RESULT_TELEPORT_HERE = §7Teleport to block AUTO_CHECKER_RESULT_AFTER_DEADLINE = §cThe deadline has expired: {0} diff --git a/SchematicSystem/src/SchematicSystem_de.properties b/SchematicSystem/src/SchematicSystem_de.properties index 466e058a..d584c602 100644 --- a/SchematicSystem/src/SchematicSystem_de.properties +++ b/SchematicSystem/src/SchematicSystem_de.properties @@ -248,7 +248,7 @@ AUTO_CHECKER_RESULT_FORBIDDEN_ITEM = §7Verbotener gegenstand: [{0}, {1}, {2}] - AUTO_CHECKER_RESULT_DEFUNCT_NBT = §7Keine NBT-Daten: §c[{0}, {1}, {2}] AUTO_CHECKER_RESULT_DESIGN_BLOCK = §7{0} im Design: [{1}, {2}, {3}] AUTO_CHECKER_RESULT_RECORD = §7Schallplatte: §c[{0}, {1}, {2}] -AUTO_CHECKER_RESULT_TOO_MANY_DISPENSER_ITEMS = §7Dispenser: §c[{0}, {1}, {2}]§7, §c{3} §7gegenstände, Max: §e{4} +AUTO_CHECKER_RESULT_TOO_MANY_INVENTORY_ITEMS = §7{6}: §c[{0}, {1}, {2}]§7, §c{3}x {5}§7, Max: §e{4} AUTO_CHECKER_RESULT_FORBIDDEN_ITEM_NBT = §7Verbotene NBT-Daten: [{0}, {1}, {2}] -> §c{3} AUTO_CHECKER_RESULT_TELEPORT_HERE = §7Zum block teleportieren AUTO_CHECKER_RESULT_AFTER_DEADLINE = §cDer einsendeschluss ist bereits vorbei: {0} diff --git a/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java b/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java index 6fa2be03..03d5e182 100644 --- a/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java +++ b/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoChecker.java @@ -75,9 +75,9 @@ public class AutoChecker { BlockPos pos = new BlockPos(x, y, z); - if (AutoCheckerItems.impl.getInventoryMaterials().contains(material)) { + if (type.Schematic.ItemsInInventory.containsKey(material)) { checkInventory(result, block, material, pos, type); - if (result.getDispenserItems().getOrDefault(pos, 0) > 0) { + if (!result.getInventoryItemCounts().getOrDefault(pos, Collections.emptyMap()).isEmpty()) { result.getBlockCounts().merge(material, 1, Integer::sum); } } else { @@ -93,19 +93,6 @@ public class AutoChecker { 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) { @@ -122,7 +109,8 @@ public class AutoChecker { List items = nbt.getList("Items", CompoundTag.class); if (items.isEmpty()) return; // Leeres Inventar - int counter = 0; + Map itemCaps = type.Schematic.ItemsInInventory.getOrDefault(material, Collections.emptyMap()); + Map itemCounts = new EnumMap<>(Material.class); int windChargeCount = 0; for (CompoundTag item : items) { if (!item.containsKey("id")) { @@ -136,16 +124,19 @@ public class AutoChecker { if (type.Schematic.Type.getName().equals("wargearseason26") && material == Material.DISPENSER && itemType == Material.WIND_CHARGE) { windChargeCount += item.getInt("count"); - } else if (!itemsInInv.getOrDefault(itemType, EnumSet.noneOf(Material.class)).contains(material)) { + } else if (!itemCaps.containsKey(itemType)) { result.getForbiddenItems().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType); - } else if (material == Material.DISPENSER && (itemType == Material.ARROW || itemType == Material.FIRE_CHARGE)) { - counter += item.getInt("count"); + } else { + itemCounts.merge(itemType, item.getInt("count"), Integer::sum); } if (item.containsKey("tag")) { result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType); } } - result.getDispenserItems().put(pos, counter); + if (!itemCounts.isEmpty()) { + result.getInventoryItemCounts().put(pos, itemCounts); + result.getInventoryBlockType().put(pos, material); + } result.getWindChargeCount().put(pos, windChargeCount); } @@ -156,7 +147,8 @@ public class AutoChecker { private final List defunctNbt = new ArrayList<>(); private final List records = new ArrayList<>(); private final Map> designBlocks = new EnumMap<>(Material.class); - private final Map dispenserItems = new HashMap<>(); + private final Map> inventoryItemCounts = new HashMap<>(); + private final Map inventoryBlockType = new HashMap<>(); private final Map windChargeCount = new HashMap<>(); private final Map> forbiddenItems = new HashMap<>(); private final Map> forbiddenNbt = new HashMap<>(); diff --git a/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerItems.java b/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerItems.java deleted file mode 100644 index 12268656..00000000 --- a/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerItems.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 AutoCheckerItems { - - public static final AutoCheckerItems impl = new 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); - - public Set getInventoryMaterials() { - return INVENTORY; - } - - public Set getAllowedMaterialsInInventory() { - return FLOWERS; - } -} diff --git a/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java b/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java index a63fb70b..090530d8 100644 --- a/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java +++ b/SchematicSystem/src/de/steamwar/schematicsystem/autocheck/AutoCheckerResult.java @@ -29,6 +29,7 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -50,7 +51,7 @@ public class AutoCheckerResult { isSizeOk() && isBlockCountOk() && isLimitedBlocksOK() && - isDispenserItemsOK() && + isInventoryItemsOK() && isWindchargeCountOK() && !type.isAfterDeadline() && entities.isEmpty() && @@ -71,8 +72,13 @@ public class AutoCheckerResult { } } - public boolean isDispenserItemsOK() { - return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems); + public boolean isInventoryItemsOK() { + return blockScanResult.getInventoryItemCounts().entrySet().stream().allMatch(posEntry -> { + Material inventory = blockScanResult.getInventoryBlockType().get(posEntry.getKey()); + Map caps = type.Schematic.ItemsInInventory.getOrDefault(inventory, Collections.emptyMap()); + return posEntry.getValue().entrySet().stream() + .allMatch(itemEntry -> itemEntry.getValue() <= caps.getOrDefault(itemEntry.getKey(), Integer.MAX_VALUE)); + }); } public boolean hasWarnings() { @@ -155,13 +161,21 @@ public class AutoCheckerResult { }); } - 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(), - blockVector3IntegerEntry.getKey().getBlockY(), - blockVector3IntegerEntry.getKey().getBlockZ(), - blockVector3IntegerEntry.getValue(), - type.Schematic.MaxDispenserItems); + blockScanResult.getInventoryItemCounts().forEach((pos, itemCounts) -> { + Material inventory = blockScanResult.getInventoryBlockType().get(pos); + Map caps = type.Schematic.ItemsInInventory.getOrDefault(inventory, Collections.emptyMap()); + itemCounts.forEach((itemType, count) -> { + int cap = caps.getOrDefault(itemType, Integer.MAX_VALUE); + if (count <= cap) return; + SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_INVENTORY_ITEMS", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(pos), + pos.getBlockX(), + pos.getBlockY(), + pos.getBlockZ(), + count, + cap, + itemType.name(), + inventory.name()); + }); }); blockScanResult.getRecords().forEach(blockVector3 -> { diff --git a/SchematicSystem/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand.java b/SchematicSystem/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand.java index 4e5ecdf7..ffd14519 100644 --- a/SchematicSystem/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand.java +++ b/SchematicSystem/src/de/steamwar/schematicsystem/commands/schematiccommand/SchematicCommand.java @@ -197,27 +197,32 @@ public class SchematicCommand extends SWCommand { clipboard.setBlock(vector, block.toBaseBlock(builder.build())); } - if (type.Schematic.MaxDispenserItems > 0) { - for (Map.Entry entry : result.getBlockScanResult().getDispenserItems().entrySet()) { - if (entry.getValue() <= type.Schematic.MaxDispenserItems) { + for (Map.Entry> posEntry : result.getBlockScanResult().getInventoryItemCounts().entrySet()) { + BlockPos pos = posEntry.getKey(); + Material inventory = result.getBlockScanResult().getInventoryBlockType().get(pos); + Map caps = type.Schematic.ItemsInInventory.getOrDefault(inventory, Collections.emptyMap()); + + for (Map.Entry itemEntry : posEntry.getValue().entrySet()) { + Material itemType = itemEntry.getKey(); + int cap = caps.getOrDefault(itemType, Integer.MAX_VALUE); + if (itemEntry.getValue() <= cap) { 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 = new ArrayList<>(tag.getList("Items", CompoundTag.class)); - Collections.reverse(items); // To let the first item be in the Dispenser + Collections.reverse(items); // To let the first item be in the inventory List list = new ArrayList<>(); - int diff = entry.getValue() - type.Schematic.MaxDispenserItems; + int diff = itemEntry.getValue() - cap; for (CompoundTag item : items) { if (item == null) { continue; } - if (diff == 0) { + if (diff == 0 || Material.matchMaterial(item.getString("id")) != itemType) { list.add(item); continue; } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SQLWrapperImpl.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SQLWrapperImpl.java index 4cb3f1c2..e8733b32 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SQLWrapperImpl.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/sql/SQLWrapperImpl.java @@ -28,7 +28,10 @@ import org.bukkit.entity.Player; import java.io.File; import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; public class SQLWrapperImpl implements SQLWrapper { @@ -52,6 +55,45 @@ public class SQLWrapperImpl implements SQLWrapper { .collect(Collectors.toList()); } + /** + * Storage-type inventory materials (chests, barrels, shulker boxes of any color). + */ + private static final Set STORAGE_MATERIALS = Collections.unmodifiableSet(EnumSet.of( + Material.CHEST, Material.TRAPPED_CHEST, Material.BARREL, 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 + )); + + /** + * Every inventory material - {@link #STORAGE_MATERIALS} plus the "functional" containers that + * are not storage (dispenser, dropper, hopper, the furnace family, brewing stand, campfire, + * jukebox). Built on top of {@link #STORAGE_MATERIALS} so anything added there is automatically + * included here. + */ + private static final Set INVENTORY_MATERIALS; + + static { + Set materials = EnumSet.copyOf(STORAGE_MATERIALS); + materials.addAll(EnumSet.of( + Material.DISPENSER, Material.DROPPER, Material.HOPPER, + Material.FURNACE, Material.BLAST_FURNACE, Material.SMOKER, + Material.BREWING_STAND, Material.CAMPFIRE, Material.JUKEBOX + )); + INVENTORY_MATERIALS = Collections.unmodifiableSet(materials); + } + + @Override + public Set getStorageMaterials() { + return STORAGE_MATERIALS; + } + + @Override + public Set getInventoryMaterials() { + return INVENTORY_MATERIALS; + } + private static final String SERVER_VERSION = Bukkit.getServer().getVersion(); @Override diff --git a/settings.gradle.kts b/settings.gradle.kts index bbb758e9..63ec80e5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,6 +21,12 @@ import org.apache.tools.ant.taskdefs.condition.Os import java.net.URI import java.util.* +pluginManagement { + plugins { + kotlin("plugin.lombok") version "2.3.20" + } +} + rootProject.name = "SteamWar" private val isInCi by lazy { Os.isFamily(Os.FAMILY_UNIX) && ProcessBuilder("hostname").start().inputStream.bufferedReader().readText().startsWith("steamwar.de") }