package com.hypixel.hytale.builtin.blockphysics; import com.hypixel.hytale.common.util.ExceptionUtil; import com.hypixel.hytale.component.ComponentType; import com.hypixel.hytale.component.Holder; import com.hypixel.hytale.component.data.unknown.UnknownComponents; import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType; import com.hypixel.hytale.server.core.prefab.PrefabStore; import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferUtil; import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer; import com.hypixel.hytale.server.core.universe.world.ValidationOption; import com.hypixel.hytale.server.core.universe.world.storage.EntityStore; import com.hypixel.hytale.server.core.util.FillerBlockUtil; import com.hypixel.hytale.server.core.util.io.FileUtil; import com.hypixel.hytale.sneakythrow.SneakyThrow; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class PrefabBufferValidator { private static final FillerBlockUtil.FillerFetcher FILLER_FETCHER = new FillerBlockUtil.FillerFetcher() { public int getBlock(IPrefabBuffer iPrefabBuffer, Void unused, int x, int y, int z) { return iPrefabBuffer.getBlockId(x, y, z); } public int getFiller(IPrefabBuffer iPrefabBuffer, Void unused, int x, int y, int z) { return iPrefabBuffer.getFiller(x, y, z); } public int getRotationIndex(IPrefabBuffer iPrefabBuffer, Void unused, int x, int y, int z) { return iPrefabBuffer.getRotationIndex(x, y, z); } }; @Nonnull public static List validateAllPrefabs(@Nonnull List list) { Set options = !list.isEmpty() ? EnumSet.copyOf(list) : EnumSet.of(ValidationOption.BLOCK_STATES, ValidationOption.ENTITIES, ValidationOption.BLOCKS, ValidationOption.BLOCK_FILLER); List out = validatePrefabsInPath(PrefabStore.get().getWorldGenPrefabsPath(), options); out.addAll(validatePrefabsInPath(PrefabStore.get().getAssetPrefabsPath(), options)); out.addAll(validatePrefabsInPath(PrefabStore.get().getServerPrefabsPath(), options)); return out; } @Nonnull public static List validatePrefabsInPath(@Nonnull Path dataFolder, @Nonnull Set options) { if (!Files.exists(dataFolder)) { return new ArrayList<>(); } else { try { List var3; try (Stream stream = Files.walk(dataFolder, FileUtil.DEFAULT_WALK_TREE_OPTIONS_ARRAY)) { var3 = stream.map(path -> { if (Files.isRegularFile(path) && path.toString().endsWith(".prefab.json")) { try { IPrefabBuffer prefab = PrefabBufferUtil.getCached(path); String var4; try { String results = validate(prefab, options); var4 = results != null ? path + "\n" + results : null; } finally { prefab.release(); } return var4; } catch (Throwable var9) { return path + "\n\t" + ExceptionUtil.combineMessages(var9, "\n\t"); } } else { return null; } }).filter(Objects::nonNull).collect(Collectors.toList()); } return var3; } catch (IOException var7) { throw SneakyThrow.sneakyThrow(var7); } } } @Nullable public static String validate(@Nonnull IPrefabBuffer prefab, @Nonnull Set options) { ComponentType> unknownComponentType = EntityStore.REGISTRY.getUnknownComponentType(); StringBuilder sb = new StringBuilder(); int offsetX = prefab.getAnchorX(); int offsetY = prefab.getAnchorY(); int offsetZ = prefab.getAnchorZ(); IPrefabBuffer.RawBlockConsumer legacyValidator = WorldValidationUtil.blockValidator(offsetX, offsetY, offsetZ, sb, options); prefab.forEachRaw( IPrefabBuffer.iterateAllColumns(), (x, y, z, mask, blockId, chance, holder, supportValue, rotation, filler, o) -> { legacyValidator.accept(x, y, z, mask, blockId, chance, holder, supportValue, rotation, filler, o); if (options.contains(ValidationOption.BLOCK_FILLER)) { FillerBlockUtil.ValidationResult fillerResult = FillerBlockUtil.validateBlock(x, y, z, blockId, rotation, filler, prefab, null, FILLER_FETCHER); switch (fillerResult) { case OK: default: break; case INVALID_BLOCK: { BlockType blockType = BlockType.getAssetMap().getAsset(blockId); sb.append("\tBlock ") .append(blockType != null ? blockType.getId() : "") .append(" at ") .append(x) .append(", ") .append(y) .append(", ") .append(z) .append(" is not valid filler") .append('\n'); break; } case INVALID_FILLER: { BlockType blockType = BlockType.getAssetMap().getAsset(blockId); sb.append("\tBlock ") .append(blockType != null ? blockType.getId() : "") .append(" at ") .append(x) .append(", ") .append(y) .append(", ") .append(z) .append(" has invalid/missing filler blocks") .append('\n'); } } } }, (x, y, z, fluidId, level, unused) -> {}, (x, z, holders, o) -> { if (holders != null) { if (options.contains(ValidationOption.ENTITIES)) { for (Holder entityHolder : holders) { UnknownComponents unknownComponents = entityHolder.getComponent(unknownComponentType); if (unknownComponents != null && !unknownComponents.getUnknownComponents().isEmpty()) { sb.append("\tUnknown Entity Components: ").append(unknownComponents.getUnknownComponents()).append("\n"); } } } } }, (Void)null ); return !sb.isEmpty() ? sb.toString() : null; } }