Fix AutoChecker inventory rules

Count every inventory block towards exactly one Schematic.Limited rule:
filled blocks towards a rule whose Content allows all their items, empty
blocks towards a rule without Content (unlimited if there is none). Each
rule's Amount now caps how many blocks it covers, instead of taking the
highest Amount of all rules for that block type.

Inventory blocks whose items fit no rule are now checked and reported,
items from different rules in one block are reported as mixed, and the
auto-fix trims or removes them accordingly.

Remove the hardcoded wargearseason26 wind charge handling and its 2048
limit, wind charges are configured via Limited rules now.

Log a warning for Limited rules that match no blocks, e.g. a misspelled
'Materials' key.

Fixes #458, #481, #502
This commit is contained in:
2026-10-15 17:53:06 +02:00
parent 2b42f0d13c
commit d727982ce0
7 changed files with 123 additions and 83 deletions
@@ -19,6 +19,7 @@
package de.steamwar.sql; package de.steamwar.sql;
import de.steamwar.sql.internal.SQLConfig;
import lombok.ToString; import lombok.ToString;
import java.io.File; import java.io.File;
@@ -250,6 +251,11 @@ public final class GameModeConfig<M, W> {
Times = new TimesConfig(loader.with("Times")); Times = new TimesConfig(loader.with("Times"));
// Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards // Arena would be here to be in config order but needs Schematic.Size and EnterStages loaded afterwards
Schematic = new SchematicConfig<>(loader.with("Schematic")); Schematic = new SchematicConfig<>(loader.with("Schematic"));
for (int i = 0; i < Schematic.Limited.size(); i++) {
if (Schematic.Limited.get(i).Materials.isEmpty()) {
SQLConfig.impl.getLogger().warning(file.getName() + ": Schematic.Limited[" + i + "] matches no blocks, check its 'Materials' entry");
}
}
GameName = loader.getString("GameName", defaultGameName.apply(file)); GameName = loader.getString("GameName", defaultGameName.apply(file));
ActiveMonths = loader.getIntList("ActiveMonths"); ActiveMonths = loader.getIntList("ActiveMonths");
TeamChatPrefix = loader.getString("TeamChatPrefix", "+"); TeamChatPrefix = loader.getString("TeamChatPrefix", "+");
@@ -703,7 +709,10 @@ public final class GameModeConfig<M, W> {
public final double MaxDesignBlastResistance; public final double MaxDesignBlastResistance;
/** /**
* List of limited block materials * List of limited block materials<br/>
* Every inventory block is counted towards exactly one rule: a filled block towards a rule whose
* {@link BlockRule#Content} allows all of its items, an empty block towards a rule without
* {@link BlockRule#Content}. Empty inventory blocks without such a rule are unlimited.
* *
* @implSpec {@link SQLWrapper#getInventoryMaterials)} are all disallowed by default to have any content but are unlimited in number. * @implSpec {@link SQLWrapper#getInventoryMaterials)} are all disallowed by default to have any content but are unlimited in number.
* @implSpec Any Block that has more Blast Resistance than {@link #MaxBlastResistance} will be defaulted to 0. * @implSpec Any Block that has more Blast Resistance than {@link #MaxBlastResistance} will be defaulted to 0.
@@ -759,18 +768,13 @@ public final class GameModeConfig<M, W> {
} }
public boolean isInventory(M material) { public boolean isInventory(M material) {
return Limited.stream() return SQLWrapper.impl.getInventoryMaterials().contains(material) || Limited.stream().anyMatch(rule -> rule.Content.loaded && rule.Materials.contains(material));
.filter(rule -> rule.Materials.contains(material))
.anyMatch(rule -> rule.Content.loaded);
} }
public Integer getItemAmount(M material, M item) { public List<BlockRule<M>> getRules(M material) {
return Limited.stream() return Limited.stream()
.filter(rule -> rule.Materials.contains(material)) .filter(rule -> rule.Materials.contains(material))
.filter(rule -> rule.Content.Items.contains(item)) .collect(Collectors.toList());
.mapToInt(rule -> rule.Content.Amount)
.max()
.orElse(0);
} }
@ToString @ToString
@@ -46,7 +46,7 @@ public interface SQLWrapper<M> {
} }
/** /**
* Every inventory material - the {@code all} group usable in {@code GameModeConfig}'s * Every inventory material - the {@code inventory} group usable in {@code GameModeConfig}'s
* {@code Limited}. Expected to be a superset of {@link #getStorageMaterials()}. * {@code Limited}. Expected to be a superset of {@link #getStorageMaterials()}.
*/ */
default Set<M> getInventoryMaterials() { default Set<M> getInventoryMaterials() {
@@ -262,9 +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_UNKNOWN_MATERIAL = §7Unknown block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK = §7{0}: §c{1}§7, Max: §e{2} 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_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_FORBIDDEN_ITEM = §7Forbidden Item: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_MIXED_ITEMS = §7Items of different rules in one inventory: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT = §7Defunct NBT: §7[{0}, {1}, {2}] 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_DESIGN_BLOCK = §7{0} in Design: [{1}, {2}, {3}]
AUTO_CHECKER_RESULT_ENTITY = §7Entity: §7[{0}, {1}, {2}] AUTO_CHECKER_RESULT_ENTITY = §7Entity: §7[{0}, {1}, {2}]
@@ -242,9 +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_UNKNOWN_MATERIAL = §7Unbekannter Block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK = §7{0}: §c{1}§7, Max: §e{2} 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_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_FORBIDDEN_ITEM = §7Verbotener gegenstand: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_MIXED_ITEMS = §7Gegenstände verschiedener Regeln in einem Inventar: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT = §7Keine NBT-Daten: §c[{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_DESIGN_BLOCK = §7{0} im Design: [{1}, {2}, {3}]
AUTO_CHECKER_RESULT_RECORD = §7Schallplatte: §c[{0}, {1}, {2}] AUTO_CHECKER_RESULT_RECORD = §7Schallplatte: §c[{0}, {1}, {2}]
@@ -25,6 +25,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BaseBlock;
import de.steamwar.sql.GameModeConfig; import de.steamwar.sql.GameModeConfig;
import de.steamwar.sql.GameModeConfig.SchematicConfig.BlockRule;
import lombok.Getter; import lombok.Getter;
import lombok.ToString; import lombok.ToString;
import org.bukkit.Material; import org.bukkit.Material;
@@ -62,6 +63,7 @@ public class AutoChecker {
public AutoChecker.BlockScanResult scan(Clipboard clipboard, GameModeConfig<Material, String> type) { public AutoChecker.BlockScanResult scan(Clipboard clipboard, GameModeConfig<Material, String> type) {
AutoChecker.BlockScanResult result = new AutoChecker.BlockScanResult(); AutoChecker.BlockScanResult result = new AutoChecker.BlockScanResult();
Map<BlockPos, List<BlockRule<Material>>> ruleCandidates = new HashMap<>();
BlockVector3 min = clipboard.getMinimumPoint(); BlockVector3 min = clipboard.getMinimumPoint();
BlockVector3 max = clipboard.getMaximumPoint(); BlockVector3 max = clipboard.getMaximumPoint();
for (int x = min.x(); x <= max.x(); x++) { for (int x = min.x(); x <= max.x(); x++) {
@@ -75,13 +77,13 @@ public class AutoChecker {
BlockPos pos = new BlockPos(x, y, z); BlockPos pos = new BlockPos(x, y, z);
result.getBlockCounts().merge(material, 1, Integer::sum);
if (type.Schematic.isInventory(material)) { if (type.Schematic.isInventory(material)) {
checkInventory(result, block, material, pos, type); result.getInventoryBlockType().put(pos, material);
if (!result.getInventoryItemCounts().getOrDefault(pos, Collections.emptyMap()).isEmpty()) { List<BlockRule<Material>> candidates = checkInventory(result, block, material, pos, type);
result.getBlockCounts().merge(material, 1, Integer::sum); if (!candidates.isEmpty()) {
ruleCandidates.put(pos, candidates);
} }
} else {
result.getBlockCounts().merge(material, 1, Integer::sum);
} }
if (x == min.x() || x == max.x() || y == max.y() || z == min.z() || z == max.z()) { if (x == min.x() || x == max.x() || y == max.y() || z == min.z() || z == max.z()) {
@@ -90,28 +92,28 @@ public class AutoChecker {
} }
} }
} }
assignRules(result, ruleCandidates);
return result; return result;
} }
private void checkInventory(AutoChecker.BlockScanResult result, BaseBlock block, Material material, BlockPos pos, GameModeConfig<Material, String> type) { /**
* Scans the inventory and returns the rules this block may be counted towards.
* An empty list means the block is either faulty (reported in the result) or unlimited.
*/
private List<BlockRule<Material>> checkInventory(AutoChecker.BlockScanResult result, BaseBlock block, Material material, BlockPos pos, GameModeConfig<Material, String> type) {
CompoundTag nbt = block.getNbtData(); CompoundTag nbt = block.getNbtData();
if (nbt == null) { if (nbt == null) {
result.getDefunctNbt().add(pos); result.getDefunctNbt().add(pos);
return; return Collections.emptyList();
} }
if (material == Material.JUKEBOX && nbt.getValue().containsKey("RecordItem")) { if (material == Material.JUKEBOX && nbt.getValue().containsKey("RecordItem")) {
result.getRecords().add(pos); result.getRecords().add(pos);
return; return Collections.emptyList();
} }
List<CompoundTag> items = nbt.getList("Items", CompoundTag.class);
if (items.isEmpty()) return; // Leeres Inventar
Map<Material, Integer> itemCounts = new EnumMap<>(Material.class); Map<Material, Integer> itemCounts = new EnumMap<>(Material.class);
int windChargeCount = 0; for (CompoundTag item : nbt.getList("Items", CompoundTag.class)) {
for (CompoundTag item : items) {
if (!item.containsKey("id")) { if (!item.containsKey("id")) {
result.getDefunctNbt().add(pos); result.getDefunctNbt().add(pos);
continue; continue;
@@ -121,22 +123,69 @@ public class AutoChecker {
if (itemType == null) // Leere Slots if (itemType == null) // Leere Slots
continue; continue;
if (type.Schematic.Type.getName().equals("wargearseason26") && material == Material.DISPENSER && itemType == Material.WIND_CHARGE) { itemCounts.merge(itemType, item.getInt("count"), Integer::sum);
windChargeCount += item.getInt("count");
} else if (type.Schematic.getItemAmount(material, itemType) == 0) {
result.getForbiddenItems().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
} else {
itemCounts.merge(itemType, item.getInt("count"), Integer::sum);
}
if (item.containsKey("tag")) { if (item.containsKey("tag")) {
result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType); result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
} }
} }
if (!itemCounts.isEmpty()) {
result.getInventoryItemCounts().put(pos, itemCounts); List<BlockRule<Material>> rules = type.Schematic.getRules(material);
result.getInventoryBlockType().put(pos, material); if (itemCounts.isEmpty()) { // Leeres Inventar
return rules.stream().filter(rule -> !rule.Content.loaded).collect(Collectors.toList());
} }
result.getWindChargeCount().put(pos, windChargeCount); result.getInventoryItemCounts().put(pos, itemCounts);
List<BlockRule<Material>> contentRules = rules.stream().filter(rule -> rule.Content.loaded).collect(Collectors.toList());
List<BlockRule<Material>> itemsFitting = contentRules.stream()
.filter(rule -> rule.Content.Items.containsAll(itemCounts.keySet()))
.collect(Collectors.toList());
List<BlockRule<Material>> fitting = itemsFitting.stream()
.filter(rule -> itemCounts.values().stream().allMatch(count -> count <= rule.Content.Amount))
.collect(Collectors.toList());
if (!fitting.isEmpty()) {
return fitting;
}
if (!itemsFitting.isEmpty()) {
BlockRule<Material> best = Collections.max(itemsFitting, Comparator.comparingInt(rule -> rule.Content.Amount));
itemCounts.forEach((itemType, count) -> {
if (count > best.Content.Amount) {
result.getTooManyItems().computeIfAbsent(pos, blockVector3 -> new EnumMap<>(Material.class)).put(itemType, best.Content.Amount);
}
});
return Collections.singletonList(best);
}
Set<Material> allowed = contentRules.stream().flatMap(rule -> rule.Content.Items.stream()).collect(Collectors.toSet());
Set<Material> forbidden = itemCounts.keySet().stream().filter(itemType -> !allowed.contains(itemType)).collect(Collectors.toSet());
if (!forbidden.isEmpty()) {
result.getForbiddenItems().put(pos, forbidden);
}
Set<Material> rest = itemCounts.keySet().stream().filter(allowed::contains).collect(Collectors.toSet());
if (!rest.isEmpty()) {
// Every remaining item is allowed on its own, but no single rule allows all of them together
BlockRule<Material> best = Collections.max(contentRules, Comparator.comparingLong(rule -> rest.stream().filter(rule.Content.Items::contains).count()));
Set<Material> mixed = rest.stream().filter(itemType -> !best.Content.Items.contains(itemType)).collect(Collectors.toSet());
if (!mixed.isEmpty()) {
result.getMixedItems().put(pos, mixed);
}
}
return Collections.emptyList();
}
/**
* Counts every block towards one of its candidate rules, preferring the rule with the most capacity left.
* Blocks with fewer candidates are assigned first.
*/
private void assignRules(AutoChecker.BlockScanResult result, Map<BlockPos, List<BlockRule<Material>>> ruleCandidates) {
ruleCandidates.entrySet().stream()
.sorted(Comparator.comparingInt(entry -> entry.getValue().size()))
.forEach(entry -> {
BlockRule<Material> rule = Collections.max(entry.getValue(), Comparator.comparingLong(candidate -> (long) candidate.Amount - result.getRuleCounts().getOrDefault(candidate, 0)));
result.getRuleCounts().merge(rule, 1, Integer::sum);
result.getRuleBlockTypes().computeIfAbsent(rule, r -> EnumSet.noneOf(Material.class)).add(result.getInventoryBlockType().get(entry.getKey()));
});
} }
@Getter @Getter
@@ -148,8 +197,11 @@ public class AutoChecker {
private final Map<Material, List<BlockPos>> designBlocks = new EnumMap<>(Material.class); private final Map<Material, List<BlockPos>> designBlocks = new EnumMap<>(Material.class);
private final Map<BlockPos, Map<Material, Integer>> inventoryItemCounts = new HashMap<>(); private final Map<BlockPos, Map<Material, Integer>> inventoryItemCounts = new HashMap<>();
private final Map<BlockPos, Material> inventoryBlockType = new HashMap<>(); private final Map<BlockPos, Material> inventoryBlockType = new HashMap<>();
private final Map<BlockPos, Integer> windChargeCount = new HashMap<>(); private final Map<BlockRule<Material>, Integer> ruleCounts = new HashMap<>();
private final Map<BlockRule<Material>, Set<Material>> ruleBlockTypes = new HashMap<>();
private final Map<BlockPos, Map<Material, Integer>> tooManyItems = new HashMap<>();
private final Map<BlockPos, Set<Material>> forbiddenItems = new HashMap<>(); private final Map<BlockPos, Set<Material>> forbiddenItems = new HashMap<>();
private final Map<BlockPos, Set<Material>> mixedItems = new HashMap<>();
private final Map<BlockPos, Set<Material>> forbiddenNbt = new HashMap<>(); private final Map<BlockPos, Set<Material>> forbiddenNbt = new HashMap<>();
} }
} }
@@ -31,6 +31,7 @@ import org.bukkit.entity.Player;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
@Builder @Builder
@Getter @Getter
@@ -46,12 +47,12 @@ public class AutoCheckerResult {
public boolean isOk() { public boolean isOk() {
return blockScanResult.getRecords().isEmpty() && return blockScanResult.getRecords().isEmpty() &&
blockScanResult.getForbiddenItems().isEmpty() && blockScanResult.getForbiddenItems().isEmpty() &&
blockScanResult.getMixedItems().isEmpty() &&
blockScanResult.getForbiddenNbt().isEmpty() && blockScanResult.getForbiddenNbt().isEmpty() &&
isSizeOk() && isSizeOk() &&
isBlockCountOk() && isBlockCountOk() &&
isLimitedBlocksOK() && isLimitedBlocksOK() &&
isInventoryItemsOK() && isInventoryItemsOK() &&
isWindchargeCountOK() &&
!type.isAfterDeadline() && !type.isAfterDeadline() &&
entities.isEmpty() && entities.isEmpty() &&
isDesignBlastResistanceOK(); isDesignBlastResistanceOK();
@@ -62,23 +63,8 @@ public class AutoCheckerResult {
!type.isAfterDeadline(); !type.isAfterDeadline();
} }
public boolean isWindchargeCountOK() {
if (type.Schematic.Type.getName().equals("wargearseason26")) {
int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
return windChargesCount <= 2048;
} else {
return true;
}
}
public boolean isInventoryItemsOK() { public boolean isInventoryItemsOK() {
return blockScanResult.getInventoryItemCounts().entrySet().stream().allMatch(posEntry -> { return blockScanResult.getTooManyItems().isEmpty();
Material inventory = blockScanResult.getInventoryBlockType().get(posEntry.getKey());
return posEntry.getValue().entrySet().stream().allMatch(itemEntry -> {
int cap = type.Schematic.getItemAmount(inventory, itemEntry.getKey());
return itemEntry.getValue() <= cap;
});
});
} }
public boolean hasWarnings() { public boolean hasWarnings() {
@@ -107,7 +93,10 @@ public class AutoCheckerResult {
public boolean isLimitedBlocksOK() { public boolean isLimitedBlocksOK() {
return blockScanResult.getBlockCounts().entrySet().stream() return blockScanResult.getBlockCounts().entrySet().stream()
.allMatch(entry -> entry.getValue() <= type.Schematic.getMaxCount(entry.getKey())); .filter(entry -> !type.Schematic.isInventory(entry.getKey()))
.allMatch(entry -> entry.getValue() <= type.Schematic.getMaxCount(entry.getKey())) &&
blockScanResult.getRuleCounts().entrySet().stream()
.allMatch(entry -> entry.getValue() <= entry.getKey().Amount);
} }
public boolean isDesignBlastResistanceOK() { public boolean isDesignBlastResistanceOK() {
@@ -132,6 +121,7 @@ public class AutoCheckerResult {
} }
if (!isLimitedBlocksOK()) { if (!isLimitedBlocksOK()) {
blockScanResult.getBlockCounts().forEach((mat, count) -> { blockScanResult.getBlockCounts().forEach((mat, count) -> {
if (type.Schematic.isInventory(mat)) return;
int maxCount = type.Schematic.getMaxCount(mat); int maxCount = type.Schematic.getMaxCount(mat);
if (count > maxCount) { if (count > maxCount) {
if (maxCount == 0) { if (maxCount == 0) {
@@ -141,30 +131,25 @@ public class AutoCheckerResult {
} }
} }
}); });
} blockScanResult.getRuleCounts().forEach((rule, count) -> {
if (count <= rule.Amount) return;
if (!isWindchargeCountOK()) { String blocks = blockScanResult.getRuleBlockTypes().get(rule).stream().map(Material::name).collect(Collectors.joining(", "));
int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0); if (rule.Amount == 0) {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES", p, windChargesCount, 2048); SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK", p, blocks);
blockScanResult.getWindChargeCount().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > 0).forEach(blockVector3IntegerEntry -> { } else {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()), SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_BLOCK", p, blocks, count, rule.Amount);
blockVector3IntegerEntry.getKey().getBlockX(), }
blockVector3IntegerEntry.getKey().getBlockY(),
blockVector3IntegerEntry.getKey().getBlockZ(),
blockVector3IntegerEntry.getValue());
}); });
} }
blockScanResult.getInventoryItemCounts().forEach((pos, itemCounts) -> { blockScanResult.getTooManyItems().forEach((pos, caps) -> {
Material inventory = blockScanResult.getInventoryBlockType().get(pos); Material inventory = blockScanResult.getInventoryBlockType().get(pos);
itemCounts.forEach((itemType, count) -> { caps.forEach((itemType, cap) -> {
int cap = type.Schematic.getItemAmount(inventory, itemType);
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), 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.getBlockX(),
pos.getBlockY(), pos.getBlockY(),
pos.getBlockZ(), pos.getBlockZ(),
count, blockScanResult.getInventoryItemCounts().get(pos).get(itemType),
cap, cap,
itemType.name(), itemType.name(),
inventory.name()); inventory.name());
@@ -177,6 +162,9 @@ public class AutoCheckerResult {
blockScanResult.getForbiddenItems().forEach((blockVector3, materials) -> { blockScanResult.getForbiddenItems().forEach((blockVector3, materials) -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_FORBIDDEN_ITEM", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), setToString(materials)); SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_FORBIDDEN_ITEM", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), setToString(materials));
}); });
blockScanResult.getMixedItems().forEach((blockVector3, materials) -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_MIXED_ITEMS", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), setToString(materials));
});
blockScanResult.getForbiddenNbt().forEach((blockVector3, materials) -> { blockScanResult.getForbiddenNbt().forEach((blockVector3, materials) -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_FORBIDDEN_ITEM_NBT", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), setToString(materials)); SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_FORBIDDEN_ITEM_NBT", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getX(), blockVector3.getY(), blockVector3.getZ(), setToString(materials));
}); });
@@ -48,6 +48,7 @@ import java.util.*;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandHelp.*; import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandHelp.*;
import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils.togglePublic; import static de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommandUtils.togglePublic;
@@ -171,8 +172,8 @@ public class SchematicCommand extends SWCommand {
Map<BlockPos, Set<Material>> toBeCheckedInvs = new HashMap<>(); Map<BlockPos, Set<Material>> toBeCheckedInvs = new HashMap<>();
toBeCheckedInvs.putAll(result.getBlockScanResult().getForbiddenItems()); Stream.of(result.getBlockScanResult().getForbiddenItems(), result.getBlockScanResult().getMixedItems(), result.getBlockScanResult().getForbiddenNbt())
toBeCheckedInvs.putAll(result.getBlockScanResult().getForbiddenNbt()); .forEach(invs -> invs.forEach((pos, materials) -> toBeCheckedInvs.computeIfAbsent(pos, p -> new HashSet<>()).addAll(materials)));
for (Map.Entry<BlockPos, Set<Material>> entry : toBeCheckedInvs.entrySet()) { for (Map.Entry<BlockPos, Set<Material>> entry : toBeCheckedInvs.entrySet()) {
BlockPos pos = entry.getKey(); BlockPos pos = entry.getKey();
@@ -197,16 +198,13 @@ public class SchematicCommand extends SWCommand {
clipboard.setBlock(vector, block.toBaseBlock(builder.build())); clipboard.setBlock(vector, block.toBaseBlock(builder.build()));
} }
for (Map.Entry<BlockPos, Map<Material, Integer>> posEntry : result.getBlockScanResult().getInventoryItemCounts().entrySet()) { for (Map.Entry<BlockPos, Map<Material, Integer>> posEntry : result.getBlockScanResult().getTooManyItems().entrySet()) {
BlockPos pos = posEntry.getKey(); BlockPos pos = posEntry.getKey();
Material inventory = result.getBlockScanResult().getInventoryBlockType().get(pos); Map<Material, Integer> itemCounts = result.getBlockScanResult().getInventoryItemCounts().get(pos);
for (Map.Entry<Material, Integer> itemEntry : posEntry.getValue().entrySet()) { for (Map.Entry<Material, Integer> itemEntry : posEntry.getValue().entrySet()) {
Material itemType = itemEntry.getKey(); Material itemType = itemEntry.getKey();
Integer cap = type.Schematic.getItemAmount(inventory, itemType); int cap = itemEntry.getValue();
if (itemEntry.getValue() <= cap) {
continue;
}
BlockVector3 vector = BlockVector3.at(pos.getX(), pos.getY(), pos.getZ()); BlockVector3 vector = BlockVector3.at(pos.getX(), pos.getY(), pos.getZ());
BaseBlock block = clipboard.getFullBlock(vector); BaseBlock block = clipboard.getFullBlock(vector);
@@ -215,7 +213,7 @@ public class SchematicCommand extends SWCommand {
List<CompoundTag> items = new ArrayList<>(tag.getList("Items", CompoundTag.class)); List<CompoundTag> items = new ArrayList<>(tag.getList("Items", CompoundTag.class));
Collections.reverse(items); // To let the first item be in the inventory Collections.reverse(items); // To let the first item be in the inventory
List<CompoundTag> list = new ArrayList<>(); List<CompoundTag> list = new ArrayList<>();
int diff = itemEntry.getValue() - cap; int diff = itemCounts.get(itemType) - cap;
for (CompoundTag item : items) { for (CompoundTag item : items) {
if (item == null) { if (item == null) {
continue; continue;