diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..81ca98a28 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Force LF for test files +**/src/test/resources/** text eol=lf diff --git a/.gitignore b/.gitignore index 9b484d50d..712b9cbb4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,55 @@ -.metadata -bin/ +# ---- OS junk ---- +.DS_Store +Thumbs.db + +# ---- Maven / Java ---- target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +.flattened-pom.xml +# JVM crash logs +hs_err_pid* +replay_pid* + +# If you're on the newer Maven Wrapper that downloads the jar, ignore it. +# Otherwise, REMOVE this line so the jar is committed. +.mvn/wrapper/maven-wrapper.jar + +# ---- Eclipse / STS ---- +.project +.classpath +.settings/ +.metadata/ +.recommenders/ +.loadpath +.externalToolBuilders/ +*.launch +.factorypath +# Common generated/temporary +bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties -.settings/ -.loadpath -.recommenders -# Eclipse Core -.project - -# External tool builders -.externalToolBuilders/ - -# Locally stored "Eclipse launch configurations" -*.launch - -# JDT-specific (Eclipse Java Development Tools) -.classpath - -# Java annotation processor (APT) -.factorypath - -# sbteclipse plugin -.target - -# Code Recommenders -.recommenders/ -/target/ - -#Logs -*.log -.test - -# Maven -dependency-reduced-pom.xml -.flattened-pom.xml \ No newline at end of file +# ---- IntelliJ IDEA ---- +.idea/ +*.iml +*.iws +out/ +# IntelliJ shelves & local tasks +.idea/shelf/ +.idea/workspace.xml +.idea/tasks.xml +.idea/usage.statistics.xml +.idea/dictionaries/ +.idea/libraries/ + +# ---- VS Code (optional) ---- +.vscode/ diff --git a/orebfuscator-common/pom.xml b/orebfuscator-common/pom.xml deleted file mode 100644 index cac0c616d..000000000 --- a/orebfuscator-common/pom.xml +++ /dev/null @@ -1,29 +0,0 @@ - - 4.0.0 - - - net.imprex - orebfuscator - ${revision} - - - orebfuscator-common - jar - - - - org.spigotmc - spigot-api - ${dependency.bukkit.version} - provided - - - org.joml - joml - ${dependency.joml.version} - compile - - - \ No newline at end of file diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/AdvancedConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/AdvancedConfig.java deleted file mode 100644 index 1be781884..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/AdvancedConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.imprex.orebfuscator.config; - -public interface AdvancedConfig { - - int obfuscationThreads(); - - boolean hasObfuscationTimeout(); - - long obfuscationTimeout(); - - int maxMillisecondsPerTick(); - - int proximityThreads(); - - int proximityDefaultBucketSize(); - - int proximityThreadCheckInterval(); - - boolean hasProximityPlayerCheckInterval(); - - int proximityPlayerCheckInterval(); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/BlockFlags.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/BlockFlags.java deleted file mode 100644 index f1b594f9b..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/BlockFlags.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.imprex.orebfuscator.config; - -public interface BlockFlags { - - public static final int FLAG_OBFUSCATE = 1; - public static final int FLAG_BLOCK_ENTITY = 2; - public static final int FLAG_PROXIMITY = 4; - public static final int FLAG_USE_BLOCK_BELOW = 8; - public static final int FLAG_ALLOW_FOR_USE_BLOCK_BELOW = 16; - - public static boolean isEmpty(int mask) { - return (mask & 0xFF) == 0; - } - - public static boolean isBitSet(int mask, int flag) { - return (mask & flag) != 0; - } - - public static boolean isObfuscateBitSet(int mask) { - return isBitSet(mask, FLAG_OBFUSCATE); - } - - public static boolean isBlockEntityBitSet(int mask) { - return isBitSet(mask, FLAG_BLOCK_ENTITY); - } - - public static boolean isProximityBitSet(int mask) { - return isBitSet(mask, FLAG_PROXIMITY); - } - - public static boolean isUseBlockBelowBitSet(int mask) { - return isBitSet(mask, FLAG_USE_BLOCK_BELOW); - } - - public static boolean isAllowForUseBlockBelowBitSet(int mask) { - return isBitSet(mask, FLAG_ALLOW_FOR_USE_BLOCK_BELOW); - } - - int flags(int blockState); - - int flags(int blockState, int y); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java deleted file mode 100644 index 711ce541c..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/CacheConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.nio.file.Path; - -import net.imprex.orebfuscator.util.ChunkPosition; - -public interface CacheConfig { - - boolean enabled(); - - int maximumSize(); - - long expireAfterAccess(); - - boolean enableDiskCache(); - - Path baseDirectory(); - - Path regionFile(ChunkPosition chunkPosition); - - int maximumOpenRegionFiles(); - - long deleteRegionFilesAfterAccess(); - - int maximumTaskQueueSize(); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/Config.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/Config.java deleted file mode 100644 index 9821ece5e..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/Config.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.imprex.orebfuscator.config; - -import org.bukkit.World; - -public interface Config { - - byte[] systemHash(); - - String report(); - - GeneralConfig general(); - - AdvancedConfig advanced(); - - CacheConfig cache(); - - WorldConfigBundle world(World world); - - boolean proximityEnabled(); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/GeneralConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/GeneralConfig.java deleted file mode 100644 index 327679466..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/GeneralConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.imprex.orebfuscator.config; - -public interface GeneralConfig { - - boolean checkForUpdates(); - - boolean updateOnBlockDamage(); - - boolean bypassNotification(); - - boolean ignoreSpectator(); - - int updateRadius(); -} \ No newline at end of file diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/ObfuscationConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/ObfuscationConfig.java deleted file mode 100644 index 459c72a56..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/ObfuscationConfig.java +++ /dev/null @@ -1,10 +0,0 @@ -package net.imprex.orebfuscator.config; - -import net.imprex.orebfuscator.util.BlockProperties; - -public interface ObfuscationConfig extends WorldConfig { - - boolean layerObfuscation(); - - Iterable hiddenBlocks(); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/ProximityConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/ProximityConfig.java deleted file mode 100644 index 97364845b..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/ProximityConfig.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.util.Map; - -import org.joml.Matrix4f; - -import net.imprex.orebfuscator.util.BlockProperties; - -public interface ProximityConfig extends WorldConfig { - - int distance(); - - boolean frustumCullingEnabled(); - - float frustumCullingMinDistanceSquared(); - - Matrix4f frustumCullingProjectionMatrix(); - - boolean rayCastCheckEnabled(); - - boolean rayCastCheckOnlyCheckCenter(); - - Iterable> hiddenBlocks(); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/WorldConfig.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/WorldConfig.java deleted file mode 100644 index bacb32b58..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/WorldConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.imprex.orebfuscator.config; - -public interface WorldConfig { - - boolean isEnabled(); - - int getMinY(); - - int getMaxY(); - - boolean matchesWorldName(String worldName); - - boolean shouldObfuscate(int y); - -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/WorldConfigBundle.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/WorldConfigBundle.java deleted file mode 100644 index c13cad552..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/config/WorldConfigBundle.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.imprex.orebfuscator.config; - -public interface WorldConfigBundle { - - BlockFlags blockFlags(); - - ObfuscationConfig obfuscation(); - - ProximityConfig proximity(); - - boolean needsObfuscation(); - - boolean skipReadSectionIndex(int index); - - boolean skipProcessingSectionIndex(int index); - - int minSectionIndex(); - - int maxSectionIndex(); - - boolean shouldObfuscate(int y); - - int nextRandomObfuscationBlock(int y); - - int nextRandomProximityBlock(int y); -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockPos.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockPos.java deleted file mode 100644 index 82e95937b..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockPos.java +++ /dev/null @@ -1,117 +0,0 @@ -package net.imprex.orebfuscator.util; - -import org.bukkit.World; - -public class BlockPos implements Comparable { - - // from net.minecraft.core.BlockPos - private static final int BITS_PER_X = 26; - private static final int BITS_PER_Z = BITS_PER_X; - private static final int BITS_PER_Y = 64 - BITS_PER_X - BITS_PER_Z; - - private static final int OFFSET_Y = 0; - private static final int OFFSET_Z = OFFSET_Y + BITS_PER_Y; - private static final int OFFSET_X = OFFSET_Z + BITS_PER_Z; - - private static final long MASK_X = (1L << BITS_PER_X) - 1L; - private static final long MASK_Y = (1L << BITS_PER_Y) - 1L; - private static final long MASK_Z = (1L << BITS_PER_Z) - 1L; - - // from net.minecraft.world.level.dimension.DimensionType - public static final int Y_SIZE = (1 << BITS_PER_Y) - 32; - public static final int MAX_Y = (Y_SIZE >> 1) - 1; - public static final int MIN_Y = MAX_Y - Y_SIZE + 1; - - public final int x; - public final int y; - public final int z; - - public BlockPos(int x, int y, int z) { - this.x = x; - this.y = y; - this.z = z; - } - - public BlockPos add(int x, int y, int z) { - return x == 0 && y == 0 && z == 0 ? this : new BlockPos(this.x + x, this.y + y, this.z + z); - } - - public double distanceSquared(double x, double y, double z) { - double dx = this.x - x; - double dy = this.y - y; - double dz = this.z - z; - return dx * dx + dy * dy + dz * dz; - } - - public ChunkPosition toChunkPosition(World world) { - return new ChunkPosition(world, this.x >> 4, this.z >> 4); - } - - public long toLong() { - return (this.x & MASK_X) << OFFSET_X | (this.y & MASK_Y) << OFFSET_Y | (this.z & MASK_Z) << OFFSET_Z; - } - - public static BlockPos fromLong(long value) { - int x = (int) (value << (64 - BITS_PER_X - OFFSET_X) >> (64 - BITS_PER_X)); - int y = (int) (value << (64 - BITS_PER_Y - OFFSET_Y) >> (64 - BITS_PER_Y)); - int z = (int) (value << (64 - BITS_PER_Z - OFFSET_Z) >> (64 - BITS_PER_Z)); - return new BlockPos(x, y, z); - } - - public int toSectionPos() { - return (this.x & 0xF) << 12 | (this.y & 0xFFF) << 0 | (this.z & 0xF) << 16; - } - - public static BlockPos fromSectionPos(int x, int z, int sectionPos) { - x += (sectionPos >> 12) & 0xF; - int y = (sectionPos << 20 >> 20); - z += (sectionPos >> 16) & 0xF; - return new BlockPos(x, y, z); - } - - @Override - public int compareTo(BlockPos other) { - if (this.y == other.y) { - if (this.z == other.z) { - return this.x - other.x; - } - return this.z - other.z; - } - return this.y - other.y; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BlockPos)) { - return false; - } - BlockPos other = (BlockPos) obj; - if (x != other.x) { - return false; - } - if (y != other.y) { - return false; - } - if (z != other.z) { - return false; - } - return true; - } - - @Override - public int hashCode() { - int result = 1; - result = 31 * result + x; - result = 31 * result + y; - result = 31 * result + z; - return result; - } - - @Override - public String toString() { - return "BlockPos [x=" + x + ", y=" + y + ", z=" + z + "]"; - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockProperties.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockProperties.java deleted file mode 100644 index a445d9fdd..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockProperties.java +++ /dev/null @@ -1,98 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import com.google.common.collect.ImmutableList; - -public class BlockProperties { - - public static Builder builder(NamespacedKey key) { - return new Builder(key); - } - - private final NamespacedKey key; - private final BlockStateProperties defaultBlockState; - private final ImmutableList blockStates; - - private BlockProperties(Builder builder) { - this.key = builder.key; - this.defaultBlockState = builder.defaultBlockState; - this.blockStates = ImmutableList.copyOf(builder.blockStates); - } - - public NamespacedKey getKey() { - return key; - } - - public BlockStateProperties getDefaultBlockState() { - return defaultBlockState; - } - - public ImmutableList getBlockStates() { - return blockStates; - } - - @Override - public int hashCode() { - return this.key.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BlockProperties)) { - return false; - } - BlockProperties other = (BlockProperties) obj; - return Objects.equals(key, other.key); - } - - @Override - public String toString() { - return "BlockProperties [key=" + key + ", defaultBlockState=" + defaultBlockState + ", blockStates=" - + blockStates + "]"; - } - - public static class Builder { - - private final NamespacedKey key; - - private BlockStateProperties defaultBlockState; - private final Set blockStates = new HashSet<>(); - - private Builder(NamespacedKey key) { - this.key = key; - } - - public Builder withBlockState(BlockStateProperties blockState) { - if (!blockStates.add(blockState)) { - throw new IllegalStateException(String.format("duplicate block state id (%s) for block: %s", blockState.getId(), key)); - } - - if (blockState.isDefaultState()) { - // check for multiple default blocks - if (this.defaultBlockState != null) { - throw new IllegalStateException(String.format("multiple default block states for block: %s", blockState.getId(), key)); - } - - this.defaultBlockState = blockState; - } - - return this; - } - - public BlockProperties build() { - Objects.requireNonNull(this.defaultBlockState, "missing default block state for block: " + this.key); - - if (this.blockStates.size() == 0) { - throw new IllegalStateException("missing block states for block: " + this.key); - } - - return new BlockProperties(this); - } - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockStateProperties.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockStateProperties.java deleted file mode 100644 index 7ac3220a0..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/BlockStateProperties.java +++ /dev/null @@ -1,104 +0,0 @@ -package net.imprex.orebfuscator.util; - -public class BlockStateProperties { - - public static Builder builder(int id) { - return new Builder(id); - } - - private final int id; - - private final boolean isAir; - private final boolean isOccluding; - private final boolean isBlockEntity; - private final boolean isDefaultState; - - private BlockStateProperties(Builder builder) { - this.id = builder.id; - this.isAir = builder.isAir; - this.isOccluding = builder.isOccluding; - this.isBlockEntity = builder.isBlockEntity; - this.isDefaultState = builder.isDefaultState; - } - - public int getId() { - return id; - } - - public boolean isAir() { - return isAir; - } - - public boolean isOccluding() { - return isOccluding; - } - - public boolean isBlockEntity() { - return isBlockEntity; - } - - public boolean isDefaultState() { - return isDefaultState; - } - - @Override - public int hashCode() { - return id; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof BlockStateProperties)) { - return false; - } - BlockStateProperties other = (BlockStateProperties) obj; - return id == other.id; - } - - @Override - public String toString() { - return "BlockStateProperties [id=" + id + ", isDefaultState=" + isDefaultState + ", isAir=" + isAir - + ", isOccluding=" + isOccluding + ", isBlockEntity=" + isBlockEntity + "]"; - } - - public static class Builder { - - private final int id; - - private boolean isAir; - private boolean isOccluding; - private boolean isBlockEntity; - private boolean isDefaultState; - - private Builder(int id) { - this.id = id; - } - - public Builder withIsAir(boolean isAir) { - this.isAir = isAir; - return this; - } - - public Builder withIsOccluding(boolean isOccluding) { - this.isOccluding = isOccluding; - return this; - } - - public Builder withIsBlockEntity(boolean isBlockEntity) { - this.isBlockEntity = isBlockEntity; - return this; - } - - public Builder withIsDefaultState(boolean isDefaultState) { - this.isDefaultState = isDefaultState; - return this; - } - - public BlockStateProperties build() { - return new BlockStateProperties(this); - } - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkDirection.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkDirection.java deleted file mode 100644 index 344655b0d..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkDirection.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.imprex.orebfuscator.util; - -public enum ChunkDirection { - - NORTH(1, 0), EAST(0, 1), SOUTH(-1, 0), WEST(0, -1); - - private int offsetX; - private int offsetZ; - - private ChunkDirection(int offsetX, int offsetZ) { - this.offsetX = offsetX; - this.offsetZ = offsetZ; - } - - public int getOffsetX() { - return offsetX; - } - - public int getOffsetZ() { - return offsetZ; - } - - public static ChunkDirection fromPosition(ChunkPosition position, int targetX, int targetZ) { - int offsetX = (targetX >> 4) - position.x; - int offsetZ = (targetZ >> 4) - position.z; - - if (offsetX == 1 && offsetZ == 0) { - return NORTH; - } else if (offsetX == 0 && offsetZ == 1) { - return EAST; - } else if (offsetX == -1 && offsetZ == 0) { - return SOUTH; - } else if (offsetX == 0 && offsetZ == -1) { - return WEST; - } - - throw new IllegalArgumentException(String.format("invalid offset (origin: %s, x: %d, z: %d)", position, targetX, targetZ)); - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java deleted file mode 100644 index 264e3c0fa..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ChunkPosition.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.util.Objects; - -import org.bukkit.World; - -public class ChunkPosition { - - public static long toLong(int chunkX, int chunkZ) { - return (chunkZ & 0xffffffffL) << 32 | chunkX & 0xffffffffL; - } - - public final String world; - public final int x; - public final int z; - - public ChunkPosition(World world, int x, int z) { - this.world = Objects.requireNonNull(world).getName(); - this.x = x; - this.z = z; - } - - @Override - public int hashCode() { - int result = 1; - result = 31 * result + world.hashCode(); - result = 31 * result + x; - result = 31 * result + z; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof ChunkPosition)) { - return false; - } - ChunkPosition other = (ChunkPosition) obj; - return x == other.x && z == other.z && Objects.equals(world, other.world); - } - - @Override - public String toString() { - return "ChunkPosition [world=" + world + ", x=" + x + ", z=" + z + "]"; - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/JavaVersion.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/JavaVersion.java deleted file mode 100644 index 46f29cd2f..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/JavaVersion.java +++ /dev/null @@ -1,36 +0,0 @@ -package net.imprex.orebfuscator.util; - -public final class JavaVersion { - - private static final int JAVA_VERSION = javaMajorVersion(); - - public static int get() { - return JAVA_VERSION; - } - - private static int javaMajorVersion() { - return majorVersion(System.getProperty("java.specification.version", "1.6")); - } - - /** - * taken from: - * https://github.com/netty/netty/blob/7ad2b91515b3affaeadb4b2975cd6d2a8342c403/common/src/main/java/io/netty/util/internal/PlatformDependent0.java#L1037 - */ - private static int majorVersion(final String javaSpecVersion) { - final String[] components = javaSpecVersion.split("\\."); - final int[] version = new int[components.length]; - for (int i = 0; i < components.length; i++) { - version[i] = Integer.parseInt(components[i]); - } - - if (version[0] == 1) { - assert version[1] >= 6; - return version[1]; - } else { - return version[0]; - } - } - - private JavaVersion() { - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MathUtil.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MathUtil.java deleted file mode 100644 index 92589026d..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MathUtil.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.imprex.orebfuscator.util; - -public class MathUtil { - - public static int ceilToPowerOfTwo(int value) { - value--; - value |= value >> 1; - value |= value >> 2; - value |= value >> 4; - value |= value >> 8; - value |= value >> 16; - value++; - return value; - } - - public static int clamp(int value, int min, int max) { - return Math.max(min, Math.min(max, value)); - } - - public static int ceilLog2(int value) { - int result = 31 - Integer.numberOfLeadingZeros(value); - // add 1 if value is NOT a power of 2 (to do the ceil) - return result + (value != 0 && (value & value - 1) == 0 ? 0 : 1); - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/NamespacedKey.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/NamespacedKey.java deleted file mode 100644 index 4235cc962..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/NamespacedKey.java +++ /dev/null @@ -1,258 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.util.Locale; -import java.util.UUID; - -import org.bukkit.plugin.Plugin; - -/** - * Represents a String based key which consists of two components - a namespace - * and a key. - * - * Namespaces may only contain lowercase alphanumeric characters, periods, - * underscores, and hyphens. - *

- * Keys may only contain lowercase alphanumeric characters, periods, - * underscores, hyphens, and forward slashes. - * - * @author org.bukkit.NamespacedKey from 1.19.4 - * - */ -public final class NamespacedKey { - - /** - * The namespace representing all inbuilt keys. - */ - public static final String MINECRAFT = "minecraft"; - /** - * The namespace representing all keys generated by Bukkit for backwards - * compatibility measures. - */ - public static final String BUKKIT = "bukkit"; - // - private final String namespace; - private final String key; - - private static boolean isValidNamespaceChar(char c) { - return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-'; - } - - private static boolean isValidKeyChar(char c) { - return isValidNamespaceChar(c) || c == '/'; - } - - private static boolean isValidNamespace(String namespace) { - int len = namespace.length(); - if (len == 0) { - return false; - } - - for (int i = 0; i < len; i++) { - if (!isValidNamespaceChar(namespace.charAt(i))) { - return false; - } - } - - return true; - } - - private static boolean isValidKey(String key) { - int len = key.length(); - if (len == 0) { - return false; - } - - for (int i = 0; i < len; i++) { - if (!isValidKeyChar(key.charAt(i))) { - return false; - } - } - - return true; - } - - /** - * Create a key in a specific namespace. - * - * @param namespace namespace - * @param key key - * @deprecated should never be used by plugins, for internal use only!! - */ - @Deprecated - public NamespacedKey(String namespace, String key) { - if (namespace == null || !isValidNamespace(namespace)) { - throw new IllegalArgumentException(String.format("Invalid namespace. Must be [a-z0-9._-]: %s", namespace)); - } else if (key == null || !isValidKey(key)) { - throw new IllegalArgumentException(String.format("Invalid key. Must be [a-z0-9/._-]: %s", key)); - } - - this.namespace = namespace; - this.key = key; - - String string = toString(); - if (string.length() >= 256) { - throw new IllegalArgumentException(String.format("NamespacedKey must be less than 256 characters (%s)", string)); - } - } - - /** - * Create a key in the plugin's namespace. - *

- * Namespaces may only contain lowercase alphanumeric characters, periods, - * underscores, and hyphens. - *

- * Keys may only contain lowercase alphanumeric characters, periods, - * underscores, hyphens, and forward slashes. - * - * @param plugin the plugin to use for the namespace - * @param key the key to create - */ - public NamespacedKey(Plugin plugin, String key) { - if (plugin == null) { - throw new IllegalArgumentException("Plugin cannot be null"); - } else if (key == null) { - throw new IllegalArgumentException("Key cannot be null"); - } - - this.namespace = plugin.getName().toLowerCase(Locale.ROOT); - this.key = key.toLowerCase(Locale.ROOT); - - // Check validity after normalization - if (!isValidNamespace(this.namespace)) { - throw new IllegalArgumentException(String.format("Invalid namespace. Must be [a-z0-9._-]: %s", this.namespace)); - } else if (!isValidKey(this.key)) { - throw new IllegalArgumentException(String.format("Invalid key. Must be [a-z0-9/._-]: %s", this.key)); - } - - String string = toString(); - if (string.length() >= 256) { - throw new IllegalArgumentException(String.format("NamespacedKey must be less than 256 characters (%s)", string)); - } - } - - public String getNamespace() { - return namespace; - } - - public String getKey() { - return key; - } - - @Override - public int hashCode() { - int hash = 5; - hash = 47 * hash + this.namespace.hashCode(); - hash = 47 * hash + this.key.hashCode(); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final NamespacedKey other = (NamespacedKey) obj; - return this.namespace.equals(other.namespace) && this.key.equals(other.key); - } - - @Override - public String toString() { - return this.namespace + ":" + this.key; - } - - /** - * Return a new random key in the {@link #BUKKIT} namespace. - * - * @return new key - * @deprecated should never be used by plugins, for internal use only!! - */ - @Deprecated - public static NamespacedKey randomKey() { - return new NamespacedKey(BUKKIT, UUID.randomUUID().toString()); - } - - /** - * Get a key in the Minecraft namespace. - * - * @param key the key to use - * @return new key in the Minecraft namespace - */ - public static NamespacedKey minecraft(String key) { - return new NamespacedKey(MINECRAFT, key); - } - - /** - * Get a NamespacedKey from the supplied string with a default namespace if a - * namespace is not defined. This is a utility method meant to fetch a - * NamespacedKey from user input. Please note that casing does matter and any - * instance of uppercase characters will be considered invalid. The input - * contract is as follows: - * - *

-	 * fromString("foo", plugin) -{@literal >} "plugin:foo"
-	 * fromString("foo:bar", plugin) -{@literal >} "foo:bar"
-	 * fromString(":foo", null) -{@literal >} "minecraft:foo"
-	 * fromString("foo", null) -{@literal >} "minecraft:foo"
-	 * fromString("Foo", plugin) -{@literal >} null
-	 * fromString(":Foo", plugin) -{@literal >} null
-	 * fromString("foo:bar:bazz", plugin) -{@literal >} null
-	 * fromString("", plugin) -{@literal >} null
-	 * 
- * - * @param string the string to convert to a NamespacedKey - * @param defaultNamespace the default namespace to use if none was supplied. If - * null, the {@code minecraft} namespace - * ({@link #minecraft(String)}) will be used - * @return the created NamespacedKey. null if invalid key - * @see #fromString(String) - */ - public static NamespacedKey fromString(String string, Plugin defaultNamespace) { - if (string == null || string.isEmpty()) { - throw new IllegalArgumentException("Input string must not be empty or null"); - } - - String[] components = string.split(":", 3); - if (components.length > 2) { - return null; - } - - String key = (components.length == 2) ? components[1] : ""; - if (components.length == 1) { - String value = components[0]; - if (value.isEmpty() || !isValidKey(value)) { - return null; - } - - return (defaultNamespace != null) ? new NamespacedKey(defaultNamespace, value) : minecraft(value); - } else if (components.length == 2 && !isValidKey(key)) { - return null; - } - - String namespace = components[0]; - if (namespace.isEmpty()) { - return (defaultNamespace != null) ? new NamespacedKey(defaultNamespace, key) : minecraft(key); - } - - if (!isValidNamespace(namespace)) { - return null; - } - - return new NamespacedKey(namespace, key); - } - - /** - * Get a NamespacedKey from the supplied string. - * - * The default namespace will be Minecraft's (i.e. {@link #minecraft(String)}). - * - * @param key the key to convert to a NamespacedKey - * @return the created NamespacedKey. null if invalid - * @see #fromString(String, Plugin) - */ - public static NamespacedKey fromString(String key) { - return fromString(key, null); - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/OFCLogger.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/OFCLogger.java deleted file mode 100644 index 0213ea5da..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/OFCLogger.java +++ /dev/null @@ -1,89 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.util.LinkedList; -import java.util.Queue; -import java.util.logging.Level; -import java.util.logging.Logger; - -public final class OFCLogger { - - public static Logger LOGGER = Logger.getLogger("Orebfuscator"); - - private static final Queue VERBOSE_LOG = new LinkedList(); - private static boolean verbose = false; - - public static void setVerboseLogging(boolean verbose) { - OFCLogger.verbose = verbose; - if (OFCLogger.verbose) { - debug("Verbose logging has been enabled"); - } - } - - public static void debug(String message) { - if (OFCLogger.verbose) { - OFCLogger.LOGGER.log(Level.INFO, "[Debug] " + message); - } - - synchronized (VERBOSE_LOG) { - while (VERBOSE_LOG.size() >= 1000) { - VERBOSE_LOG.poll(); - } - VERBOSE_LOG.offer(message); - } - } - - public static String getLatestVerboseLog() { - synchronized (VERBOSE_LOG) { - int length = 0; - for (String message : VERBOSE_LOG) { - length += message.length() + 1; - } - - StringBuilder builder = new StringBuilder(length); - for (String message : VERBOSE_LOG) { - builder.append(message).append("\n"); - } - builder.deleteCharAt(builder.length() - 1); - return builder.toString(); - } - } - - public static void warn(String message) { - log(Level.WARNING, message); - } - - /** - * Log an information - */ - public static void info(String message) { - log(Level.INFO, message); - } - - /** - * Log with a specified level - */ - public static void log(Level level, String message) { - OFCLogger.LOGGER.log(level, message); - } - - /** - * Log an error - */ - public static void error(Throwable e) { - log(Level.SEVERE, e.getMessage(), e); - } - - /** - * Log an error - */ - public static void error(String message, Throwable e) { - log(Level.SEVERE, message, e); - } - - /** - * Log with a specified level and throwable - */ - public static void log(Level level, String message, Throwable throwable) { - OFCLogger.LOGGER.log(level, message, throwable); - } -} \ No newline at end of file diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/SimpleCache.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/SimpleCache.java deleted file mode 100644 index 93aeea5d1..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/SimpleCache.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.Consumer; - -/** - * Simple cache implementation that removes the oldest element once a certain size is reached - */ -public class SimpleCache extends LinkedHashMap { - - private static final long serialVersionUID = -2732738355560313649L; - - private final int maximumSize; - private final Consumer> remove; - - public SimpleCache(int maximumSize, Consumer> remove) { - super(16, 0.75f, true); - - this.maximumSize = maximumSize; - this.remove = remove; - } - - @Override - protected boolean removeEldestEntry(Map.Entry entry) { - if (this.size() > this.maximumSize) { - this.remove.accept(entry); - return true; - } - return false; - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/Version.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/Version.java deleted file mode 100644 index 19eb47e61..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/Version.java +++ /dev/null @@ -1,103 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.io.IOException; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; - -public record Version(int major, int minor, int patch) implements Comparable { - - private static final Pattern VERSION_PATTERN = Pattern.compile("(?\\d+)(?:\\.(?\\d+))?(?:\\.(?\\d+))?"); - - public static Version parse(String version) { - Matcher matcher = VERSION_PATTERN.matcher(version); - - if (!matcher.find()) { - throw new IllegalArgumentException("can't parse version: " + version); - } - - int major = Integer.parseInt(matcher.group("major")); - - String minorGroup = matcher.group("minor"); - int minor = minorGroup != null - ? Integer.parseInt(minorGroup) - : 0; - - String patchGroup = matcher.group("patch"); - int patch = patchGroup != null - ? Integer.parseInt(patchGroup) - : 0; - - return new Version(major, minor, patch); - } - - public boolean isAbove(Version version) { - return this.compareTo(version) > 0; - } - - public boolean isAtOrAbove(Version version) { - return this.compareTo(version) >= 0; - } - - public boolean isAtOrBelow(Version version) { - return this.compareTo(version) <= 0; - } - - public boolean isBelow(Version version) { - return this.compareTo(version) < 0; - } - - @Override - public int compareTo(Version other) { - int major = Integer.compare(this.major, other.major); - if (major != 0) { - return major; - } - - int minor = Integer.compare(this.minor, other.minor); - if (minor != 0) { - return minor; - } - - return Integer.compare(this.patch, other.patch); - } - - @Override - public int hashCode() { - return Objects.hash(major, minor, patch); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof Version)) { - return false; - } - Version other = (Version) obj; - return major == other.major && minor == other.minor && patch == other.patch; - } - - @Override - public String toString() { - return String.format("%s.%s.%s", this.major, this.minor, this.patch); - } - - public static final class Json extends TypeAdapter { - - @Override - public void write(JsonWriter out, Version value) throws IOException { - out.value(value.toString()); - } - - @Override - public Version read(JsonReader in) throws IOException { - return Version.parse(in.nextString()); - } - } -} diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/WeightedIntRandom.java b/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/WeightedIntRandom.java deleted file mode 100644 index 28ca62a49..000000000 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/WeightedIntRandom.java +++ /dev/null @@ -1,67 +0,0 @@ -package net.imprex.orebfuscator.util; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ThreadLocalRandom; - -public class WeightedIntRandom { - - public static Builder builder() { - return new Builder(); - } - - /** - * Sum of all weights - */ - private final int weight; - - /** - * Flat int array where each entry has a probability of one - * - * @implNote use flat int array instead of treemap cause O(N*log(N)) is too - * slow since this is the second hottest code path - */ - private final int[] entries; - - private WeightedIntRandom(Builder builder) { - this.weight = builder.weight; - this.entries = new int[builder.weight]; - - int index = 0; - for (Map.Entry entry : builder.entries.entrySet()) { - for (int weight = 0; weight < entry.getValue(); weight++) { - this.entries[index++] = entry.getKey(); - } - } - } - - public int next() { - int index = ThreadLocalRandom.current().nextInt(this.weight); - return this.entries[index]; - } - - public static class Builder { - - private int weight = 0; - - private final Map entries = new HashMap<>(); - - private Builder() { - } - - /** - * Returns true if this random did not already contain the specified value - */ - public boolean add(int value, int weight) { - if (entries.putIfAbsent(value, weight) == null) { - this.weight += weight; - return true; - } - return false; - } - - public WeightedIntRandom build() { - return new WeightedIntRandom(this); - } - } -} diff --git a/orebfuscator-common/src/test/java/net/imprex/orebfuscator/util/BlockPosTest.java b/orebfuscator-common/src/test/java/net/imprex/orebfuscator/util/BlockPosTest.java deleted file mode 100644 index 7a79e4a02..000000000 --- a/orebfuscator-common/src/test/java/net/imprex/orebfuscator/util/BlockPosTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package net.imprex.orebfuscator.util; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class BlockPosTest { - - @Test - public void testLongFormat() { - BlockPos positionA = new BlockPos(-52134, BlockPos.MAX_Y, 6243234); - BlockPos positionB = new BlockPos(0, BlockPos.MIN_Y, -4); - BlockPos positionC = new BlockPos(15, 0, -5663423); - BlockPos positionD = new BlockPos(21523, 16, -5663423); - - long valueA = positionA.toLong(); - long valueB = positionB.toLong(); - long valueC = positionC.toLong(); - long valueD = positionD.toLong(); - - assertEquals(positionA, BlockPos.fromLong(valueA)); - assertEquals(positionB, BlockPos.fromLong(valueB)); - assertEquals(positionC, BlockPos.fromLong(valueC)); - assertEquals(positionD, BlockPos.fromLong(valueD)); - } - - @Test - public void testSectionPos() { - final int chunkX = -42 << 4; - final int chunkZ = 6521 << 4; - - BlockPos positionA = new BlockPos(chunkX + 8, BlockPos.MAX_Y, chunkZ); - BlockPos positionB = new BlockPos(chunkX, BlockPos.MIN_Y, chunkZ + 15); - BlockPos positionC = new BlockPos(chunkX + 15, 0, chunkZ + 4); - - int sectionPosA = positionA.toSectionPos(); - int sectionPosB = positionB.toSectionPos(); - int sectionPosC = positionC.toSectionPos(); - - assertEquals(positionA, BlockPos.fromSectionPos(chunkX, chunkZ, sectionPosA)); - assertEquals(positionB, BlockPos.fromSectionPos(chunkX, chunkZ, sectionPosB)); - assertEquals(positionC, BlockPos.fromSectionPos(chunkX, chunkZ, sectionPosC)); - } -} diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-api/pom.xml b/orebfuscator-compatibility/orebfuscator-compatibility-api/pom.xml index c801f3c96..38dbcd35f 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-api/pom.xml +++ b/orebfuscator-compatibility/orebfuscator-compatibility-api/pom.xml @@ -19,12 +19,6 @@ ${dependency.bukkit.version} provided - - net.imprex - orebfuscator-common - ${revision} - compile - net.imprex orebfuscator-nms-api diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorCompatibility.java b/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorCompatibility.java index 3c87f7b28..e9f70779b 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorCompatibility.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorCompatibility.java @@ -7,12 +7,11 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.compatibility.CompatibilityLayer; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.ChunkPosition; -import net.imprex.orebfuscator.util.MinecraftVersion; -import net.imprex.orebfuscator.util.OFCLogger; import net.imprex.orebfuscator.util.ServerVersion; public class OrebfuscatorCompatibility { @@ -27,12 +26,12 @@ public static void initialize(Plugin plugin, Config config) { String className = "net.imprex.orebfuscator.compatibility.bukkit.BukkitCompatibilityLayer"; if (ServerVersion.isFolia()) { className = "net.imprex.orebfuscator.compatibility.folia.FoliaCompatibilityLayer"; - } else if (ServerVersion.isPaper() && MinecraftVersion.minorVersion() >= 13) { + } else if (ServerVersion.isPaper()) { className = "net.imprex.orebfuscator.compatibility.paper.PaperCompatibilityLayer"; } try { - OFCLogger.debug("Loading compatibility layer for: " + className); + OfcLogger.debug("Loading compatibility layer for: " + className); Class nmsManager = Class.forName(className).asSubclass(CompatibilityLayer.class); Constructor constructor = nmsManager.getConstructor(Plugin.class, Config.class); OrebfuscatorCompatibility.instance = constructor.newInstance(plugin, config); @@ -42,7 +41,7 @@ public static void initialize(Plugin plugin, Config config) { throw new RuntimeException("Couldn't initialize compatibility layer", e); } - OFCLogger.debug("Compatibility layer successfully loaded"); + OfcLogger.debug("Compatibility layer successfully loaded"); } public static boolean isGameThread() { @@ -65,8 +64,8 @@ public static void cancelTasks() { instance.getScheduler().cancelTasks(); } - public static CompletableFuture getNeighboringChunks(World world, ChunkPosition position) { - return instance.getNeighboringChunks(world, position); + public static CompletableFuture getNeighboringChunks(World world, ChunkCacheKey key) { + return instance.getNeighboringChunks(world, key); } public static void close() { diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/compatibility/CompatibilityLayer.java b/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/compatibility/CompatibilityLayer.java index 15db93d1e..7cf05fa18 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/compatibility/CompatibilityLayer.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-api/src/main/java/net/imprex/orebfuscator/compatibility/CompatibilityLayer.java @@ -4,8 +4,8 @@ import org.bukkit.World; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.ChunkPosition; public interface CompatibilityLayer { @@ -13,5 +13,5 @@ public interface CompatibilityLayer { CompatibilityScheduler getScheduler(); - CompletableFuture getNeighboringChunks(World world, ChunkPosition position); + CompletableFuture getNeighboringChunks(World world, ChunkCacheKey key); } diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitChunkLoader.java b/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitChunkLoader.java index f713b128f..04b8caac6 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitChunkLoader.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitChunkLoader.java @@ -9,11 +9,11 @@ import org.bukkit.World; import org.bukkit.plugin.Plugin; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.ChunkCacheKey; +import dev.imprex.orebfuscator.util.ChunkDirection; import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.ChunkDirection; -import net.imprex.orebfuscator.util.ChunkPosition; public class BukkitChunkLoader implements Runnable { @@ -27,8 +27,8 @@ public BukkitChunkLoader(Plugin plugin, Config config) { Bukkit.getScheduler().runTaskTimer(plugin, this, 0, 1); } - public CompletableFuture submitRequest(World world, ChunkPosition chunkPosition) { - Request request = new Request(world, chunkPosition); + public CompletableFuture submitRequest(World world, ChunkCacheKey key) { + Request request = new Request(world, key); this.requests.offer(request); return request.future; } @@ -46,13 +46,13 @@ public void run() { private class Request implements Runnable { private final World world; - private final ChunkPosition position; + private final ChunkCacheKey key; private final CompletableFuture future = new CompletableFuture<>(); - public Request(World world, ChunkPosition position) { + public Request(World world, ChunkCacheKey key) { this.world = world; - this.position = position; + this.key = key; } @Override @@ -60,8 +60,8 @@ public void run() { final ReadOnlyChunk[] neighboringChunks = new ReadOnlyChunk[4]; for (ChunkDirection direction : ChunkDirection.values()) { - int chunkX = position.x + direction.getOffsetX(); - int chunkZ = position.z + direction.getOffsetZ(); + int chunkX = key.x() + direction.getOffsetX(); + int chunkZ = key.z() + direction.getOffsetZ(); neighboringChunks[direction.ordinal()] = OrebfuscatorNms.getReadOnlyChunk(world, chunkX, chunkZ); } diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitCompatibilityLayer.java b/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitCompatibilityLayer.java index 6cd899e8f..b25b6dcf2 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitCompatibilityLayer.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-bukkit/src/main/java/net/imprex/orebfuscator/compatibility/bukkit/BukkitCompatibilityLayer.java @@ -5,11 +5,11 @@ import org.bukkit.World; import org.bukkit.plugin.Plugin; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.compatibility.CompatibilityLayer; import net.imprex.orebfuscator.compatibility.CompatibilityScheduler; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.ChunkPosition; public class BukkitCompatibilityLayer implements CompatibilityLayer { @@ -34,7 +34,7 @@ public CompatibilityScheduler getScheduler() { } @Override - public CompletableFuture getNeighboringChunks(World world, ChunkPosition position) { - return this.chunkLoader.submitRequest(world, position); + public CompletableFuture getNeighboringChunks(World world, ChunkCacheKey key) { + return this.chunkLoader.submitRequest(world, key); } } diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-folia/src/main/java/net/imprex/orebfuscator/compatibility/folia/FoliaCompatibilityLayer.java b/orebfuscator-compatibility/orebfuscator-compatibility-folia/src/main/java/net/imprex/orebfuscator/compatibility/folia/FoliaCompatibilityLayer.java index fc0a3906d..1eb7533df 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-folia/src/main/java/net/imprex/orebfuscator/compatibility/folia/FoliaCompatibilityLayer.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-folia/src/main/java/net/imprex/orebfuscator/compatibility/folia/FoliaCompatibilityLayer.java @@ -2,9 +2,9 @@ import org.bukkit.plugin.Plugin; +import dev.imprex.orebfuscator.config.api.Config; import net.imprex.orebfuscator.compatibility.CompatibilityScheduler; import net.imprex.orebfuscator.compatibility.paper.AbstractPaperCompatibilityLayer; -import net.imprex.orebfuscator.config.Config; public class FoliaCompatibilityLayer extends AbstractPaperCompatibilityLayer { diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/AbstractPaperCompatibilityLayer.java b/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/AbstractPaperCompatibilityLayer.java index 9cba81977..057f92d80 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/AbstractPaperCompatibilityLayer.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/AbstractPaperCompatibilityLayer.java @@ -4,22 +4,22 @@ import org.bukkit.World; +import dev.imprex.orebfuscator.util.ChunkCacheKey; +import dev.imprex.orebfuscator.util.ChunkDirection; import net.imprex.orebfuscator.OrebfuscatorNms; import net.imprex.orebfuscator.compatibility.CompatibilityLayer; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.ChunkDirection; -import net.imprex.orebfuscator.util.ChunkPosition; public abstract class AbstractPaperCompatibilityLayer implements CompatibilityLayer { @Override - public CompletableFuture getNeighboringChunks(World world, ChunkPosition position) { + public CompletableFuture getNeighboringChunks(World world, ChunkCacheKey key) { CompletableFuture[] futures = new CompletableFuture[4]; ReadOnlyChunk[] neighboringChunks = new ReadOnlyChunk[4]; for (ChunkDirection direction : ChunkDirection.values()) { - int chunkX = position.x + direction.getOffsetX(); - int chunkZ = position.z + direction.getOffsetZ(); + int chunkX = key.x() + direction.getOffsetX(); + int chunkZ = key.z() + direction.getOffsetZ(); int index = direction.ordinal(); futures[index] = world.getChunkAtAsync(chunkX, chunkZ).thenAccept(chunk -> { diff --git a/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/PaperCompatibilityLayer.java b/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/PaperCompatibilityLayer.java index 99a2bafd8..ba437f8fa 100644 --- a/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/PaperCompatibilityLayer.java +++ b/orebfuscator-compatibility/orebfuscator-compatibility-paper/src/main/java/net/imprex/orebfuscator/compatibility/paper/PaperCompatibilityLayer.java @@ -2,9 +2,9 @@ import org.bukkit.plugin.Plugin; +import dev.imprex.orebfuscator.config.api.Config; import net.imprex.orebfuscator.compatibility.CompatibilityScheduler; import net.imprex.orebfuscator.compatibility.bukkit.BukkitScheduler; -import net.imprex.orebfuscator.config.Config; public class PaperCompatibilityLayer extends AbstractPaperCompatibilityLayer { diff --git a/orebfuscator-core/pom.xml b/orebfuscator-core/pom.xml new file mode 100644 index 000000000..9afa7bfa2 --- /dev/null +++ b/orebfuscator-core/pom.xml @@ -0,0 +1,84 @@ + + 4.0.0 + + + net.imprex + orebfuscator + ${revision} + + + dev.imprex.orebfuscator + orebfuscator-core + jar + + + + + io.netty + netty-buffer + ${dependency.netty.version} + provided + + + io.netty + netty-transport + ${dependency.netty.version} + provided + + + + + com.google.guava + guava + ${dependency.guava.version} + compile + + + com.google.code.gson + gson + ${dependency.gson.version} + provided + + + org.yaml + snakeyaml + ${dependency.snakeyaml.version} + compile + + + org.joml + joml + ${dependency.joml.version} + provided + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + + com.google.common + dev.imprex.shaded.com.google.common + + + org.yaml.snakeyaml + dev.imprex.shaded.org.yaml.snakeyaml + + + + + + + \ No newline at end of file diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/cache/AbstractRegionFileCache.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/cache/AbstractRegionFileCache.java new file mode 100644 index 000000000..5b25016bc --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/cache/AbstractRegionFileCache.java @@ -0,0 +1,118 @@ +package dev.imprex.orebfuscator.cache; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; +import dev.imprex.orebfuscator.util.SimpleCache; + +public abstract class AbstractRegionFileCache { + + protected final ReadWriteLock lock = new ReentrantReadWriteLock(true); + protected final Map regionFiles; + + protected final CacheConfig cacheConfig; + + public AbstractRegionFileCache(CacheConfig cacheConfig) { + this.cacheConfig = cacheConfig; + + this.regionFiles = new SimpleCache<>(cacheConfig.maximumOpenRegionFiles(), this::remove); + } + + protected abstract T createRegionFile(Path path) throws IOException; + + protected abstract void closeRegionFile(T t) throws IOException; + + protected abstract DataInputStream createInputStream(T t, ChunkCacheKey key) throws IOException; + + protected abstract DataOutputStream createOutputStream(T t, ChunkCacheKey key) throws IOException; + + public final DataInputStream createInputStream(ChunkCacheKey key) throws IOException { + T t = this.get(this.cacheConfig.regionFile(key)); + return t != null ? this.createInputStream(t, key) : null; + } + + public final DataOutputStream createOutputStream(ChunkCacheKey key) throws IOException { + T t = this.get(this.cacheConfig.regionFile(key)); + return t != null ? this.createOutputStream(t, key) : null; + } + + private final void remove(Map.Entry entry) { + try { + this.closeRegionFile(entry.getValue()); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected final T get(Path path) throws IOException { + this.lock.readLock().lock(); + try { + T t = this.regionFiles.get(path); + if (t != null) { + return t; + } + } finally { + this.lock.readLock().unlock(); + } + + if (Files.notExists(path.getParent())) { + Files.createDirectories(path.getParent()); + } + + if (this.regionFiles.size() > this.cacheConfig.maximumOpenRegionFiles()) { + throw new IllegalStateException(String.format("RegionFileCache got bigger than expected (%d > %d)", + this.regionFiles.size(), this.cacheConfig.maximumOpenRegionFiles())); + } + + T t = Objects.requireNonNull(this.createRegionFile(path)); + + this.lock.writeLock().lock(); + try { + if (this.regionFiles.putIfAbsent(path, t) != null) { + // some other thread was faster, close fd + closeRegionFile(t); + } + return this.regionFiles.get(path); + } finally { + this.lock.writeLock().unlock(); + } + } + + public final void close(Path path) throws IOException { + this.lock.writeLock().lock(); + try { + T t = this.regionFiles.remove(path); + if (t != null) { + this.closeRegionFile(t); + } + } finally { + this.lock.writeLock().unlock(); + } + } + + public final void clear() { + this.lock.writeLock().lock(); + try { + for (T t : this.regionFiles.values()) { + try { + if (t != null) { + this.closeRegionFile(t); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + this.regionFiles.clear(); + } finally { + this.lock.writeLock().unlock(); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ByteBufUtil.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ByteBufUtil.java new file mode 100644 index 000000000..c1062b8c7 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ByteBufUtil.java @@ -0,0 +1,48 @@ +package dev.imprex.orebfuscator.chunk; + +import io.netty.buffer.ByteBuf; + +public class ByteBufUtil { + + public static int getVarIntSize(int value) { + for (int bytes = 1; bytes < 5; bytes++) { + if ((value & -1 << bytes * 7) == 0) { + return bytes; + } + } + return 5; + } + + public static void skipVarInt(ByteBuf buffer) { + int bytes = 0; + byte in; + do { + in = buffer.readByte(); + if (++bytes > 5) { + throw new IndexOutOfBoundsException("varint32 too long"); + } + } while ((in & 0x80) != 0); + } + + public static int readVarInt(ByteBuf buffer) { + int out = 0; + int bytes = 0; + byte in; + do { + in = buffer.readByte(); + out |= (in & 0x7F) << bytes++ * 7; + if (bytes > 5) { + throw new IndexOutOfBoundsException("varint32 too long"); + } + } while ((in & 0x80) != 0); + return out; + } + + public static void writeVarInt(ByteBuf buffer, int value) { + while ((value & -0x80) != 0) { + buffer.writeByte(value & 0x7F | 0x80); + value >>>= 7; + } + buffer.writeByte(value); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/Chunk.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/Chunk.java new file mode 100644 index 000000000..3c908454d --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/Chunk.java @@ -0,0 +1,143 @@ +package dev.imprex.orebfuscator.chunk; + +import java.util.Arrays; +import dev.imprex.orebfuscator.interop.ChunkPacketAccessor; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; +import io.netty.buffer.Unpooled; + +public class Chunk implements AutoCloseable { + + private final ChunkFactory factory; + + private final int chunkX; + private final int chunkZ; + + private final WorldAccessor worldAccessor; + private final ChunkSectionHolder[] sections; + + private final ByteBuf inputBuffer; + private final ByteBuf outputBuffer; + + Chunk(ChunkFactory factory, ChunkPacketAccessor packet) { + this.factory = factory; + + this.chunkX = packet.chunkX(); + this.chunkZ = packet.chunkZ(); + + this.worldAccessor = packet.world(); + this.sections = new ChunkSectionHolder[this.worldAccessor.getSectionCount()]; + + byte[] data = packet.data(); + this.inputBuffer = Unpooled.wrappedBuffer(data); + this.outputBuffer = PooledByteBufAllocator.DEFAULT.heapBuffer(data.length); + + for (int sectionIndex = 0; sectionIndex < this.sections.length; sectionIndex++) { + if (packet.isSectionPresent(sectionIndex)) { + this.sections[sectionIndex] = new ChunkSectionHolder(); + } + } + } + + public WorldAccessor world() { + return worldAccessor; + } + + public int getSectionCount() { + return this.sections.length; + } + + public ChunkSection getSection(int index) { + ChunkSectionHolder chunkSection = this.sections[index]; + if (chunkSection != null) { + return chunkSection.chunkSection; + } + return null; + } + + public int getBlockState(int x, int y, int z) { + if (x >> 4 == this.chunkX && z >> 4 == this.chunkZ) { + ChunkSectionHolder chunkSection = this.sections[this.worldAccessor.getSectionIndex(y)]; + if (chunkSection != null) { + return chunkSection.data[ChunkSection.positionToIndex(x & 0xF, y & 0xF, z & 0xF)]; + } + return 0; + } + + return -1; + } + + public byte[] finalizeOutput() { + for (ChunkSectionHolder chunkSection : this.sections) { + if (chunkSection != null) { + chunkSection.write(); + } + } + + this.outputBuffer.writeBytes(this.inputBuffer); + + return Arrays.copyOfRange( + this.outputBuffer.array(), this.outputBuffer.arrayOffset(), + this.outputBuffer.arrayOffset() + this.outputBuffer.readableBytes()); + } + + @Override + public void close() throws Exception { + this.inputBuffer.release(); + this.outputBuffer.release(); + } + + private void skipBiomePalettedContainer() { + int bitsPerValue = this.inputBuffer.readUnsignedByte(); + + if (bitsPerValue == 0) { + ByteBufUtil.readVarInt(this.inputBuffer); + } else if (bitsPerValue <= 3) { + for (int i = ByteBufUtil.readVarInt(this.inputBuffer); i > 0; i--) { + ByteBufUtil.readVarInt(this.inputBuffer); + } + } + + int expectedDataLength = SimpleVarBitBuffer.calculateArraySize(bitsPerValue, 64); + + if (factory.versionFlags().hasLongArrayLengthField()) { + int dataLength = ByteBufUtil.readVarInt(this.inputBuffer); + if (expectedDataLength != dataLength) { + throw new IndexOutOfBoundsException( + "data.length != VarBitBuffer::size " + dataLength + " " + expectedDataLength); + } + } + + this.inputBuffer.skipBytes(Long.BYTES * expectedDataLength); + } + + private class ChunkSectionHolder { + + public final ChunkSection chunkSection; + + public final int[] data; + public final int extraOffset; + + private int extraBytes; + + public ChunkSectionHolder() { + this.chunkSection = new ChunkSection(factory); + + this.data = this.chunkSection.read(inputBuffer); + this.extraOffset = inputBuffer.readerIndex(); + + if (factory.versionFlags().hasBiomePalettedContainer()) { + skipBiomePalettedContainer(); + this.extraBytes = inputBuffer.readerIndex() - this.extraOffset; + } + } + + public void write() { + this.chunkSection.write(outputBuffer); + if (this.extraBytes > 0) { + outputBuffer.writeBytes(inputBuffer, this.extraOffset, extraBytes); + } + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkFactory.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkFactory.java new file mode 100644 index 000000000..8581780b3 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkFactory.java @@ -0,0 +1,28 @@ +package dev.imprex.orebfuscator.chunk; + +import dev.imprex.orebfuscator.interop.ChunkPacketAccessor; +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import dev.imprex.orebfuscator.interop.ServerAccessor; + +public class ChunkFactory { + + private final RegistryAccessor registryAccessor; + private final ChunkVersionFlags versionFlags; + + public ChunkFactory(ServerAccessor serverAccessor) { + this.registryAccessor = serverAccessor.getRegistry(); + this.versionFlags = new ChunkVersionFlags(serverAccessor); + } + + RegistryAccessor registryAccessor() { + return registryAccessor; + } + + ChunkVersionFlags versionFlags() { + return versionFlags; + } + + public Chunk fromPacket(ChunkPacketAccessor packet) { + return new Chunk(this, packet); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkSection.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkSection.java new file mode 100644 index 000000000..c9fa599ba --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkSection.java @@ -0,0 +1,146 @@ +package dev.imprex.orebfuscator.chunk; + +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import io.netty.buffer.ByteBuf; + +public class ChunkSection { + + private final RegistryAccessor registryAccessor; + private final ChunkVersionFlags versionFlags; + + private int blockCount; + private int bitsPerBlock = -1; + + private Palette palette; + private VarBitBuffer data; + + public ChunkSection(ChunkFactory factory) { + this.registryAccessor = factory.registryAccessor(); + this.versionFlags = factory.versionFlags(); + + this.setBitsPerBlock(0, true); + } + + public RegistryAccessor registryAccessor() { + return registryAccessor; + } + + private void setBitsPerBlock(int bitsPerBlock, boolean grow) { + if (this.bitsPerBlock != bitsPerBlock) { + if (versionFlags.hasSingleValuePalette() && bitsPerBlock == 0) { + this.bitsPerBlock = 0; + this.palette = new SingleValuePalette(this, 0); + } else if (!grow && bitsPerBlock == 1) { + // fix: fawe chunk format incompatibility with bitsPerBlock == 1 + // https://github.com/Imprex-Development/Orebfuscator/issues/36 + this.bitsPerBlock = bitsPerBlock; + this.palette = new IndirectPalette(this.bitsPerBlock, this); + } else if (bitsPerBlock <= 8) { + this.bitsPerBlock = Math.max(4, bitsPerBlock); + this.palette = new IndirectPalette(this.bitsPerBlock, this); + } else { + this.bitsPerBlock = registryAccessor.getMaxBitsPerBlockState(); + this.palette = new DirectPalette(); + } + + if (this.bitsPerBlock == 0) { + this.data = new ZeroVarBitBuffer(4096); + } else { + this.data = new SimpleVarBitBuffer(this.bitsPerBlock, 4096); + } + } + } + + int grow(int bitsPerBlock, int blockId) { + Palette palette = this.palette; + VarBitBuffer data = this.data; + + this.setBitsPerBlock(bitsPerBlock, true); + + for (int i = 0; i < data.size(); i++) { + int preBlockId = palette.valueFor(data.get(i)); + this.data.set(i, this.palette.idFor(preBlockId)); + } + + return this.palette.idFor(blockId); + } + + static int positionToIndex(int x, int y, int z) { + return y << 8 | z << 4 | x; + } + + public void setBlockState(int x, int y, int z, int blockId) { + this.setBlockState(positionToIndex(x, y, z), blockId); + } + + public void setBlockState(int index, int blockId) { + int prevBlockId = this.getBlockState(index); + + if (!registryAccessor.isAir(prevBlockId)) { + --this.blockCount; + } + + if (!registryAccessor.isAir(blockId)) { + ++this.blockCount; + } + + int paletteIndex = this.palette.idFor(blockId); + this.data.set(index, paletteIndex); + } + + public int getBlock(int x, int y, int z) { + return this.getBlockState(positionToIndex(x, y, z)); + } + + public int getBlockState(int index) { + return this.palette.valueFor(this.data.get(index)); + } + + public boolean isEmpty() { + return this.blockCount == 0; + } + + public void write(ByteBuf buffer) { + buffer.writeShort(this.blockCount); + + buffer.writeByte(this.bitsPerBlock); + this.palette.write(buffer); + + long[] data = this.data.toArray(); + + if (versionFlags.hasLongArrayLengthField()) { + ByteBufUtil.writeVarInt(buffer, data.length); + } + + for (long entry : data) { + buffer.writeLong(entry); + } + } + + public int[] read(ByteBuf buffer) { + this.blockCount = buffer.readShort(); + + this.setBitsPerBlock(buffer.readUnsignedByte(), false); + + this.palette.read(buffer); + + long[] data = this.data.toArray(); + + if (versionFlags.hasLongArrayLengthField()) { + int length = ByteBufUtil.readVarInt(buffer); + if (data.length != length) { + throw new IndexOutOfBoundsException("data.length != VarBitBuffer::size " + length + " " + this.data); + } + } + + for (int i = 0; i < data.length; i++) { + data[i] = buffer.readLong(); + } + + int[] directData = new int[4096]; + for (int i = 0; i < directData.length; i++) { + directData[i] = this.getBlockState(i); + } + return directData; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkVersionFlags.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkVersionFlags.java new file mode 100644 index 000000000..14f9cae36 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ChunkVersionFlags.java @@ -0,0 +1,33 @@ +package dev.imprex.orebfuscator.chunk; + +import dev.imprex.orebfuscator.interop.ServerAccessor; + +public final class ChunkVersionFlags { + + // hasLongArrayLengthField < 1.21.5 + // hasBiomePalettedContainer >= 1.18 + // hasSingleValuePalette >= 1.18 + + private final boolean hasLongArrayLengthField; + private final boolean hasBiomePalettedContainer; + private final boolean hasSingleValuePalette; + + public ChunkVersionFlags(ServerAccessor serverAccessor) { + var version = serverAccessor.getMinecraftVersion(); + hasLongArrayLengthField = version.isBelow("1.21.5"); + hasBiomePalettedContainer = version.isAtOrAbove("1.18"); + hasSingleValuePalette = version.isAtOrAbove("1.18"); + } + + public boolean hasLongArrayLengthField() { + return hasLongArrayLengthField; + } + + public boolean hasBiomePalettedContainer() { + return hasBiomePalettedContainer; + } + + public boolean hasSingleValuePalette() { + return hasSingleValuePalette; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/DirectPalette.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/DirectPalette.java new file mode 100644 index 000000000..884625fc8 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/DirectPalette.java @@ -0,0 +1,24 @@ +package dev.imprex.orebfuscator.chunk; + +import io.netty.buffer.ByteBuf; + +public class DirectPalette implements Palette { + + @Override + public int idFor(int value) { + return value; + } + + @Override + public int valueFor(int id) { + return id; + } + + @Override + public void read(ByteBuf buffer) { + } + + @Override + public void write(ByteBuf buffer) { + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/IndirectPalette.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/IndirectPalette.java new file mode 100644 index 000000000..29d87f497 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/IndirectPalette.java @@ -0,0 +1,69 @@ +package dev.imprex.orebfuscator.chunk; + +import java.util.Arrays; +import io.netty.buffer.ByteBuf; + +public class IndirectPalette implements Palette { + + private final int bitsPerValue; + private final ChunkSection chunkSection; + + private final byte[] byValue; + private final int[] byId; + + private int size = 0; + + public IndirectPalette(int bitsPerValue, ChunkSection chunkSection) { + this.bitsPerValue = bitsPerValue; + this.chunkSection = chunkSection; + + // TODO improve block to index + this.byValue = new byte[chunkSection.registryAccessor().getUniqueBlockStateCount()]; + Arrays.fill(this.byValue, (byte) 0xFF); + this.byId = new int[1 << bitsPerValue]; + } + + @Override + public int idFor(int value) { + int id = this.byValue[value] & 0xFF; + if (id == 0xFF) { + id = this.size++; + + if (id != 0xFF && id < this.byId.length) { + this.byValue[value] = (byte) id; + this.byId[id] = value; + } else { + id = this.chunkSection.grow(this.bitsPerValue + 1, value); + } + } + return id; + } + + @Override + public int valueFor(int id) { + if (id < 0 || id >= this.size) { + throw new IndexOutOfBoundsException(); + } else { + return this.byId[id]; + } + } + + @Override + public void read(ByteBuf buffer) { + this.size = ByteBufUtil.readVarInt(buffer); + for (int id = 0; id < size; id++) { + int value = ByteBufUtil.readVarInt(buffer); + this.byId[id] = value; + this.byValue[value] = (byte) id; + } + } + + @Override + public void write(ByteBuf buffer) { + ByteBufUtil.writeVarInt(buffer, this.size); + + for (int id = 0; id < this.size; id++) { + ByteBufUtil.writeVarInt(buffer, this.valueFor(id)); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/Palette.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/Palette.java new file mode 100644 index 000000000..bd005e2d9 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/Palette.java @@ -0,0 +1,15 @@ +package dev.imprex.orebfuscator.chunk; + +import io.netty.buffer.ByteBuf; + +public interface Palette { + + int idFor(int value); + + int valueFor(int id); + + void read(ByteBuf buffer); + + void write(ByteBuf buffer); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/SimpleVarBitBuffer.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/SimpleVarBitBuffer.java new file mode 100644 index 000000000..c41edb650 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/SimpleVarBitBuffer.java @@ -0,0 +1,51 @@ +package dev.imprex.orebfuscator.chunk; + +public class SimpleVarBitBuffer implements VarBitBuffer { + + public static int calculateArraySize(int bitsPerEntry, int size) { + return bitsPerEntry == 0 ? 0 : (int) Math.ceil((float) size / (64 / bitsPerEntry)); + } + + private final int bitsPerEntry; + private final int entriesPerLong; + private final long adjustmentMask; + + private final int size; + private final long[] buffer; + + public SimpleVarBitBuffer(int bitsPerEntry, int size) { + this.bitsPerEntry = bitsPerEntry; + this.entriesPerLong = 64 / bitsPerEntry; + this.adjustmentMask = (1L << bitsPerEntry) - 1L; + + this.size = size; + this.buffer = new long[(int) Math.ceil((float) size / this.entriesPerLong)]; + } + + public int get(int index) { + int position = index / this.entriesPerLong; + int offset = (index - position * this.entriesPerLong) * this.bitsPerEntry; + return (int) (this.buffer[position] >> offset & this.adjustmentMask); + } + + public void set(int index, int value) { + int position = index / this.entriesPerLong; + int offset = (index - position * this.entriesPerLong) * this.bitsPerEntry; + this.buffer[position] = this.buffer[position] + & ~(this.adjustmentMask << offset) | (value & this.adjustmentMask) << offset; + } + + public long[] toArray() { + return this.buffer; + } + + public int size() { + return this.size; + } + + @Override + public String toString() { + return String.format("[size=%d, length=%d, bitsPerEntry=%d, entriesPerLong=%d]", size, buffer.length, bitsPerEntry, + entriesPerLong); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/SingleValuePalette.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/SingleValuePalette.java new file mode 100644 index 000000000..b5939a42a --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/SingleValuePalette.java @@ -0,0 +1,48 @@ +package dev.imprex.orebfuscator.chunk; + +import io.netty.buffer.ByteBuf; + +public class SingleValuePalette implements Palette { + + private final ChunkSection chunkSection; + + private int value = -1; + + public SingleValuePalette(ChunkSection chunkSection, int value) { + this.chunkSection = chunkSection; + this.value = value; + } + + @Override + public int idFor(int value) { + if (this.value != -1 && value != this.value) { + return this.chunkSection.grow(1, value); + } else { + this.value = value; + return 0; + } + } + + @Override + public int valueFor(int id) { + if (this.value != -1 && id == 0) { + return this.value; + } else { + throw new IllegalStateException("value isn't initialized"); + } + } + + @Override + public void read(ByteBuf buffer) { + this.value = ByteBufUtil.readVarInt(buffer); + } + + @Override + public void write(ByteBuf buffer) { + if (this.value == -1) { + throw new IllegalStateException("value isn't initialized"); + } else { + ByteBufUtil.writeVarInt(buffer, this.value); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/VarBitBuffer.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/VarBitBuffer.java new file mode 100644 index 000000000..85890c686 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/VarBitBuffer.java @@ -0,0 +1,12 @@ +package dev.imprex.orebfuscator.chunk; + +public interface VarBitBuffer { + + int get(int index); + + void set(int index, int value); + + long[] toArray(); + + int size(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ZeroVarBitBuffer.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ZeroVarBitBuffer.java new file mode 100644 index 000000000..4c1fc7b94 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/chunk/ZeroVarBitBuffer.java @@ -0,0 +1,23 @@ +package dev.imprex.orebfuscator.chunk; + +public record ZeroVarBitBuffer(int size) implements VarBitBuffer { + + public static final long[] EMPTY = new long[0]; + + @Override + public int get(int index) { + return 0; + } + + @Override + public void set(int index, int value) { + if (value != 0) { + throw new IllegalArgumentException("ZeroVarBitBuffer can't hold any value"); + } + } + + @Override + public long[] toArray() { + return EMPTY; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/AbstractWorldConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/AbstractWorldConfig.java new file mode 100644 index 000000000..175fd921e --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/AbstractWorldConfig.java @@ -0,0 +1,146 @@ +package dev.imprex.orebfuscator.config; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import com.google.gson.JsonObject; +import dev.imprex.orebfuscator.config.api.WorldConfig; +import dev.imprex.orebfuscator.config.components.BlockParser; +import dev.imprex.orebfuscator.config.components.ConfigBlockValue; +import dev.imprex.orebfuscator.config.components.WeightedBlockList; +import dev.imprex.orebfuscator.config.components.WorldMatcher; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.MathUtil; +import dev.imprex.orebfuscator.util.WeightedRandom; + +public abstract class AbstractWorldConfig implements WorldConfig { + + private final String name; + + protected boolean enabledValue = false; + protected boolean enabled = false; + + protected int minY = BlockPos.MIN_Y; + protected int maxY = BlockPos.MAX_Y; + + protected final List worldMatchers = new ArrayList<>(); + protected final List weightedBlockLists = new ArrayList<>(); + + public AbstractWorldConfig(String name) { + this.name = name; + } + + protected void deserializeBase(ConfigurationSection section, ConfigParsingContext context) { + this.enabledValue = section.getBoolean("enabled", true); + + int minY = MathUtil.clamp(section.getInt("minY", BlockPos.MIN_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); + int maxY = MathUtil.clamp(section.getInt("maxY", BlockPos.MAX_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); + + this.minY = Math.min(minY, maxY); + this.maxY = Math.max(minY, maxY); + + section.getStringList("worlds").stream() + .map(WorldMatcher::parseMatcher) + .forEach(worldMatchers::add); + + if (this.worldMatchers.isEmpty()) { + context.section("worlds").error(ConfigMessage.MISSING_OR_EMPTY); + } + } + + protected void serializeBase(ConfigurationSection section) { + section.set("enabled", this.enabledValue); + + section.set("minY", this.minY); + section.set("maxY", this.maxY); + + section.set("worlds", worldMatchers.stream() + .map(WorldMatcher::serialize) + .collect(Collectors.toList())); + } + + protected void deserializeRandomBlocks(BlockParser.Factory blockParserFactory, ConfigurationSection section, + ConfigParsingContext context) { + context = context.section("randomBlocks"); + + ConfigurationSection subSectionContainer = section.getSection("randomBlocks"); + if (subSectionContainer == null) { + context.error(ConfigMessage.MISSING_OR_EMPTY); + return; + } + + for (ConfigurationSection subSection : subSectionContainer.getSubSections()) { + ConfigParsingContext subContext = context.section(subSection.getName()); + this.weightedBlockLists.add(new WeightedBlockList(blockParserFactory, subSection, subContext)); + } + + if (this.weightedBlockLists.isEmpty()) { + context.error(ConfigMessage.MISSING_OR_EMPTY); + } + } + + protected void serializeRandomBlocks(ConfigurationSection section) { + ConfigurationSection subSectionContainer = section.createSection("randomBlocks"); + + for (WeightedBlockList weightedBlockList : this.weightedBlockLists) { + weightedBlockList.serialize(subSectionContainer); + } + } + + protected void disableOnError(ConfigParsingContext context) { + this.enabled = context.disableIfError(this.enabledValue); + } + + protected String getName() { + return name; + } + + public JsonObject randomBlocksToJson() { + JsonObject object = new JsonObject(); + for (WeightedBlockList list : weightedBlockLists) { + object.add(list.getName(), ConfigBlockValue.toJson(list.getBlocks())); + } + return object; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + @Override + public int getMinY() { + return this.minY; + } + + @Override + public int getMaxY() { + return this.maxY; + } + + @Override + public boolean matchesWorldName(String worldName) { + for (WorldMatcher matcher : this.worldMatchers) { + if (matcher.test(worldName)) { + return true; + } + } + return false; + } + + @Override + public boolean shouldObfuscate(int y) { + return y >= this.minY && y <= this.maxY; + } + + + WeightedRandom[] createWeightedRandoms(WorldAccessor world) { + OfcLogger.debug(String.format("Creating weighted randoms for %s for world %s:", name, world)); + return WeightedBlockList.create(world, this.weightedBlockLists); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/ConfigLookup.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/ConfigLookup.java new file mode 100644 index 000000000..3af7a23f3 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/ConfigLookup.java @@ -0,0 +1,100 @@ +package dev.imprex.orebfuscator.config; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import dev.imprex.orebfuscator.util.Version; + +public class ConfigLookup { + + private static final Pattern FILENAME_PATTERN = Pattern.compile("config-(?.*)\\.yml"); + + public static Version getConfigVersion(Version version) throws IOException { + List versions = discoverConfigs().stream() + .map(FILENAME_PATTERN::matcher) + .filter(Matcher::find) + .map(m -> m.group("version")) + .map(Version::tryParse) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted(Comparator.reverseOrder()) + .toList(); + + for (Version configVersion : versions) { + if (version.isAtOrAbove(configVersion)) { + return configVersion; + } + } + + return null; + } + + public static InputStream loadConfig(Version version) { + String path = String.format("/config/config-%s.yml", version); + return ConfigLookup.class.getResourceAsStream(path); + } + + private static List discoverConfigs() throws IOException { + var protectionDomain = ConfigLookup.class.getProtectionDomain(); + var codeSource = protectionDomain != null + ? protectionDomain.getCodeSource() + : null; + var location = codeSource != null + ? codeSource.getLocation() + : null; + + if (location == null) { + return Collections.emptyList(); + } + + if (location.getPath().endsWith(".jar")) { + URI jar = URI.create("jar:" + location); + try (FileSystem fileSystem = FileSystems.newFileSystem(jar, Map.of())) { + Path configDir = fileSystem.getPath("/config/"); + if (!Files.isDirectory(configDir)) { + return Collections.emptyList(); + } + + try (var stream = Files.list(configDir)) { + return stream + .map(configDir::relativize) + .map(Path::toString) + .toList(); + } + } + } + + Path jarDir = Paths.get(URI.create(location.toString())); + if (!Files.isDirectory(jarDir)) { + return Collections.emptyList(); + } + + // remap local IDE execution paths + Path baseDir = jarDir.endsWith("build/classes/java/main/") + ? jarDir.resolve("../../../resources/main/config").normalize() + : jarDir.resolve("config/"); + + if (!Files.isDirectory(baseDir)) { + return Collections.emptyList(); + } + + try (var stream = Files.list(baseDir)) { + return stream + .map(baseDir::relativize) + .map(Path::toString) + .toList(); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorAdvancedConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorAdvancedConfig.java new file mode 100644 index 000000000..a10a236b8 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorAdvancedConfig.java @@ -0,0 +1,132 @@ +package dev.imprex.orebfuscator.config; + +import dev.imprex.orebfuscator.config.api.AdvancedConfig; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.logging.OfcLogger; + +public class OrebfuscatorAdvancedConfig implements AdvancedConfig { + + private boolean verbose = false; + + private int obfuscationThreads = -1; + private long obfuscationTimeout = 10_000L; + private int maxMillisecondsPerTick = 10; + + private int proximityThreads = -1; + private int proximityDefaultBucketSize = 50; + private int proximityThreadCheckInterval = 50; + private int proximityPlayerCheckInterval = 5000; + + private boolean obfuscationThreadsSet = false; + private boolean hasObfuscationTimeout = false; + private boolean proximityThreadsSet = false; + private boolean hasProximityPlayerCheckInterval = true; + + public void deserialize(ConfigurationSection section, ConfigParsingContext context) { + this.verbose = section.getBoolean("verbose", false); + + // parse obfuscation section + ConfigParsingContext obfuscationContext = context.section("obfuscation"); + ConfigurationSection obfuscationSection = section.getSection("obfuscation"); + if (obfuscationSection != null) { + this.obfuscationThreads = obfuscationSection.getInt("threads", -1); + this.obfuscationThreadsSet = (this.obfuscationThreads > 0); + + this.obfuscationTimeout = obfuscationSection.getLong("timeout", 10_000L); + this.hasObfuscationTimeout = (this.obfuscationTimeout > 0); + + this.maxMillisecondsPerTick = obfuscationSection.getInt("maxMillisecondsPerTick", 10); + obfuscationContext.errorMinMaxValue("maxMillisecondsPerTick", 1, 50, this.maxMillisecondsPerTick); + } else { + obfuscationContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + // parse proximity section + ConfigParsingContext proximityContext = context.section("proximity"); + ConfigurationSection proximitySection = section.getSection("proximity"); + if (proximitySection != null) { + this.proximityThreads = proximitySection.getInt("threads", -1); + this.proximityThreadsSet = (this.proximityThreads > 0); + + this.proximityDefaultBucketSize = proximitySection.getInt("defaultBucketSize", 50); + proximityContext.errorMinValue("defaultBucketSize", 1, this.proximityDefaultBucketSize); + + this.proximityThreadCheckInterval = proximitySection.getInt("threadCheckInterval", 50); + proximityContext.errorMinValue("threadCheckInterval", 1, this.proximityThreadCheckInterval); + + this.proximityPlayerCheckInterval = proximitySection.getInt("playerCheckInterval", 5000); + this.hasProximityPlayerCheckInterval = (this.proximityPlayerCheckInterval > 0); + } else { + proximityContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + int availableThreads = Runtime.getRuntime().availableProcessors(); + this.obfuscationThreads = obfuscationThreadsSet ? obfuscationThreads : availableThreads; + this.proximityThreads = (int) (proximityThreadsSet ? proximityThreads : Math.ceil(availableThreads / 2f)); + + OfcLogger.setVerboseLogging(this.verbose); + OfcLogger.debug("advanced.obfuscationThreads = " + this.obfuscationThreads); + OfcLogger.debug("advanced.proximityThreads = " + this.proximityThreads); + } + + public void serialize(ConfigurationSection section) { + section.set("verbose", this.verbose); + + section.set("obfuscation.threads", this.obfuscationThreadsSet ? this.obfuscationThreads : -1); + section.set("obfuscation.timeout", this.hasObfuscationTimeout ? this.obfuscationTimeout : -1); + section.set("obfuscation.maxMillisecondsPerTick", this.maxMillisecondsPerTick); + + section.set("proximity.threads", this.proximityThreadsSet ? this.proximityThreads : -1); + section.set("proximity.defaultBucketSize", this.proximityDefaultBucketSize); + section.set("proximity.threadCheckInterval", this.proximityThreadCheckInterval); + section.set("proximity.playerCheckInterval", + this.hasProximityPlayerCheckInterval ? this.proximityPlayerCheckInterval : -1); + } + + @Override + public int obfuscationThreads() { + return this.obfuscationThreads; + } + + @Override + public boolean hasObfuscationTimeout() { + return this.hasObfuscationTimeout; + } + + @Override + public long obfuscationTimeout() { + return this.obfuscationTimeout; + } + + @Override + public int maxMillisecondsPerTick() { + return this.maxMillisecondsPerTick; + } + + @Override + public int proximityThreads() { + return this.proximityThreads; + } + + @Override + public int proximityDefaultBucketSize() { + return this.proximityDefaultBucketSize; + } + + @Override + public int proximityThreadCheckInterval() { + return this.proximityThreadCheckInterval; + } + + @Override + public boolean hasProximityPlayerCheckInterval() { + return this.hasProximityPlayerCheckInterval; + } + + @Override + public int proximityPlayerCheckInterval() { + return this.proximityPlayerCheckInterval; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorBlockFlags.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorBlockFlags.java new file mode 100644 index 000000000..81832563f --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorBlockFlags.java @@ -0,0 +1,89 @@ +package dev.imprex.orebfuscator.config; + +import java.util.Map.Entry; +import dev.imprex.orebfuscator.config.api.BlockFlags; +import dev.imprex.orebfuscator.config.components.ConfigBlockValue; +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; + +public class OrebfuscatorBlockFlags implements BlockFlags { + + private static final BlockFlags EMPTY_FLAGS = new EmptyBlockFlags(); + + static BlockFlags create(RegistryAccessor registry, OrebfuscatorObfuscationConfig worldConfig, + OrebfuscatorProximityConfig proximityConfig) { + if ((worldConfig != null && worldConfig.isEnabled()) || (proximityConfig != null + && proximityConfig.isEnabled())) { + return new OrebfuscatorBlockFlags(registry, worldConfig, proximityConfig); + } + return EMPTY_FLAGS; + } + + private final int[] blockFlags; + + private OrebfuscatorBlockFlags(RegistryAccessor registry, OrebfuscatorObfuscationConfig worldConfig, + OrebfuscatorProximityConfig proximityConfig) { + this.blockFlags = new int[registry.getUniqueBlockStateCount()]; + + if (worldConfig != null && worldConfig.isEnabled()) { + for (ConfigBlockValue blockValue : worldConfig.hiddenBlocks()) { + for (BlockProperties block : blockValue.blocks()) { + this.setBlockBits(block, FLAG_OBFUSCATE); + } + } + } + + if (proximityConfig != null && proximityConfig.isEnabled()) { + for (Entry entry : proximityConfig.hiddenBlocks()) { + for (BlockProperties block : entry.getKey().blocks()) { + this.setBlockBits(block, entry.getValue()); + } + } + for (BlockProperties block : proximityConfig.allowForUseBlockBelow()) { + this.setBlockBits(block, FLAG_ALLOW_FOR_USE_BLOCK_BELOW); + } + } + } + + private void setBlockBits(BlockProperties block, int bits) { + for (BlockStateProperties blockState : block.getBlockStates()) { + int blockMask = this.blockFlags[blockState.getId()] | bits; + + if (blockState.isBlockEntity()) { + blockMask |= FLAG_BLOCK_ENTITY; + } + + this.blockFlags[blockState.getId()] = blockMask; + } + } + + @Override + public int flags(int blockState) { + return this.blockFlags[blockState]; + } + + @Override + public int flags(int blockState, int y) { + int flags = this.blockFlags[blockState]; + + if (ProximityHeightCondition.match(flags, y)) { + flags |= FLAG_PROXIMITY; + } + + return flags; + } + + private static class EmptyBlockFlags implements BlockFlags { + + @Override + public int flags(int blockState) { + return 0; + } + + @Override + public int flags(int blockState, int y) { + return 0; + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java new file mode 100644 index 000000000..3c03d88b1 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java @@ -0,0 +1,166 @@ +package dev.imprex.orebfuscator.config; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Path; +import java.util.concurrent.TimeUnit; + +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.interop.ServerAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.ChunkCacheKey; + +public class OrebfuscatorCacheConfig implements CacheConfig { + + private final Path worldDirectory; + + private boolean enabledValue = true; + private int maximumSize = 32768; + private long expireAfterAccess = TimeUnit.SECONDS.toMillis(60); + + private boolean enableDiskCacheValue = true; + private Path baseDirectory; + private int maximumOpenRegionFiles = 256; + private long deleteRegionFilesAfterAccess = TimeUnit.DAYS.toMillis(2); + private int maximumTaskQueueSize = 32768; + + // feature enabled states after context evaluation + private boolean enabled = false; + private boolean enableDiskCache = false; + + public OrebfuscatorCacheConfig(ServerAccessor server) { + this.worldDirectory = server.getWorldDirectory().normalize(); + this.baseDirectory = this.worldDirectory.resolve("orebfuscator_cache/"); + } + + public void deserialize(ConfigurationSection section, ConfigParsingContext context) { + this.enabledValue = section.getBoolean("enabled", true); + + // parse memoryCache section + ConfigParsingContext memoryContext = context.section("memoryCache"); + ConfigurationSection memorySection = section.getSection("memoryCache"); + if (memorySection != null) { + this.maximumSize = memorySection.getInt("maximumSize", 32768); + memoryContext.errorMinValue("maximumSize", 1, this.maximumSize); + + this.expireAfterAccess = memorySection.getLong("expireAfterAccess", TimeUnit.SECONDS.toMillis(60)); + memoryContext.errorMinValue("expireAfterAccess", 1, this.expireAfterAccess); + } else { + memoryContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + // parse diskCache section, isolate errors to disable only diskCache on section error + ConfigParsingContext diskContext = context.section("diskCache", true); + ConfigurationSection diskSection = section.getSection("diskCache"); + if (diskSection != null) { + this.enableDiskCacheValue = diskSection.getBoolean("enabled", true); + this.baseDirectory = this.deserializeBaseDirectory(diskSection, diskContext, "orebfuscator_cache/"); + + this.maximumOpenRegionFiles = diskSection.getInt("maximumOpenFiles", 256); + diskContext.errorMinValue("maximumOpenFiles", 1, this.maximumOpenRegionFiles); + + this.deleteRegionFilesAfterAccess = diskSection.getLong("deleteFilesAfterAccess", + TimeUnit.DAYS.toMillis(2)); + diskContext.errorMinValue("deleteFilesAfterAccess", 1, this.deleteRegionFilesAfterAccess); + + this.maximumTaskQueueSize = diskSection.getInt("maximumTaskQueueSize", 32768); + diskContext.errorMinValue("maximumTaskQueueSize", 1, this.maximumTaskQueueSize); + } else { + diskContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + // try to create diskCache.directory + if (this.enabledValue && this.enableDiskCacheValue) { + OfcLogger.debug("Using '" + this.baseDirectory.toAbsolutePath() + "' as chunk cache path"); + try { + if (Files.notExists(this.baseDirectory)) { + Files.createDirectories(this.baseDirectory); + } + } catch (IOException e) { + diskContext.error(ConfigMessage.CACHE_CANT_CREATE, e); + } + } + + // disable features if their config sections contain errors + this.enabled = context.disableIfError(this.enabledValue); + this.enableDiskCache = diskContext.disableIfError(this.enableDiskCacheValue); + } + + public void serialize(ConfigurationSection section) { + section.set("enabled", this.enabledValue); + + section.set("memoryCache.maximumSize", this.maximumSize); + section.set("memoryCache.expireAfterAccess", this.expireAfterAccess); + + section.set("diskCache.enabled", this.enableDiskCacheValue); + + String directoryString = this.worldDirectory.relativize(baseDirectory).toString(); + section.set("diskCache.directory", directoryString); + + section.set("diskCache.maximumOpenFiles", this.maximumOpenRegionFiles); + section.set("diskCache.deleteFilesAfterAccess", this.deleteRegionFilesAfterAccess); + section.set("diskCache.maximumTaskQueueSize", this.maximumTaskQueueSize); + } + + private Path deserializeBaseDirectory(ConfigurationSection section, ConfigParsingContext context, + String defaultPath) { + String baseDirectory = section.getString("directory", defaultPath); + + try { + return this.worldDirectory.resolve(baseDirectory).normalize(); + } catch (InvalidPathException e) { + context.warn("directory", ConfigMessage.CACHE_INVALID_PATH, baseDirectory, defaultPath); + return this.worldDirectory.resolve(defaultPath).normalize(); + } + } + + @Override + public boolean enabled() { + return this.enabled; + } + + @Override + public int maximumSize() { + return this.maximumSize; + } + + @Override + public long expireAfterAccess() { + return this.expireAfterAccess; + } + + @Override + public boolean enableDiskCache() { + return this.enableDiskCache; + } + + @Override + public Path baseDirectory() { + return this.baseDirectory; + } + + @Override + public Path regionFile(ChunkCacheKey key) { + return this.baseDirectory.resolve(key.world()) + .resolve("r." + (key.x() >> 5) + "." + (key.z() >> 5) + ".mca"); + } + + @Override + public int maximumOpenRegionFiles() { + return this.maximumOpenRegionFiles; + } + + @Override + public long deleteRegionFilesAfterAccess() { + return this.deleteRegionFilesAfterAccess; + } + + @Override + public int maximumTaskQueueSize() { + return this.maximumTaskQueueSize; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorConfig.java new file mode 100644 index 000000000..6c2ca44b5 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorConfig.java @@ -0,0 +1,442 @@ +package dev.imprex.orebfuscator.config; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import com.google.common.hash.Hashing; +import com.google.gson.JsonObject; +import dev.imprex.orebfuscator.config.api.AdvancedConfig; +import dev.imprex.orebfuscator.config.api.BlockFlags; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.config.api.GeneralConfig; +import dev.imprex.orebfuscator.config.api.ObfuscationConfig; +import dev.imprex.orebfuscator.config.api.ProximityConfig; +import dev.imprex.orebfuscator.config.api.WorldConfigBundle; +import dev.imprex.orebfuscator.config.components.BlockParser; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.context.DefaultConfigParsingContext; +import dev.imprex.orebfuscator.config.migrations.ConfigMigrator; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.config.yaml.InvalidConfigurationException; +import dev.imprex.orebfuscator.config.yaml.YamlConfiguration; +import dev.imprex.orebfuscator.interop.ServerAccessor; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.Version; +import dev.imprex.orebfuscator.util.WeightedRandom; + +public class OrebfuscatorConfig implements Config { + + private static final int CONFIG_VERSION = 5; + + private final OrebfuscatorGeneralConfig generalConfig = new OrebfuscatorGeneralConfig(); + private final OrebfuscatorAdvancedConfig advancedConfig = new OrebfuscatorAdvancedConfig(); + private final OrebfuscatorCacheConfig cacheConfig; + + private final List obfuscationConfigs = new ArrayList<>(); + private final List proximityConfigs = new ArrayList<>(); + + private final Map worldConfigBundles = new WeakHashMap<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + + private final ServerAccessor server; + private final Path path; + private final YamlConfiguration configuration; + + private byte[] systemHash; + private String configReport; + + public OrebfuscatorConfig(ServerAccessor server) { + this.server = server; + this.path = server.getConfigDirectory().resolve("config.yml"); + + this.cacheConfig = new OrebfuscatorCacheConfig(this.server); + this.configuration = this.loadConfiguration(); + } + + public YamlConfiguration loadConfiguration() { + try { + // try to create default config if necessary + if (Files.notExists(this.path)) { + Files.createDirectories(this.path.getParent()); + + Version version = this.server.getMinecraftVersion(); + Version configVersion = ConfigLookup.getConfigVersion(version); + + OfcLogger.info( + String.format("No config found, creating default config for version %s and above", configVersion)); + + try (InputStream stream = Objects.requireNonNull(ConfigLookup.loadConfig(configVersion), + "Can't find default config for version: " + version)) { + Files.copy(stream, path); + } + } + + YamlConfiguration configuration = YamlConfiguration.loadConfig(this.path); + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + + this.deserialize(configuration, context); + this.configReport = context.report(); + + if (context.hasErrors()) { + OfcLogger.error(this.configReport, null); + throw new IllegalArgumentException( + "Can't parse config due to errors, Orebfuscator will now disable itself!"); + } else if (this.configReport != null) { + OfcLogger.warn(this.configReport); + } + + this.systemHash = this.calculateSystemHash(configuration); + + return configuration; + } catch (IOException | InvalidConfigurationException e) { + throw new RuntimeException("Unable to create/read config", e); + } + } + + public void store() { + this.configuration.clear(); + this.serialize(this.configuration); + + try { + this.configuration.save(this.path); + } catch (IOException e) { + OfcLogger.error("Can't save config", e); + } + } + + private byte[] calculateSystemHash(YamlConfiguration configuration) throws IOException { + return Hashing.murmur3_128().newHasher() + .putBytes(this.server.getOrebfuscatorVersion().getBytes(StandardCharsets.UTF_8)) + .putBytes(this.server.getMinecraftVersion().toString().getBytes(StandardCharsets.UTF_8)) + .putBytes(configuration.withoutComments().getBytes(StandardCharsets.UTF_8)) + .hash().asBytes(); + } + + private void deserialize(YamlConfiguration configuration, ConfigParsingContext context) { + if (ConfigMigrator.willMigrate(configuration)) { + try { + configuration.save(server.getConfigDirectory().resolve("config-old.yml")); + } catch (IOException e) { + OfcLogger.error("Can't save original config before migration", e); + } + } + + // try to migrate to latest version + ConfigMigrator.migrateToLatestVersion(configuration); + + // instantly fail on invalid config version + if (configuration.getInt("version", -1) != CONFIG_VERSION) { + throw new RuntimeException("config is not up to date, please delete your config"); + } + + this.obfuscationConfigs.clear(); + this.proximityConfigs.clear(); + this.worldConfigBundles.clear(); + + // parse general section + ConfigParsingContext generalContext = context.section("general"); + ConfigurationSection generalSection = configuration.getSection("general"); + if (generalSection != null) { + this.generalConfig.deserialize(generalSection, generalContext); + } else { + generalContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + // parse advanced section + ConfigParsingContext advancedContext = context.section("advanced"); + ConfigurationSection advancedSection = configuration.getSection("advanced"); + if (advancedSection != null) { + this.advancedConfig.deserialize(advancedSection, advancedContext); + } else { + advancedContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + // parse cache section + ConfigParsingContext cacheContext = context.section("cache", true); + ConfigurationSection cacheSection = configuration.getSection("cache"); + if (cacheSection != null) { + this.cacheConfig.deserialize(cacheSection, cacheContext); + } else { + cacheContext.warn(ConfigMessage.MISSING_USING_DEFAULTS); + } + + final BlockParser.Factory blockParserFactory = BlockParser.factory(server.getRegistry()); + + // parse obfuscation sections + ConfigParsingContext obfuscationContext = context.section("obfuscation"); + ConfigurationSection obfuscationSection = configuration.getSection("obfuscation"); + if (obfuscationSection != null) { + for (ConfigurationSection config : obfuscationSection.getSubSections()) { + ConfigParsingContext configContext = obfuscationContext.section(config.getName(), true); + this.obfuscationConfigs.add( + new OrebfuscatorObfuscationConfig(blockParserFactory, config, configContext)); + } + } + if (this.obfuscationConfigs.isEmpty()) { + obfuscationContext.warn(ConfigMessage.MISSING_OR_EMPTY); + } + + // parse proximity sections + ConfigParsingContext proximityContext = context.section("proximity"); + ConfigurationSection proximitySection = configuration.getSection("proximity"); + if (proximitySection != null) { + for (ConfigurationSection config : proximitySection.getSubSections()) { + ConfigParsingContext configContext = proximityContext.section(config.getName(), true); + this.proximityConfigs.add(new OrebfuscatorProximityConfig(blockParserFactory, config, configContext)); + } + } + if (this.proximityConfigs.isEmpty()) { + proximityContext.warn(ConfigMessage.MISSING_OR_EMPTY); + } + + for (WorldAccessor world : this.server.getWorlds()) { + this.worldConfigBundles.put(world, new OrebfuscatorWorldConfigBundle(world)); + } + } + + private void serialize(ConfigurationSection section) { + section.set("version", CONFIG_VERSION); + + this.generalConfig.serialize(section.createSection("general")); + this.advancedConfig.serialize(section.createSection("advanced")); + this.cacheConfig.serialize(section.createSection("cache")); + + ConfigurationSection obfuscation = section.createSection("obfuscation"); + for (OrebfuscatorObfuscationConfig obfuscationConfig : this.obfuscationConfigs) { + obfuscationConfig.serialize(obfuscation.createSection(obfuscationConfig.getName())); + } + + ConfigurationSection proximity = section.createSection("proximity"); + for (OrebfuscatorProximityConfig proximityConfig : this.proximityConfigs) { + proximityConfig.serialize(proximity.createSection(proximityConfig.getName())); + } + } + + public JsonObject toJson() { + JsonObject object = new JsonObject(); + + for (var config : obfuscationConfigs) { + object.add(config.getName(), config.toJson()); + } + + for (var config : proximityConfigs) { + object.add(config.getName(), config.toJson()); + } + + return object; + } + + @Override + public byte[] systemHash() { + return systemHash; + } + + @Override + public String report() { + return configReport; + } + + @Override + public GeneralConfig general() { + return this.generalConfig; + } + + @Override + public AdvancedConfig advanced() { + return this.advancedConfig; + } + + @Override + public CacheConfig cache() { + return this.cacheConfig; + } + + @Override + public WorldConfigBundle world(WorldAccessor world) { + return this.getWorldConfigBundle(world); + } + + @Override + public boolean proximityEnabled() { + for (ProximityConfig proximityConfig : this.proximityConfigs) { + if (proximityConfig.isEnabled()) { + return true; + } + } + return false; + } + + public boolean usesBlockSpecificConfigs() { + for (OrebfuscatorProximityConfig config : this.proximityConfigs) { + if (config.usesBlockSpecificConfigs()) { + return true; + } + } + return false; + } + + public boolean usesFrustumCulling() { + for (ProximityConfig config : this.proximityConfigs) { + if (config.frustumCullingEnabled()) { + return true; + } + } + return false; + } + + public String usesRayCastCheck() { + for (ProximityConfig config : this.proximityConfigs) { + if (config.rayCastCheckEnabled()) { + return config.rayCastCheckOnlyCheckCenter() ? "center" : "true"; + } + } + return "false"; + } + + private OrebfuscatorWorldConfigBundle getWorldConfigBundle(WorldAccessor world) { + this.lock.readLock().lock(); + try { + OrebfuscatorWorldConfigBundle worldConfigs = this.worldConfigBundles.get(Objects.requireNonNull(world)); + if (worldConfigs != null) { + return worldConfigs; + } + } finally { + this.lock.readLock().unlock(); + } + + OrebfuscatorWorldConfigBundle worldConfigs = new OrebfuscatorWorldConfigBundle(world); + this.lock.writeLock().lock(); + try { + this.worldConfigBundles.putIfAbsent(world, worldConfigs); + return this.worldConfigBundles.get(world); + } finally { + this.lock.writeLock().unlock(); + } + } + + private class OrebfuscatorWorldConfigBundle implements WorldConfigBundle { + + private final OrebfuscatorObfuscationConfig obfuscationConfig; + private final OrebfuscatorProximityConfig proximityConfig; + + private final BlockFlags blockFlags; + private final boolean needsObfuscation; + + private final int minY; + private final int maxY; + + private final int minSectionIndex; + private final int maxSectionIndex; + + private final WorldAccessor world; + private final WeightedRandom[] obfuscationRandoms; + private final WeightedRandom[] proximityRandoms; + + public OrebfuscatorWorldConfigBundle(WorldAccessor world) { + String worldName = world.getName(); + this.world = world; + + this.obfuscationConfig = findConfig(obfuscationConfigs, worldName, "obfuscation"); + this.proximityConfig = findConfig(proximityConfigs, worldName, "proximity"); + + this.blockFlags = OrebfuscatorBlockFlags.create(server.getRegistry(), obfuscationConfig, proximityConfig); + this.needsObfuscation = obfuscationConfig != null && obfuscationConfig.isEnabled() || + proximityConfig != null && proximityConfig.isEnabled(); + + this.minY = Math.min( + this.obfuscationConfig != null ? this.obfuscationConfig.getMinY() : BlockPos.MAX_Y, + this.proximityConfig != null ? this.proximityConfig.getMinY() : BlockPos.MAX_Y); + this.maxY = Math.max( + this.obfuscationConfig != null ? this.obfuscationConfig.getMaxY() : BlockPos.MIN_Y, + this.proximityConfig != null ? this.proximityConfig.getMaxY() : BlockPos.MIN_Y); + + this.minSectionIndex = world.getSectionIndex(this.minY); + this.maxSectionIndex = world.getSectionIndex(this.maxY - 1) + 1; + + this.obfuscationRandoms = this.obfuscationConfig != null + ? this.obfuscationConfig.createWeightedRandoms(world) : null; + this.proximityRandoms = this.proximityConfig != null + ? this.proximityConfig.createWeightedRandoms(world) : null; + } + + private T findConfig(Collection configs, String worldName, + String configType) { + List matchingConfigs = configs.stream() + .filter(config -> config.matchesWorldName(worldName)) + .toList(); + + if (matchingConfigs.size() > 1) { + OfcLogger.warn(String.format("world '%s' has more than one %s config choosing first one", worldName, + configType)); + } + + T config = !matchingConfigs.isEmpty() ? matchingConfigs.get(0) : null; + String configName = config == null ? "null" : config.getName(); + + OfcLogger.debug(String.format("using '%s' %s config for world '%s'", configName, configType, worldName)); + + return config; + } + + @Override + public BlockFlags blockFlags() { + return this.blockFlags; + } + + @Override + public ObfuscationConfig obfuscation() { + return this.obfuscationConfig; + } + + @Override + public ProximityConfig proximity() { + return this.proximityConfig; + } + + @Override + public boolean needsObfuscation() { + return this.needsObfuscation; + } + + @Override + public int minSectionIndex() { + return this.minSectionIndex; + } + + @Override + public int maxSectionIndex() { + return this.maxSectionIndex; + } + + @Override + public boolean shouldObfuscate(int y) { + return y >= this.minY && y <= this.maxY; + } + + @Override + public int nextRandomObfuscationBlock(int y) { + return this.obfuscationRandoms != null + ? this.obfuscationRandoms[y - this.world.getMinBuildHeight()].next() : 0; + } + + @Override + public int nextRandomProximityBlock(int y) { + return this.proximityRandoms != null + ? this.proximityRandoms[y - this.world.getMinBuildHeight()].next() : 0; + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorGeneralConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorGeneralConfig.java new file mode 100644 index 000000000..4dea68504 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorGeneralConfig.java @@ -0,0 +1,57 @@ +package dev.imprex.orebfuscator.config; + +import dev.imprex.orebfuscator.config.api.GeneralConfig; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; + +public class OrebfuscatorGeneralConfig implements GeneralConfig { + + private boolean checkForUpdates = true; + private boolean updateOnBlockDamage = true; + private boolean bypassNotification = true; + private boolean ignoreSpectator = false; + private int updateRadius = 2; + + public void deserialize(ConfigurationSection section, ConfigParsingContext context) { + this.checkForUpdates = section.getBoolean("checkForUpdates", true); + this.updateOnBlockDamage = section.getBoolean("updateOnBlockDamage", true); + this.bypassNotification = section.getBoolean("bypassNotification", true); + this.ignoreSpectator = section.getBoolean("ignoreSpectator", false); + + this.updateRadius = section.getInt("updateRadius", 2); + context.errorMinValue("updateRadius", 1, this.updateRadius); + } + + public void serialize(ConfigurationSection section) { + section.set("checkForUpdates", this.checkForUpdates); + section.set("updateOnBlockDamage", this.updateOnBlockDamage); + section.set("bypassNotification", this.bypassNotification); + section.set("ignoreSpectator", this.ignoreSpectator); + section.set("updateRadius", this.updateRadius); + } + + @Override + public boolean checkForUpdates() { + return this.checkForUpdates; + } + + @Override + public boolean updateOnBlockDamage() { + return this.updateOnBlockDamage; + } + + @Override + public boolean bypassNotification() { + return this.bypassNotification; + } + + @Override + public boolean ignoreSpectator() { + return this.ignoreSpectator; + } + + @Override + public int updateRadius() { + return this.updateRadius; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorObfuscationConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorObfuscationConfig.java new file mode 100644 index 000000000..e1dc29b1b --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorObfuscationConfig.java @@ -0,0 +1,86 @@ +package dev.imprex.orebfuscator.config; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import com.google.gson.JsonObject; +import dev.imprex.orebfuscator.config.api.ObfuscationConfig; +import dev.imprex.orebfuscator.config.components.BlockParser; +import dev.imprex.orebfuscator.config.components.ConfigBlockValue; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; + +public class OrebfuscatorObfuscationConfig extends AbstractWorldConfig implements ObfuscationConfig { + + private boolean layerObfuscation = false; + + private final Set hiddenBlocks = new LinkedHashSet<>(); + + OrebfuscatorObfuscationConfig(BlockParser.Factory blockParserFactory, ConfigurationSection section, + ConfigParsingContext context) { + super(section.getName()); + this.deserializeBase(section, context); + + this.layerObfuscation = section.getBoolean("layerObfuscation", false); + + this.deserializeHiddenBlocks(blockParserFactory, section, context); + this.deserializeRandomBlocks(blockParserFactory, section, context); + this.disableOnError(context); + } + + void serialize(ConfigurationSection section) { + this.serializeBase(section); + + section.set("layerObfuscation", this.layerObfuscation); + + this.serializeHiddenBlocks(section); + this.serializeRandomBlocks(section); + } + + private void deserializeHiddenBlocks(BlockParser.Factory blockParserFactory, ConfigurationSection section, + ConfigParsingContext context) { + context = context.section("hiddenBlocks"); + + final BlockParser blockParser = blockParserFactory.excludeAir(); + + boolean isEmpty = true; + for (String value : section.getStringList("hiddenBlocks")) { + var parsed = blockParser.parse(context, value); + this.hiddenBlocks.add(parsed); + isEmpty &= parsed.blocks().isEmpty(); + } + + if (isEmpty) { + context.error(ConfigMessage.MISSING_OR_EMPTY); + } + } + + private void serializeHiddenBlocks(ConfigurationSection section) { + List blockNames = new ArrayList<>(); + + for (ConfigBlockValue block : this.hiddenBlocks) { + blockNames.add(block.value()); + } + + section.set("hiddenBlocks", blockNames); + } + + public JsonObject toJson() { + JsonObject object = new JsonObject(); + object.add("hiddenBlocks", ConfigBlockValue.toJson(hiddenBlocks)); + object.add("randomBlocks", randomBlocksToJson()); + return object; + } + + @Override + public boolean layerObfuscation() { + return this.layerObfuscation; + } + + @Override + public Iterable hiddenBlocks() { + return this.hiddenBlocks; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java new file mode 100644 index 000000000..509c8b7a6 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java @@ -0,0 +1,211 @@ +package dev.imprex.orebfuscator.config; + +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import org.joml.Matrix4f; +import com.google.gson.JsonObject; +import dev.imprex.orebfuscator.config.api.BlockFlags; +import dev.imprex.orebfuscator.config.api.ProximityConfig; +import dev.imprex.orebfuscator.config.components.BlockParser; +import dev.imprex.orebfuscator.config.components.ConfigBlockValue; +import dev.imprex.orebfuscator.config.components.WeightedBlockList; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.util.BlockProperties; + +public class OrebfuscatorProximityConfig extends AbstractWorldConfig implements ProximityConfig { + + private int distance = 24; + + private boolean frustumCullingEnabled = true; + private float frustumCullingMinDistance = 3; + private float frustumCullingFov = 80f; + + private float frustumCullingMinDistanceSquared = 9; + private final Matrix4f frustumCullingProjectionMatrix; + + private boolean rayCastCheckEnabled = false; + private boolean rayCastCheckOnlyCheckCenter = false; + private int defaultBlockFlags = (ProximityHeightCondition.MATCH_ALL | BlockFlags.FLAG_USE_BLOCK_BELOW); + + private boolean usesBlockSpecificConfigs = false; + private final Map hiddenBlocks = new LinkedHashMap<>(); + private final Set allowForUseBlockBelow = new HashSet<>(); + + OrebfuscatorProximityConfig(BlockParser.Factory blockParserFactory, ConfigurationSection section, + ConfigParsingContext context) { + super(section.getName()); + this.deserializeBase(section, context); + + this.distance = section.getInt("distance", 24); + context.errorMinValue("distance", 1, this.distance); + + this.frustumCullingEnabled = section.getBoolean("frustumCulling.enabled", false); + this.frustumCullingMinDistance = section.getDouble("frustumCulling.minDistance", 3d).floatValue(); + this.frustumCullingFov = section.getDouble("frustumCulling.fov", 80d).floatValue(); + + if (this.frustumCullingEnabled) { + context.errorMinMaxValue("frustumCulling.fov", 10, 170, (int) this.frustumCullingFov); + } + + this.frustumCullingMinDistanceSquared = this.frustumCullingMinDistance * this.frustumCullingMinDistance; + this.frustumCullingProjectionMatrix = new Matrix4f() // create projection matrix with aspect 16:9 + .perspective(this.frustumCullingFov, 16f / 9f, 0.01f, 2 * this.distance); + + this.rayCastCheckEnabled = section.getBoolean("rayCastCheck.enabled", false); + this.rayCastCheckOnlyCheckCenter = section.getBoolean("rayCastCheck.onlyCheckCenter", false); + + this.defaultBlockFlags = ProximityHeightCondition.create(this.minY, this.maxY); + if (section.getBoolean("useBlockBelow", true)) { + this.defaultBlockFlags |= BlockFlags.FLAG_USE_BLOCK_BELOW; + } + + this.deserializeHiddenBlocks(blockParserFactory, section, context); + this.deserializeRandomBlocks(blockParserFactory, section, context); + + for (WeightedBlockList blockList : this.weightedBlockLists) { + this.allowForUseBlockBelow.addAll(blockList.getBlocks().stream() + .flatMap(value -> value.blocks().stream()) + .toList()); + } + + this.disableOnError(context); + } + + protected void serialize(ConfigurationSection section) { + this.serializeBase(section); + + section.set("distance", this.distance); + + section.set("frustumCulling.enabled", this.frustumCullingEnabled); + section.set("frustumCulling.minDistance", this.frustumCullingMinDistance); + section.set("frustumCulling.fov", this.frustumCullingFov); + + section.set("rayCastCheck.enabled", this.rayCastCheckEnabled); + section.set("rayCastCheck.onlyCheckCenter", this.rayCastCheckOnlyCheckCenter); + section.set("useBlockBelow", BlockFlags.isUseBlockBelowBitSet(this.defaultBlockFlags)); + + this.serializeHiddenBlocks(section); + this.serializeRandomBlocks(section); + } + + private void deserializeHiddenBlocks(BlockParser.Factory blockParserFactory, ConfigurationSection section, + ConfigParsingContext context) { + context = context.section("hiddenBlocks"); + + ConfigurationSection blockSection = section.getSection("hiddenBlocks"); + if (blockSection == null) { + return; + } + + final BlockParser blockParser = blockParserFactory.excludeAir(); + + boolean isEmpty = true; + for (ConfigurationSection block : blockSection.getSubSections()) { + ConfigBlockValue parsed = blockParser.parse(context, block.getName()); + + int blockFlags = this.defaultBlockFlags; + + // parse block specific height condition + if (block.isNumber("minY") && block.isNumber("maxY")) { + int minY = block.getInt("minY", this.minY); + int maxY = block.getInt("maxY", this.maxY); + + blockFlags = ProximityHeightCondition.remove(blockFlags); + blockFlags |= ProximityHeightCondition.create( + Math.min(minY, maxY), + Math.max(minY, maxY)); + usesBlockSpecificConfigs = true; + } + + // parse block specific flags + if (block.isBoolean("useBlockBelow")) { + if (block.getBoolean("useBlockBelow", true)) { + blockFlags |= BlockFlags.FLAG_USE_BLOCK_BELOW; + } else { + blockFlags &= ~BlockFlags.FLAG_USE_BLOCK_BELOW; + } + usesBlockSpecificConfigs = true; + } + + this.hiddenBlocks.put(parsed, blockFlags); + isEmpty &= parsed.blocks().isEmpty(); + } + + if (isEmpty) { + context.error(ConfigMessage.MISSING_OR_EMPTY); + } + } + + private void serializeHiddenBlocks(ConfigurationSection section) { + ConfigurationSection blockSection = section.createSection("hiddenBlocks"); + + for (Map.Entry entry : this.hiddenBlocks.entrySet()) { + ConfigurationSection block = blockSection.createSection(entry.getKey().value()); + + int blockFlags = entry.getValue(); + if (!ProximityHeightCondition.equals(blockFlags, this.defaultBlockFlags)) { + block.set("minY", ProximityHeightCondition.getMinY(blockFlags)); + block.set("maxY", ProximityHeightCondition.getMaxY(blockFlags)); + } + + if (BlockFlags.isUseBlockBelowBitSet(blockFlags) != BlockFlags.isUseBlockBelowBitSet( + this.defaultBlockFlags)) { + block.set("useBlockBelow", BlockFlags.isUseBlockBelowBitSet(blockFlags)); + } + } + } + + public JsonObject toJson() { + JsonObject object = new JsonObject(); + object.add("hiddenBlocks", ConfigBlockValue.toJson(hiddenBlocks.keySet())); + object.add("randomBlocks", randomBlocksToJson()); + return object; + } + + @Override + public int distance() { + return this.distance; + } + + @Override + public boolean frustumCullingEnabled() { + return this.frustumCullingEnabled; + } + + @Override + public float frustumCullingMinDistanceSquared() { + return this.frustumCullingMinDistanceSquared; + } + + @Override + public Matrix4f frustumCullingProjectionMatrix() { + return new Matrix4f(frustumCullingProjectionMatrix); + } + + @Override + public boolean rayCastCheckEnabled() { + return this.rayCastCheckEnabled; + } + + @Override + public boolean rayCastCheckOnlyCheckCenter() { + return this.rayCastCheckOnlyCheckCenter; + } + + @Override + public Iterable> hiddenBlocks() { + return this.hiddenBlocks.entrySet(); + } + + public Iterable allowForUseBlockBelow() { + return this.allowForUseBlockBelow; + } + + boolean usesBlockSpecificConfigs() { + return usesBlockSpecificConfigs; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/ProximityHeightCondition.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/ProximityHeightCondition.java new file mode 100644 index 000000000..aaed0a246 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/ProximityHeightCondition.java @@ -0,0 +1,55 @@ +package dev.imprex.orebfuscator.config; + +import dev.imprex.orebfuscator.util.BlockPos; + +/** + * Only use 25 MSBs of blockFlags for ProximityHeightCondition 12 bit min y | 12 bit max y | 1 bit present + */ +public class ProximityHeightCondition { + + public static final int MATCH_ALL = ProximityHeightCondition.create(BlockPos.MIN_Y, BlockPos.MAX_Y); + + public static int clampY(int y) { + return Math.min(BlockPos.MAX_Y, Math.max(BlockPos.MIN_Y, y)); + } + + private static int unsigned(int y) { + return (clampY(y) - BlockPos.MIN_Y) & 0xFFF; + } + + private static int signed(int y) { + return y + BlockPos.MIN_Y; + } + + public static int create(int minY, int maxY) { + return unsigned(minY) << 20 | unsigned(maxY) << 8 | 0x80; + } + + public static int remove(int hideCondition) { + return hideCondition & 0x7F; + } + + private static int extractHideCondition(int hideCondition) { + return hideCondition & 0xFFFFFF80; + } + + public static boolean equals(int a, int b) { + return extractHideCondition(a) == extractHideCondition(b); + } + + public static boolean match(int hideCondition, int y) { + return isPresent(hideCondition) && getMinY(hideCondition) <= y && getMaxY(hideCondition) >= y; + } + + public static boolean isPresent(int hideCondition) { + return (hideCondition & 0x80) != 0; + } + + public static int getMinY(int hideCondition) { + return signed(hideCondition >>> 20); + } + + public static int getMaxY(int hideCondition) { + return signed(hideCondition << 12 >>> 20); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/AdvancedConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/AdvancedConfig.java new file mode 100644 index 000000000..93427ce5b --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/AdvancedConfig.java @@ -0,0 +1,22 @@ +package dev.imprex.orebfuscator.config.api; + +public interface AdvancedConfig { + + int obfuscationThreads(); + + boolean hasObfuscationTimeout(); + + long obfuscationTimeout(); + + int maxMillisecondsPerTick(); + + int proximityThreads(); + + int proximityDefaultBucketSize(); + + int proximityThreadCheckInterval(); + + boolean hasProximityPlayerCheckInterval(); + + int proximityPlayerCheckInterval(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/BlockFlags.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/BlockFlags.java new file mode 100644 index 000000000..98e36a54c --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/BlockFlags.java @@ -0,0 +1,42 @@ +package dev.imprex.orebfuscator.config.api; + +public interface BlockFlags { + + int FLAG_OBFUSCATE = 1; + int FLAG_BLOCK_ENTITY = 2; + int FLAG_PROXIMITY = 4; + int FLAG_USE_BLOCK_BELOW = 8; + int FLAG_ALLOW_FOR_USE_BLOCK_BELOW = 16; + + static boolean isEmpty(int mask) { + return (mask & 0xFF) == 0; + } + + static boolean isBitSet(int mask, int flag) { + return (mask & flag) != 0; + } + + static boolean isObfuscateBitSet(int mask) { + return isBitSet(mask, FLAG_OBFUSCATE); + } + + static boolean isBlockEntityBitSet(int mask) { + return isBitSet(mask, FLAG_BLOCK_ENTITY); + } + + static boolean isProximityBitSet(int mask) { + return isBitSet(mask, FLAG_PROXIMITY); + } + + static boolean isUseBlockBelowBitSet(int mask) { + return isBitSet(mask, FLAG_USE_BLOCK_BELOW); + } + + static boolean isAllowForUseBlockBelowBitSet(int mask) { + return isBitSet(mask, FLAG_ALLOW_FOR_USE_BLOCK_BELOW); + } + + int flags(int blockState); + + int flags(int blockState, int y); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/CacheConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/CacheConfig.java new file mode 100644 index 000000000..ae4569e7e --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/CacheConfig.java @@ -0,0 +1,25 @@ +package dev.imprex.orebfuscator.config.api; + +import java.nio.file.Path; +import dev.imprex.orebfuscator.util.ChunkCacheKey; + +public interface CacheConfig { + + boolean enabled(); + + int maximumSize(); + + long expireAfterAccess(); + + boolean enableDiskCache(); + + Path baseDirectory(); + + Path regionFile(ChunkCacheKey chunkPosition); + + int maximumOpenRegionFiles(); + + long deleteRegionFilesAfterAccess(); + + int maximumTaskQueueSize(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/Config.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/Config.java new file mode 100644 index 000000000..5cd05f261 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/Config.java @@ -0,0 +1,20 @@ +package dev.imprex.orebfuscator.config.api; + +import dev.imprex.orebfuscator.interop.WorldAccessor; + +public interface Config { + + byte[] systemHash(); + + String report(); + + GeneralConfig general(); + + AdvancedConfig advanced(); + + CacheConfig cache(); + + WorldConfigBundle world(WorldAccessor world); + + boolean proximityEnabled(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/GeneralConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/GeneralConfig.java new file mode 100644 index 000000000..96a5972c4 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/GeneralConfig.java @@ -0,0 +1,14 @@ +package dev.imprex.orebfuscator.config.api; + +public interface GeneralConfig { + + boolean checkForUpdates(); + + boolean updateOnBlockDamage(); + + boolean bypassNotification(); + + boolean ignoreSpectator(); + + int updateRadius(); +} \ No newline at end of file diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/ObfuscationConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/ObfuscationConfig.java new file mode 100644 index 000000000..ddb22cff1 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/ObfuscationConfig.java @@ -0,0 +1,10 @@ +package dev.imprex.orebfuscator.config.api; + +import dev.imprex.orebfuscator.config.components.ConfigBlockValue; + +public interface ObfuscationConfig extends WorldConfig { + + boolean layerObfuscation(); + + Iterable hiddenBlocks(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/ProximityConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/ProximityConfig.java new file mode 100644 index 000000000..b529f4e08 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/ProximityConfig.java @@ -0,0 +1,24 @@ +package dev.imprex.orebfuscator.config.api; + +import java.util.Map; + +import org.joml.Matrix4f; + +import dev.imprex.orebfuscator.config.components.ConfigBlockValue; + +public interface ProximityConfig extends WorldConfig { + + int distance(); + + boolean frustumCullingEnabled(); + + float frustumCullingMinDistanceSquared(); + + Matrix4f frustumCullingProjectionMatrix(); + + boolean rayCastCheckEnabled(); + + boolean rayCastCheckOnlyCheckCenter(); + + Iterable> hiddenBlocks(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/WorldConfig.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/WorldConfig.java new file mode 100644 index 000000000..8b19b2353 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/WorldConfig.java @@ -0,0 +1,15 @@ +package dev.imprex.orebfuscator.config.api; + +public interface WorldConfig { + + boolean isEnabled(); + + int getMinY(); + + int getMaxY(); + + boolean matchesWorldName(String worldName); + + boolean shouldObfuscate(int y); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/WorldConfigBundle.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/WorldConfigBundle.java new file mode 100644 index 000000000..ba33f9368 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/api/WorldConfigBundle.java @@ -0,0 +1,22 @@ +package dev.imprex.orebfuscator.config.api; + +public interface WorldConfigBundle { + + BlockFlags blockFlags(); + + ObfuscationConfig obfuscation(); + + ProximityConfig proximity(); + + boolean needsObfuscation(); + + int minSectionIndex(); + + int maxSectionIndex(); + + boolean shouldObfuscate(int y); + + int nextRandomObfuscationBlock(int y); + + int nextRandomProximityBlock(int y); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/BlockParser.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/BlockParser.java new file mode 100644 index 000000000..92743c63f --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/BlockParser.java @@ -0,0 +1,115 @@ +package dev.imprex.orebfuscator.config.components; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import org.jetbrains.annotations.NotNull; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockTag; + +public class BlockParser { + + public static BlockParser.Factory factory(RegistryAccessor registryAccessor) { + return new Factory(registryAccessor); + } + + private final @NotNull RegistryAccessor registry; + private final boolean excludeAir; + + private BlockParser(@NotNull RegistryAccessor registry, boolean excludeAir) { + this.registry = Objects.requireNonNull(registry); + this.excludeAir = excludeAir; + } + + @NotNull + public ConfigBlockValue parse(@NotNull ConfigParsingContext context, @NotNull String value) { + var parsed = ConfigFunctionValue.parse(value); + if (parsed != null) { + return switch (parsed.function()) { + case "tag" -> parseBlockTag(context, parsed.argument()); + default -> { + context.warn(ConfigMessage.FUNCTION_UNKNOWN, parsed.function(), parsed.argument()); + yield ConfigBlockValue.invalid(value); + } + }; + } else { + return parseBlock(context, value); + } + } + + @NotNull + private ConfigBlockValue parseBlockTag(@NotNull ConfigParsingContext context, @NotNull String value) { + BlockTag tag = registry.getBlockTagByName(value); + if (tag == null) { + context.warn(ConfigMessage.BLOCK_TAG_UNKNOWN, value); + return ConfigBlockValue.invalidTag(value); + } + + Set blocks = tag.blocks(); + if (blocks.isEmpty()) { + context.warn(ConfigMessage.BLOCK_TAG_EMPTY, value); + return ConfigBlockValue.invalidTag(value); + } + + if (excludeAir) { + // copy to mutable set + blocks = new HashSet<>(blocks); + + for (var iterator = blocks.iterator(); iterator.hasNext(); ) { + BlockProperties block = iterator.next(); + + if (block.getDefaultBlockState().isAir()) { + context.warn(ConfigMessage.BLOCK_TAG_AIR_BLOCK, block.getKey(), value); + iterator.remove(); + } + } + + if (blocks.isEmpty()) { + context.warn(ConfigMessage.BLOCK_TAG_AIR_ONLY, value); + return ConfigBlockValue.invalidTag(value); + } + } + + return ConfigBlockValue.tag(tag, blocks); + } + + @NotNull + private ConfigBlockValue parseBlock(@NotNull ConfigParsingContext context, @NotNull String value) { + BlockProperties block = registry.getBlockByName(value); + if (block == null) { + context.warn(ConfigMessage.BLOCK_UNKNOWN, value); + } else if (excludeAir && block.getDefaultBlockState().isAir()) { + context.warn(ConfigMessage.BLOCK_AIR, value); + } else { + return ConfigBlockValue.block(block); + } + + return ConfigBlockValue.invalid(value); + } + + public static class Factory { + + private final @NotNull RegistryAccessor registry; + + private final BlockParser excludeAir; + private final BlockParser includeAir; + + public Factory(@NotNull RegistryAccessor registry) { + this.registry = Objects.requireNonNull(registry); + + this.excludeAir = new BlockParser(registry, true); + this.includeAir = new BlockParser(registry, false); + } + + public BlockParser excludeAir() { + return excludeAir; + } + + public BlockParser includeAir() { + return includeAir; + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/ConfigBlockValue.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/ConfigBlockValue.java new file mode 100644 index 000000000..7e35a1344 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/ConfigBlockValue.java @@ -0,0 +1,80 @@ +package dev.imprex.orebfuscator.config.components; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; +import java.util.Set; +import org.jetbrains.annotations.NotNull; +import com.google.gson.JsonArray; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockTag; + +public record ConfigBlockValue(@NotNull String value, @NotNull Set blocks) implements + Comparable { + + public static JsonObject toJson(Collection values) { + JsonObject object = new JsonObject(); + + var list = values.stream().sorted(Comparator.comparing((ConfigBlockValue a) -> a.value())).toList(); + + for (var entry : list) { + if (entry.blocks().size() > 1) { + JsonArray array = new JsonArray(entry.blocks().size()); + for (var block : entry.blocks()) { + array.add(block.getKey().toString()); + } + object.add(entry.value(), array); + } else { + object.add(entry.value(), JsonNull.INSTANCE); + } + } + + return object; + } + + @NotNull + public static ConfigBlockValue invalid(@NotNull String value) { + return new ConfigBlockValue(value, Collections.emptySet()); + } + + @NotNull + public static ConfigBlockValue block(@NotNull BlockProperties block) { + return new ConfigBlockValue(block.getKey().toString(), Set.of(block)); + } + + @NotNull + public static ConfigBlockValue invalidTag(@NotNull String value) { + return new ConfigBlockValue(String.format("tag(%s)", value), Collections.emptySet()); + } + + @NotNull + public static ConfigBlockValue tag(@NotNull BlockTag tag, @NotNull Set blocks) { + return new ConfigBlockValue(String.format("tag(%s)", tag.key()), Collections.unmodifiableSet(blocks)); + } + + @Override + public int hashCode() { + return this.value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return (this == obj) || (obj instanceof ConfigBlockValue other) && Objects.equals(this.value, other.value); + } + + @Override + public int compareTo(ConfigBlockValue o) { + boolean isATag = this.value().startsWith("tag("); + boolean isBTag = o.value().startsWith("tag("); + + int tag = Boolean.compare(isATag, isBTag); + if (tag == 0) { + return this.value().compareTo(o.value()); + } + + return tag; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/ConfigFunctionValue.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/ConfigFunctionValue.java new file mode 100644 index 000000000..9f456bf77 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/ConfigFunctionValue.java @@ -0,0 +1,24 @@ +package dev.imprex.orebfuscator.config.components; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public record ConfigFunctionValue(@NotNull String function, @NotNull String argument) { + + private static final Pattern CONFIG_FUNCTION_PATTERN = Pattern.compile("^(?\\w+)\\((?.+)\\)$"); + + @Nullable + public static ConfigFunctionValue parse(@NotNull String value) { + Matcher matcher = CONFIG_FUNCTION_PATTERN.matcher(value); + if (matcher.find()) { + String function = matcher.group("function"); + String argument = matcher.group("argument"); + + return new ConfigFunctionValue(function.toLowerCase(), argument); + } else { + return null; + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/WeightedBlockList.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/WeightedBlockList.java new file mode 100644 index 000000000..8973f914a --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/WeightedBlockList.java @@ -0,0 +1,132 @@ +package dev.imprex.orebfuscator.config.components; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import dev.imprex.orebfuscator.config.context.ConfigMessage; +import dev.imprex.orebfuscator.config.context.ConfigParsingContext; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.MathUtil; +import dev.imprex.orebfuscator.util.WeightedRandom; + +public class WeightedBlockList { + + public static WeightedRandom[] create(WorldAccessor world, List lists) { + WeightedRandom[] heightMap = new WeightedRandom[world.getHeight()]; + + List last = new ArrayList<>(); + List next = new ArrayList<>(); + + int count = 0; + + for (int y = world.getMinBuildHeight(); y < world.getMaxBuildHeight(); y++) { + for (WeightedBlockList list : lists) { + if (list.minY <= y && list.maxY >= y) { + next.add(list); + } + } + + int index = y - world.getMinBuildHeight(); + if (index > 0 && last.equals(next)) { + // copy last weighted random + heightMap[index] = heightMap[index - 1]; + } else { + WeightedRandom.Builder builder = WeightedRandom.builder(); + + for (WeightedBlockList list : next) { + for (Map.Entry entry : list.blocks.entrySet()) { + // TODO: add support for other block states in future + var blockStates = entry.getKey().blocks().stream() + .map(block -> block.getDefaultBlockState()) + .collect(Collectors.toSet()); + double weight = (double) entry.getValue() / (double) blockStates.size(); + + for (BlockStateProperties state : blockStates) { + builder.add(state.getId(), weight); + } + } + } + + heightMap[index] = builder.build(); + count++; + + // only update last if recomputed + last.clear(); + last.addAll(next); + } + + next.clear(); + } + + OfcLogger.debug(String.format("Successfully created %s weighted randoms", count)); + + return heightMap; + } + + private final String name; + + private final int minY; + private final int maxY; + + private final Map blocks = new LinkedHashMap<>(); + + public WeightedBlockList(BlockParser.Factory blockParserFactory, ConfigurationSection section, + ConfigParsingContext context) { + this.name = section.getName(); + + int minY = MathUtil.clamp(section.getInt("minY", BlockPos.MIN_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); + int maxY = MathUtil.clamp(section.getInt("maxY", BlockPos.MAX_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); + + this.minY = Math.min(minY, maxY); + this.maxY = Math.max(minY, maxY); + + ConfigParsingContext blocksContext = context.section("blocks"); + ConfigurationSection blocksSection = section.getSection("blocks"); + if (blocksSection == null) { + blocksContext.error(ConfigMessage.MISSING_OR_EMPTY); + return; + } + + final BlockParser parser = blockParserFactory.includeAir(); + + boolean isEmpty = true; + for (String value : blocksSection.getKeys()) { + int weight = blocksSection.getInt(value, 1); + var parsed = parser.parse(context, value); + this.blocks.put(parsed, weight); + isEmpty &= parsed.blocks().isEmpty(); + } + + if (isEmpty) { + blocksContext.error(ConfigMessage.MISSING_OR_EMPTY); + } + } + + public void serialize(ConfigurationSection section) { + section = section.createSection(this.name); + + section.set("minY", this.minY); + section.set("maxY", this.maxY); + + section = section.createSection("blocks"); + for (Map.Entry entry : this.blocks.entrySet()) { + section.set(entry.getKey().value(), entry.getValue()); + } + } + + public String getName() { + return name; + } + + public Set getBlocks() { + return Collections.unmodifiableSet(this.blocks.keySet()); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/WorldMatcher.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/WorldMatcher.java new file mode 100644 index 000000000..40beacd3d --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/components/WorldMatcher.java @@ -0,0 +1,54 @@ +package dev.imprex.orebfuscator.config.components; + +import java.util.function.Predicate; +import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; + +public class WorldMatcher implements Predicate { + + @NotNull + public static WorldMatcher parseMatcher(@NotNull String value) { + var parsed = ConfigFunctionValue.parse(value); + if (parsed != null && parsed.function().equals("regex")) { + return new WorldMatcher(parseRegexMatcher(parsed.argument()), Type.REGEX); + } else { + return new WorldMatcher(parseWildcardMatcher(value), Type.WILDCARD); + } + } + + private static Pattern parseRegexMatcher(String pattern) { + return Pattern.compile(pattern); + } + + private static Pattern parseWildcardMatcher(String value) { + String pattern = ("\\Q" + value + "\\E").replace("*", "\\E.*\\Q"); + return Pattern.compile(pattern); + } + + private final Pattern pattern; + private final Type type; + + private WorldMatcher(Pattern pattern, Type type) { + this.pattern = pattern; + this.type = type; + } + + @Override + public boolean test(String value) { + return this.pattern.matcher(value).matches(); + } + + public String serialize() { + if (this.type == Type.REGEX) { + return String.format("regex(%s)", this.pattern.pattern()); + } else { + return this.pattern.pattern() + .replace("\\E.*\\Q", "*") + .replaceAll("\\\\Q|\\\\E", ""); + } + } + + private enum Type { + REGEX, WILDCARD + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/ConfigMessage.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/ConfigMessage.java new file mode 100644 index 000000000..d1e160bb7 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/ConfigMessage.java @@ -0,0 +1,56 @@ +package dev.imprex.orebfuscator.config.context; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ConfigMessage { + + public static final ConfigMessage MISSING_OR_EMPTY = fixed("is missing or empty"); + public static final ConfigMessage MISSING_USING_DEFAULTS = fixed("is missing, using defaults"); + + public static final ConfigMessage DISABLED_ERRORS = fixed("got disabled due to errors"); + + public static final ConfigMessage VALUE_MIN = dynamic("value too low {value(%d) < min(%d)}"); + public static final ConfigMessage VALUE_MIN_MAX = dynamic("value out of range {value(%d) not in range[%d, %d]}"); + + public static final ConfigMessage FUNCTION_UNKNOWN = dynamic( + "skipping unknown function literal '%s(...)' with argument '%s'"); + + public static final ConfigMessage CACHE_INVALID_PATH = dynamic( + "contains malformed path '%s', using default path '%s'"); + public static final ConfigMessage CACHE_CANT_CREATE = dynamic("can't create cache directory '%s'"); + + public static final ConfigMessage BLOCK_TAG_UNKNOWN = dynamic("skipping unknown block tag '%s'"); + public static final ConfigMessage BLOCK_TAG_EMPTY = dynamic("skipping empty block tag '%s'"); + public static final ConfigMessage BLOCK_TAG_AIR_BLOCK = dynamic("skipping air block '%s' for block tag '%s'"); + public static final ConfigMessage BLOCK_TAG_AIR_ONLY = dynamic( + "skipping block tag '%s' because it only contains air"); + + public static final ConfigMessage BLOCK_UNKNOWN = dynamic("skipping unknown block '%s'"); + public static final ConfigMessage BLOCK_AIR = dynamic("skipping air block '%s'"); + + private static ConfigMessage fixed(@NotNull String message) { + return new ConfigMessage(message, true); + } + + private static ConfigMessage dynamic(@NotNull String message) { + return new ConfigMessage(message, false); + } + + private final String message; + private final boolean fixed; + + private ConfigMessage(@NotNull String message, boolean fixed) { + this.message = message; + this.fixed = fixed; + } + + @NotNull + @Contract(pure = true) + public String format(@Nullable Object... arguments) { + return this.fixed + ? this.message + : String.format(this.message, arguments); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/ConfigParsingContext.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/ConfigParsingContext.java new file mode 100644 index 000000000..74f8a8cb2 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/ConfigParsingContext.java @@ -0,0 +1,135 @@ +package dev.imprex.orebfuscator.config.context; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Represents a context used during configuration parsing to track and collect warnings and errors. + *

+ * Paths within this context are always '.' separated and cannot contain only whitespace. A path is always relative to + * the current context and cannot reference a parent context ('..'). + *

+ *

+ * If a child section isolates errors, the parent context will not return true for {@code hasErrors()}, even if the + * child section or its descendants contain errors. This allows certain configuration sections to be disabled + * individually without affecting the entire configuration. + *

+ */ +public interface ConfigParsingContext { + + /** + * Creates or retrieves an existing child context for the specified path. + * + * @param path The relative path of the child context. + * @param isolateErrors Whether errors in this child context should be isolated from the parent. + * @return An existing or newly created {@code ConfigParsingContext} instance for the given path. + */ + @NotNull + ConfigParsingContext section(@NotNull String path, boolean isolateErrors); + + /** + * Creates or retrieves an existing child context for the specified path without isolating errors. + * + * @param path The relative path of the child context. + * @return An existing or newly created {@code ConfigParsingContext} instance for the given path. + */ + @NotNull + default ConfigParsingContext section(@NotNull String path) { + return section(path, false); + } + + /** + * Adds a warning message to the current context. + * + * @param message The warning message to be logged. + * @param arguments Optional arguments used to format the message. + */ + void warn(@NotNull ConfigMessage message, @Nullable Object... arguments); + + /** + * Adds a warning message to the context of the specified path. + * + * @param path The relative path where the warning should be logged. + * @param message The warning message to be logged. + * @param arguments Optional arguments used to format the message. + */ + void warn(@NotNull String path, @NotNull ConfigMessage message, @Nullable Object... arguments); + + /** + * Determines whether a subsystem should remain enabled based on the presence of errors. + *

+ * If errors are present in this context, the subsystem should be disabled. Otherwise, the given enabled state is + * returned unchanged. + *

+ * + * @param enabled The current enabled state of the subsystem. + * @return {@code false} if errors are present and the subsystem would be enabled, otherwise returns the given + * {@code enabled} state. + * @see #hasErrors() + */ + default boolean disableIfError(boolean enabled) { + if (enabled && hasErrors()) { + warn(ConfigMessage.DISABLED_ERRORS); + return false; + } + return enabled; + } + + /** + * Adds an error message to the current context. + * + * @param message The error message to be logged. + * @param arguments Optional arguments used to format the message. + */ + void error(@NotNull ConfigMessage message, @Nullable Object... arguments); + + /** + * Adds an error message to the context of the specified path. + * + * @param path The relative path where the error should be logged. + * @param message The error message to be logged. + * @param arguments Optional arguments used to format the message. + */ + void error(@NotNull String path, @NotNull ConfigMessage message, @Nullable Object... arguments); + + /** + * Adds an error if the specified value is below the minimum allowed value. + * + * @param path The relative path where the error should be logged. + * @param min The minimum allowed value. + * @param value The actual value to be checked. + */ + default void errorMinValue(@NotNull String path, long min, long value) { + if (value < min) { + error(path, ConfigMessage.VALUE_MIN, value, min); + } + } + + /** + * Adds an error if the specified value is outside the allowed min-max range (inclusive). + * + * @param path The relative path where the error should be logged. + * @param min The minimum allowed value. + * @param max The maximum allowed value. + * @param value The actual value to be checked. + */ + default void errorMinMaxValue(@NotNull String path, long min, long max, long value) { + if (value < min || value > max) { + error(path, ConfigMessage.VALUE_MIN_MAX, value, min, max); + } + } + + /** + * Checks whether this context or any of its child contexts (recursively) contain errors. + *

+ * If a child context is set to isolate errors, its errors will not contribute to the result of this method. This + * allows for selective error handling within different parts of the configuration. + *

+ * + * @return {@code true} if errors are present in this context or any non-isolating child contexts, otherwise + * {@code false}. + */ + @Contract(pure = true) + boolean hasErrors(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/DefaultConfigParsingContext.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/DefaultConfigParsingContext.java new file mode 100644 index 000000000..b8404140d --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/context/DefaultConfigParsingContext.java @@ -0,0 +1,165 @@ +package dev.imprex.orebfuscator.config.context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class DefaultConfigParsingContext implements ConfigParsingContext { + + private static final String ANSI_RESET = "\u001B[m"; + private static final String ANSI_ERROR = "\u001B[31;1m"; // Bold Red + private static final String ANSI_WARN = "\u001B[33;1m"; // Bold Yellow + + private final int depth; + private boolean isolateErrors = false; + + private final Map section = new LinkedHashMap<>(); + private final List messages = new ArrayList<>(); + + public DefaultConfigParsingContext() { + this(null); + } + + private DefaultConfigParsingContext(@Nullable DefaultConfigParsingContext parentContext) { + this.depth = parentContext != null + ? parentContext.depth + 1 + : 0; + } + + @Override + @NotNull + public DefaultConfigParsingContext section(@NotNull String path, boolean isolateErrors) { + DefaultConfigParsingContext context = getContext(path); + context.isolateErrors = isolateErrors; + return context; + } + + @Override + public void warn(@NotNull ConfigMessage message, @Nullable Object... arguments) { + Objects.requireNonNull(message, "message can't be null"); + + this.messages.add(new Message(false, message.format(arguments))); + } + + @Override + public void warn(@NotNull String path, @NotNull ConfigMessage message, @Nullable Object... arguments) { + getContext(path).warn(message, arguments); + } + + @Override + public void error(@NotNull ConfigMessage message, @Nullable Object... arguments) { + Objects.requireNonNull(message, "message can't be null"); + + this.messages.add(new Message(true, message.format(arguments))); + } + + @Override + public void error(@NotNull String path, @NotNull ConfigMessage message, @Nullable Object... arguments) { + getContext(path).error(message, arguments); + } + + @Override + @Contract(pure = true) + public boolean hasErrors() { + for (Message message : this.messages) { + if (message.isError()) { + return true; + } + } + + for (var section : this.section.values()) { + if (!section.isolateErrors && section.hasErrors()) { + return true; + } + } + + return false; + } + + private DefaultConfigParsingContext getContext(@NotNull String path) { + Objects.requireNonNull(path, "context path can't be null"); + + DefaultConfigParsingContext context = this; + for (String segment : path.split("\\.")) { + if (segment.isBlank()) { + throw new IllegalArgumentException("ConfigParsingContext path doesn't support blank segments: " + path); + } + + DefaultConfigParsingContext nextContext = context.section.get(segment); + if (nextContext == null) { + nextContext = new DefaultConfigParsingContext(context); + context.section.put(segment, nextContext); + } + context = nextContext; + } + + return context; + } + + private int getMessageCount() { + int messageCount = this.messages.size(); + + for (DefaultConfigParsingContext section : section.values()) { + messageCount += section.getMessageCount(); + } + + return messageCount; + } + + private StringBuilder buildReport(final StringBuilder builder) { + final String indent = " ".repeat(this.depth); + + // sort -> errors should come before warnings + Collections.sort(this.messages); + + for (Message message : this.messages) { + String color = message.isError() ? ANSI_ERROR : ANSI_WARN; + builder.append(indent).append(color).append("- ").append(message.content()).append('\n'); + } + + for (var entry : this.section.entrySet()) { + if (entry.getValue().getMessageCount() == 0) { + continue; + } + + builder.append(indent).append(ANSI_WARN).append(entry.getKey()).append(":\n"); + entry.getValue().buildReport(builder); + } + + return builder; + } + + @Nullable + public String report() { + int messageCount = this.getMessageCount(); + if (messageCount == 0) { + return null; + } + + StringBuilder builder = new StringBuilder() + .append(hasErrors() ? ANSI_ERROR : ANSI_WARN) + .append("Encountered ").append(messageCount).append(" issue(s) while parsing the config:\n") + .append(ANSI_RESET); + + return buildReport(builder) + .append(ANSI_RESET) + .toString(); + } + + private record Message(boolean isError, @NotNull String content) implements Comparable { + + @Override + public int compareTo(Message o) { + int a = this.isError ? 1 : 0; + int b = o.isError ? 1 : 0; + return b - a; + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigration.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigration.java new file mode 100644 index 000000000..d455e41bf --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigration.java @@ -0,0 +1,31 @@ +package dev.imprex.orebfuscator.config.migrations; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; + +interface ConfigMigration { + + int sourceVersion(); + + @NotNull ConfigurationSection migrate(@NotNull ConfigurationSection root); + + static void migrateNames(@Nullable ConfigurationSection section, @NotNull List> mapping) { + Objects.requireNonNull(mapping, "mappings can't be null"); + if (section == null) { + return; + } + + for (Map.Entry entry : mapping) { + Object value = section.get(entry.getKey()); + if (value != null) { + section.set(entry.getValue(), value); + } + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV1.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV1.java new file mode 100644 index 000000000..e752b16f4 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV1.java @@ -0,0 +1,65 @@ +package dev.imprex.orebfuscator.config.migrations; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; + +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; + +class ConfigMigrationV1 implements ConfigMigration { + + @Override + public int sourceVersion() { + return 1; + } + + @Override + public @NotNull ConfigurationSection migrate(@NotNull ConfigurationSection root) { + // check if config is still using old path + String obfuscationConfigPath = root.contains("world") ? "world" : "obfuscation"; + convertSectionListToSection(root, obfuscationConfigPath); + convertSectionListToSection(root, "proximity"); + + return root; + } + + private static void convertSectionListToSection(ConfigurationSection root, String path) { + List configList = getSectionList(root, path); + ConfigurationSection configContainer = root.createSection(path); + + for (ConfigurationSection config : configList) { + configContainer.set(config.getName(), config); + } + } + + private static List getSectionList(ConfigurationSection root, String path) { + List configList = new ArrayList<>(); + + List list = root.getList(path, Collections.emptyList()); + for (int i = 0; i < list.size(); i++) { + if (list.get(i) instanceof Map map) { + ConfigurationSection config = root.createSection(path + "-" + i); + configList.add(convertMapsToSections(map, config)); + } + } + + return configList; + } + + private static ConfigurationSection convertMapsToSections(Map entries, ConfigurationSection section) { + for (Map.Entry entry : entries.entrySet()) { + String key = entry.getKey().toString(); + Object value = entry.getValue(); + + if (value instanceof Map map) { + convertMapsToSections(map, section.createSection(key)); + } else { + section.set(key, value); + } + } + return section; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV2.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV2.java new file mode 100644 index 000000000..4ceb1b3a8 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV2.java @@ -0,0 +1,43 @@ +package dev.imprex.orebfuscator.config.migrations; + +import org.jetbrains.annotations.NotNull; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.util.BlockPos; + +class ConfigMigrationV2 implements ConfigMigration { + + @Override + public int sourceVersion() { + return 2; + } + + @Override + public @NotNull ConfigurationSection migrate(@NotNull ConfigurationSection root) { + convertRandomBlocksToSections(root.getSection("obfuscation")); + convertRandomBlocksToSections(root.getSection("proximity")); + return root; + } + + private static void convertRandomBlocksToSections(ConfigurationSection configContainer) { + if (configContainer == null) { + return; + } + + for (ConfigurationSection config : configContainer.getSubSections()) { + ConfigurationSection blockSection = config.getSection("randomBlocks"); + if (blockSection == null) { + continue; + } + + ConfigurationSection newBlockSection = config.createSection("randomBlocks"); + newBlockSection = newBlockSection.createSection("section-global"); + newBlockSection.set("minY", BlockPos.MIN_Y); + newBlockSection.set("maxY", BlockPos.MAX_Y); + newBlockSection = newBlockSection.createSection("blocks"); + + for (String blockName : blockSection.getKeys()) { + newBlockSection.set(blockName, blockSection.getInt(blockName)); + } + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV3.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV3.java new file mode 100644 index 000000000..460dab21a --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV3.java @@ -0,0 +1,100 @@ +package dev.imprex.orebfuscator.config.migrations; + +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.util.BlockPos; + +class ConfigMigrationV3 implements ConfigMigration { + + @Override + public int sourceVersion() { + return 3; + } + + @Override + public @NotNull ConfigurationSection migrate(@NotNull ConfigurationSection root) { + migrateAdvancedConfig(root); + migrateCacheConfig(root); + migrateProximityConfigs(root); + return root; + } + + private static void migrateAdvancedConfig(ConfigurationSection root) { + ConfigMigration.migrateNames(root.getSection("advanced"), List.of( + // obfuscation mapping + Map.entry("obfuscationWorkerThreads", "obfuscation.threads"), + Map.entry("obfuscationTimeout", "obfuscation.timeout"), + Map.entry("maxMillisecondsPerTick", "obfuscation.maxMillisecondsPerTick"), + // proximity mapping + Map.entry("proximityHiderThreads", "proximity.threads"), + Map.entry("proximityDefaultBucketSize", "proximity.defaultBucketSize"), + Map.entry("proximityThreadCheckInterval", "proximity.threadCheckInterval"), + Map.entry("proximityPlayerCheckInterval", "proximity.playerCheckInterval") + )); + } + + private static void migrateCacheConfig(ConfigurationSection root) { + ConfigMigration.migrateNames(root.getSection("cache"), List.of( + // memory cache mapping + Map.entry("maximumSize", "memoryCache.maximumSize"), + Map.entry("expireAfterAccess", "memoryCache.expireAfterAccess"), + // disk cache mapping + Map.entry("enableDiskCache", "diskCache.enabled"), + Map.entry("baseDirectory", "diskCache.directory"), + Map.entry("maximumOpenRegionFiles", "diskCache.maximumOpenFiles"), + Map.entry("deleteRegionFilesAfterAccess", "diskCache.deleteFilesAfterAccess"), + Map.entry("maximumTaskQueueSize", "diskCache.maximumTaskQueueSize") + )); + } + + private static void migrateProximityConfigs(ConfigurationSection root) { + ConfigurationSection configContainer = root.getSection("proximity"); + if (configContainer == null) { + return; + } + + for (ConfigurationSection config : configContainer.getSubSections()) { + // LEGACY: transform to post 5.2.2 + if (config.isSection("defaults")) { + Integer y = config.getInt("defaults.y"); + if (config.getBoolean("defaults.above", false)) { + config.set("minY", y); + config.set("maxY", BlockPos.MAX_Y); + } else { + config.set("minY", BlockPos.MIN_Y); + config.set("maxY", y); + } + + config.set("useBlockBelow", config.getBoolean("defaults.useBlockBelow")); + } + + // rename all ray cast name variations + if (config.isBoolean("useRayCastCheck") || config.isBoolean("useFastGazeCheck")) { + config.set("rayCastCheck.enabled", + config.getBoolean("useRayCastCheck", + config.getBoolean("useFastGazeCheck"))); + } + + // LEGACY: transform pre 5.2.2 height condition + ConfigurationSection hiddenBlocks = config.getSection("hiddenBlocks"); + if (hiddenBlocks == null) { + continue; + } + + for (ConfigurationSection block : hiddenBlocks.getSubSections()) { + if (block.isInt("y") && block.isBoolean("above")) { + Integer y = block.getInt("y"); + if (block.getBoolean("above", false)) { + block.set("minY", y); + block.set("maxY", BlockPos.MAX_Y); + } else { + block.set("minY", BlockPos.MIN_Y); + block.set("maxY", y); + } + } + } + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV4.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV4.java new file mode 100644 index 000000000..c72a40df7 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrationV4.java @@ -0,0 +1,36 @@ +package dev.imprex.orebfuscator.config.migrations; + +import org.jetbrains.annotations.NotNull; +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; + +class ConfigMigrationV4 implements ConfigMigration { + + @Override + public int sourceVersion() { + return 4; + } + + @Override + public @NotNull ConfigurationSection migrate(@NotNull ConfigurationSection root) { + migrateWorlds(root.getSection("obfuscation")); + migrateWorlds(root.getSection("proximity")); + return root; + } + + private static void migrateWorlds(ConfigurationSection configContainer) { + if (configContainer == null) { + return; + } + + for (ConfigurationSection config : configContainer.getSubSections()) { + var worlds = config.getStringList("worlds").stream().map(value -> { + if (value.startsWith("regex:")) { + return String.format("regex(%s)", value.substring(6)); + } + return value; + }).toList(); + + config.set("worlds", worlds); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrator.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrator.java new file mode 100644 index 000000000..a43ca11e2 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/migrations/ConfigMigrator.java @@ -0,0 +1,47 @@ +package dev.imprex.orebfuscator.config.migrations; + +import java.util.HashMap; +import java.util.Map; + +import dev.imprex.orebfuscator.config.yaml.ConfigurationSection; +import dev.imprex.orebfuscator.logging.OfcLogger; + +public class ConfigMigrator { + + private static final Map MIGRATIONS = new HashMap<>(); + + static { + register(new ConfigMigrationV1()); + register(new ConfigMigrationV2()); + register(new ConfigMigrationV3()); + register(new ConfigMigrationV4()); + } + + private static void register(ConfigMigration migration) { + MIGRATIONS.put(migration.sourceVersion(), migration); + } + + public static boolean willMigrate(ConfigurationSection root) { + int version = root.getInt("version", -1); + return MIGRATIONS.get(version) != null; + } + + public static void migrateToLatestVersion(ConfigurationSection root) { + while (true) { + int sourceVersion = root.getInt("version", -1); + int targetVersion = sourceVersion + 1; + + ConfigMigration migration = MIGRATIONS.get(sourceVersion); + if (migration == null) { + break; + } + + OfcLogger.info("Starting to migrate config to version " + targetVersion); + + root = migration.migrate(root); + root.set("version", targetVersion); + + OfcLogger.info("Successfully migrated config to version " + targetVersion); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/ConfigurationSection.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/ConfigurationSection.java new file mode 100644 index 000000000..966c6a5bc --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/ConfigurationSection.java @@ -0,0 +1,306 @@ +/* + * This code is adapted from the Bukkit/Spigot project: + * https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/configuration/MemorySection.java + * Copyright (C) 2011-2024 Bukkit Project (original authors and contributors) + * Licensed under the GNU General Public License v3.0 (GPLv3) + */ +package dev.imprex.orebfuscator.config.yaml; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ConfigurationSection { + + static final char PATH_SEPARATOR = '.'; + + protected final Map map = new LinkedHashMap<>(); + private final String path; + + protected ConfigurationSection(@NotNull String path) { + Objects.requireNonNull(path, "Path cannot be null"); + + this.path = path; + } + + @NotNull + public String getName() { + return path; + } + + @NotNull + public Set getKeys() { + return this.map.keySet(); + } + + public boolean contains(@NotNull String path) { + return get(path, null) != null; + } + + public void set(@NotNull String path, @Nullable Object value) { + Objects.requireNonNull(path, "Path cannot be null"); + + int end = -1, start; + ConfigurationSection section = this; + while ((end = path.indexOf(PATH_SEPARATOR, start = end + 1)) != -1) { + String segment = path.substring(start, end); + ConfigurationSection subSection = section.getSection(segment); + if (subSection == null) { + if (value == null) { + // no need to create missing subsections if we want to remove the value: + return; + } + section = section.createSection(segment); + } else { + section = subSection; + } + } + + String segment = path.substring(start); + if (section == this) { + if (value == null) { + map.remove(segment); + } else { + map.put(segment, value); + } + } else { + section.set(segment, value); + } + } + + @Nullable + public Object get(@NotNull String path) { + return get(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public Object get(@NotNull String path, @Nullable Object defaultValue) { + Objects.requireNonNull(path, "Path cannot be null"); + + if (path.isBlank()) { + return this; + } + + int end = -1, start; + ConfigurationSection section = this; + while ((end = path.indexOf(PATH_SEPARATOR, start = end + 1)) != -1) { + section = section.getSection(path.substring(start, end)); + if (section == null) { + return defaultValue; + } + } + + String segment = path.substring(start); + if (section == this) { + Object result = map.get(segment); + return (result == null) ? defaultValue : result; + } + return section.get(segment, defaultValue); + } + + @NotNull + public ConfigurationSection createSection(@NotNull String path) { + Objects.requireNonNull(path, "Cannot create section at empty path"); + + if (path.isBlank()) { + return this; + } + + int end = -1, start; + ConfigurationSection section = this; + while ((end = path.indexOf(PATH_SEPARATOR, start = end + 1)) != -1) { + String segment = path.substring(start, end); + ConfigurationSection subSection = section.getSection(segment); + if (subSection == null) { + section = section.createSection(segment); + } else { + section = subSection; + } + } + + String segment = path.substring(start); + if (section == this) { + ConfigurationSection result = new ConfigurationSection(segment); + map.put(segment, result); + return result; + } + return section.createSection(segment); + } + + public boolean isBoolean(@NotNull String path) { + return get(path) instanceof Boolean; + } + + @Nullable + public Boolean getBoolean(@NotNull String path) { + return getBoolean(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public Boolean getBoolean(@NotNull String path, @Nullable Boolean defaultValue) { + return get(path, defaultValue) instanceof Boolean value + ? value : defaultValue; + } + + public boolean isInt(@NotNull String path) { + return get(path) instanceof Integer; + } + + @Nullable + public Integer getInt(@NotNull String path) { + return getInt(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public Integer getInt(@NotNull String path, @Nullable Integer defaultValue) { + return get(path, defaultValue) instanceof Number value + ? Integer.valueOf(value.intValue()) : defaultValue; + } + + public boolean isLong(@NotNull String path) { + return get(path) instanceof Long; + } + + @Nullable + public Long getLong(@NotNull String path) { + return getLong(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public Long getLong(@NotNull String path, @Nullable Long defaultValue) { + return get(path, defaultValue) instanceof Number value + ? Long.valueOf(value.longValue()) : defaultValue; + } + + public boolean isDouble(@NotNull String path) { + return get(path) instanceof Double; + } + + @Nullable + public Double getDouble(@NotNull String path) { + return getDouble(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public Double getDouble(@NotNull String path, @Nullable Double defaultValue) { + return get(path, defaultValue) instanceof Number value + ? Double.valueOf(value.doubleValue()) : defaultValue; + } + + public boolean isNumber(@NotNull String path) { + return get(path) instanceof Number; + } + + public boolean isString(@NotNull String path) { + return get(path) instanceof String; + } + + @Nullable + public String getString(@NotNull String path) { + return getString(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public String getString(@NotNull String path, @Nullable String defaultValue) { + return get(path, defaultValue) instanceof String value + ? value : defaultValue; + } + + public boolean isList(@NotNull String path) { + return get(path) instanceof List; + } + + @Nullable + public List getList(@NotNull String path) { + return getList(path, null); + } + + @Nullable + @Contract("_, !null -> !null") + public List getList(@NotNull String path, List defaultValue) { + return get(path, defaultValue) instanceof List value + ? value : defaultValue; + } + + @NotNull + public List getStringList(@NotNull String path) { + List list = getList(path); + if (list == null) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + + for (Object object : list) { + if ((object instanceof String) || (isPrimitiveWrapper(object))) { + result.add(String.valueOf(object)); + } + } + + return result; + } + + public boolean isSection(@NotNull String path) { + return get(path) instanceof ConfigurationSection; + } + + @Nullable + public ConfigurationSection getSection(@NotNull String path) { + return get(path) instanceof ConfigurationSection value + ? value : null; + } + + @NotNull + public List getSubSections() { + List result = new ArrayList<>(); + + for (Object value : this.map.values()) { + if (value instanceof ConfigurationSection subSection) { + result.add(subSection); + } + } + + return result; + } + + private static boolean isPrimitiveWrapper(@Nullable Object input) { + return input instanceof Integer || input instanceof Boolean + || input instanceof Character || input instanceof Byte + || input instanceof Short || input instanceof Double + || input instanceof Long || input instanceof Float; + } + + @Override + public int hashCode() { + return Objects.hash(map, path); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ConfigurationSection other = (ConfigurationSection) obj; + return Objects.equals(map, other.map) && Objects.equals(path, other.path); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/InvalidConfigurationException.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/InvalidConfigurationException.java new file mode 100644 index 000000000..990c6564c --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/InvalidConfigurationException.java @@ -0,0 +1,22 @@ +/* + * This code is adapted from the Bukkit/Spigot project: + * https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/configuration/InvalidConfigurationException.java + * Copyright (C) 2011-2024 Bukkit Project (original authors and contributors) + * Licensed under the GNU General Public License v3.0 (GPLv3) + */ +package dev.imprex.orebfuscator.config.yaml; + +public class InvalidConfigurationException extends Exception { + + public InvalidConfigurationException(String message) { + super(message); + } + + public InvalidConfigurationException(Throwable cause) { + super(cause); + } + + public InvalidConfigurationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/NodeCommentData.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/NodeCommentData.java new file mode 100644 index 000000000..948222135 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/NodeCommentData.java @@ -0,0 +1,111 @@ +package dev.imprex.orebfuscator.config.yaml; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.yaml.snakeyaml.comments.CommentLine; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.ScalarNode; +import org.yaml.snakeyaml.nodes.SequenceNode; + +class NodeCommentData { + + private final List blockComments; + private final List inLineComments; + private final List endComments; + + private final Map children = new HashMap<>(); + private final Map> sequenceValueComments; + + public NodeCommentData() { + this.blockComments = Collections.emptyList(); + this.inLineComments = Collections.emptyList(); + this.endComments = null; + this.sequenceValueComments = null; + } + + public NodeCommentData(@NotNull NodeTuple nodeTuple) { + Node keyNode = nodeTuple.getKeyNode(); + Node valueNode = nodeTuple.getValueNode(); + + this.blockComments = keyNode.getBlockComments(); + this.endComments = null; // only present on root node + + if (valueNode instanceof SequenceNode) { + // disallow inline comments for sequence nodes as they change indentation on dump + this.inLineComments = Collections.emptyList(); + } else if (valueNode instanceof MappingNode) { + this.inLineComments = keyNode.getInLineComments(); + } else { + this.inLineComments = valueNode.getInLineComments(); + } + + // each value node in a sequence has its own comments so we need to store them + if (valueNode instanceof SequenceNode sequenceNode) { + this.sequenceValueComments = new HashMap<>(); + for (Node value : sequenceNode.getValue()) { + if (value instanceof ScalarNode scalarNode) { + // only store inline comments as the reset mess up indentation + this.sequenceValueComments.put(scalarNode.getValue(), scalarNode.getInLineComments()); + } + } + } else { + this.sequenceValueComments = null; + } + } + + public NodeCommentData(@NotNull MappingNode node) { + this.blockComments = node.getBlockComments(); + this.inLineComments = node.getInLineComments(); + this.endComments = node.getEndComments(); + this.sequenceValueComments = null; + } + + public void addChild(@NotNull String key, @NotNull NodeCommentData commentData) { + this.children.put(key, commentData); + } + + @Nullable + public NodeCommentData getChild(@NotNull String key) { + return this.children.get(key); + } + + public void apply(@NotNull NodeTuple nodeTuple) { + Node keyNode = nodeTuple.getKeyNode(); + Node valueNode = nodeTuple.getValueNode(); + + keyNode.setBlockComments(this.blockComments); + + if (valueNode instanceof MappingNode) { + keyNode.setInLineComments(this.inLineComments); + } else if (!(valueNode instanceof SequenceNode)) { + valueNode.setInLineComments(this.inLineComments); + } + + // apply inline comments to each value node individually + if (valueNode instanceof SequenceNode sequenceNode && this.sequenceValueComments != null) { + for (Node value : sequenceNode.getValue()) { + if (!(value instanceof ScalarNode scalarNode)) { + continue; + } + + List inlineComments = this.sequenceValueComments.get(scalarNode.getValue()); + if (inlineComments != null) { + scalarNode.setInLineComments(inlineComments); + } + } + } + } + + public void apply(@NotNull MappingNode node) { + node.setBlockComments(this.blockComments); + node.setInLineComments(this.inLineComments); + node.setEndComments(this.endComments); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/YamlConfiguration.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/YamlConfiguration.java new file mode 100644 index 000000000..b517c396f --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/YamlConfiguration.java @@ -0,0 +1,197 @@ +/* + * This code is adapted from the Bukkit/Spigot project: + * https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/ + * configuration/file/YamlConfiguration.java Copyright (C) 2011-2024 Bukkit Project (original + * authors and contributors) Licensed under the GNU General Public License v3.0 (GPLv3) + */ +package dev.imprex.orebfuscator.config.yaml; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.AnchorNode; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.Tag; +import org.yaml.snakeyaml.reader.UnicodeReader; +import org.yaml.snakeyaml.representer.Representer; + +public class YamlConfiguration extends ConfigurationSection { + + @NotNull + public static YamlConfiguration loadConfig(@NotNull Path path) throws IOException, InvalidConfigurationException { + Objects.requireNonNull(path, "Path cannot be null"); + + try (InputStream inputStream = Files.newInputStream(path)) { + return loadConfig(inputStream); + } + } + + @NotNull + public static YamlConfiguration loadConfig(@NotNull InputStream inputStream) + throws IOException, InvalidConfigurationException { + Objects.requireNonNull(inputStream, "InputStream cannot be null"); + + try (Reader reader = new UnicodeReader(inputStream)) { + YamlConfiguration configuration = new YamlConfiguration(); + configuration.load(reader); + return configuration; + } catch (YAMLException e) { + throw new InvalidConfigurationException(e); + } + } + + private final DumperOptions dumperOptions = new DumperOptions(); + private final Representer representer = new Representer(dumperOptions); + + private final LoaderOptions loaderOptions = new LoaderOptions(); + private final YamlConstructor constructor = new YamlConstructor(loaderOptions); + + private final Yaml yaml = new Yaml(constructor, representer, dumperOptions, loaderOptions); + private NodeCommentData commentData = new NodeCommentData(); + + protected YamlConfiguration() { + super(""); + + dumperOptions.setIndent(2); + dumperOptions.setWidth(80); + dumperOptions.setProcessComments(true); + dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + + loaderOptions.setProcessComments(true); + } + + public void clear() { + this.map.clear(); + } + + public void save(@NotNull Path path) throws IOException { + Objects.requireNonNull(path, "Path cannot be null"); + + Files.createDirectories(path.getParent()); + + try (Writer writer = Files.newBufferedWriter(path)) { + dumperOptions.setProcessComments(true); + save(writer); + } + } + + public String withoutComments() throws IOException { + try (Writer writer = new StringWriter()) { + dumperOptions.setProcessComments(false); + save(writer); + return writer.toString(); + } + } + + private void save(@NotNull Writer writer) throws IOException { + Objects.requireNonNull(writer, "Writer cannot be null"); + + MappingNode node = toNodeTree(this.commentData, this); + this.commentData.apply(node); + + if (isNullOrEmpty(node.getBlockComments()) && isNullOrEmpty(node.getEndComments()) && node.getValue().isEmpty()) { + writer.write(""); + } else { + if (node.getValue().isEmpty()) { + node.setFlowStyle(DumperOptions.FlowStyle.FLOW); + } + yaml.serialize(node, writer); + } + } + + private void load(@NotNull Reader reader) throws YAMLException, InvalidConfigurationException { + Objects.requireNonNull(reader, "Reader cannot be null"); + + Node rawNode = yaml.compose(reader); + if (rawNode != null && !(rawNode instanceof MappingNode)) { + throw new InvalidConfigurationException("Top level is not a Map."); + } + + this.map.clear(); + + MappingNode node = (MappingNode) rawNode; + if (node != null) { + this.commentData = new NodeCommentData(node); + fromNodeTree(node, this.commentData, this); + } + } + + private MappingNode toNodeTree(@Nullable NodeCommentData commentData, @NotNull ConfigurationSection section) { + List nodeTuples = new ArrayList<>(); + + for (Map.Entry entry : section.map.entrySet()) { + Node key = representer.represent(entry.getKey()); + + NodeCommentData childCommentData = null; + if (commentData != null) { + childCommentData = commentData.getChild(entry.getKey()); + } + + Node value; + if (entry.getValue() instanceof ConfigurationSection configurationSection) { + value = toNodeTree(childCommentData, configurationSection); + } else { + value = representer.represent(entry.getValue()); + } + + NodeTuple nodeTuple = new NodeTuple(key, value); + if (childCommentData != null) { + childCommentData.apply(nodeTuple); + } + + nodeTuples.add(nodeTuple); + } + + return new MappingNode(Tag.MAP, nodeTuples, DumperOptions.FlowStyle.BLOCK); + } + + private void fromNodeTree(@NotNull MappingNode input, @NotNull NodeCommentData commentData, + @NotNull ConfigurationSection section) throws InvalidConfigurationException { + constructor.flattenMapping(input); + + for (NodeTuple nodeTuple : input.getValue()) { + Node keyNode = nodeTuple.getKeyNode(); + String key = String.valueOf(constructor.constructObject(keyNode)); + if (key.isBlank() || key.indexOf(ConfigurationSection.PATH_SEPARATOR) != -1) { + throw new InvalidConfigurationException(String.format("Invalid key%s%n key can't be blank or contain any '%s'", + keyNode.getStartMark(), ConfigurationSection.PATH_SEPARATOR)); + } + + Node valueNode = nodeTuple.getValueNode(); + while (valueNode instanceof AnchorNode value) { + valueNode = value.getRealNode(); + } + + NodeCommentData childCommentData = new NodeCommentData(nodeTuple); + commentData.addChild(key, childCommentData); + + if (valueNode instanceof MappingNode value) { + fromNodeTree(value, childCommentData, section.createSection(key)); + } else { + section.set(key, constructor.constructObject(valueNode)); + } + } + } + + private boolean isNullOrEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/YamlConstructor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/YamlConstructor.java new file mode 100644 index 000000000..9acf5a40c --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/YamlConstructor.java @@ -0,0 +1,23 @@ +package dev.imprex.orebfuscator.config.yaml; + +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.constructor.Constructor; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; + +class YamlConstructor extends Constructor { + + public YamlConstructor(LoaderOptions loadingConfig) { + super(loadingConfig); + } + + @Override + public void flattenMapping(MappingNode node) { + super.flattenMapping(node); + } + + @Override + public Object constructObject(Node node) { + return super.constructObject(node); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/package-info.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/package-info.java new file mode 100644 index 000000000..380b89609 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/config/yaml/package-info.java @@ -0,0 +1,15 @@ +/** + * This package contains classes adapted or derived from the Bukkit Project, specifically the configuration package + * available at: + * https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit/browse/src/main/java/org/bukkit/configuration + *

+ * Original authors and contributors of the Bukkit Project hold copyright over the original work. The code is licensed + * under the GNU General Public License v3.0 (GPLv3), and this project continues to adhere to that license. + *

+ * Copyright (C) 2011-2024 Bukkit Project (original authors and contributors) License: GNU General Public License v3.0 + * (GPLv3) + *

+ * For more information, visit: https://hub.spigotmc.org/stash/projects/SPIGOT/repos/bukkit + */ + +package dev.imprex.orebfuscator.config.yaml; \ No newline at end of file diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/ChunkPacketAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/ChunkPacketAccessor.java new file mode 100644 index 000000000..e77b8533d --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/ChunkPacketAccessor.java @@ -0,0 +1,21 @@ +package dev.imprex.orebfuscator.interop; + +import java.util.function.Predicate; +import dev.imprex.orebfuscator.util.BlockPos; + +public interface ChunkPacketAccessor { + + WorldAccessor world(); + + int chunkX(); + + int chunkZ(); + + boolean isSectionPresent(int index); + + byte[] data(); + + void setData(byte[] data); + + void filterBlockEntities(Predicate predicate); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/RegistryAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/RegistryAccessor.java new file mode 100644 index 000000000..3f316c2b7 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/RegistryAccessor.java @@ -0,0 +1,24 @@ +package dev.imprex.orebfuscator.interop; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockTag; + +public interface RegistryAccessor { + + int getUniqueBlockStateCount(); + + int getMaxBitsPerBlockState(); + + boolean isAir(int blockId); + + boolean isOccluding(int blockId); + + boolean isBlockEntity(int blockId); + + @Nullable BlockProperties getBlockByName(@NotNull String name); + + @Nullable BlockTag getBlockTagByName(@NotNull String name); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/ServerAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/ServerAccessor.java new file mode 100644 index 000000000..c10e72b44 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/ServerAccessor.java @@ -0,0 +1,21 @@ +package dev.imprex.orebfuscator.interop; + +import java.nio.file.Path; +import java.util.List; +import dev.imprex.orebfuscator.util.Version; + +public interface ServerAccessor { + + Path getConfigDirectory(); + + Path getWorldDirectory(); + + String getOrebfuscatorVersion(); + + Version getMinecraftVersion(); + + RegistryAccessor getRegistry(); + + List getWorlds(); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/WorldAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/WorldAccessor.java new file mode 100644 index 000000000..9661af574 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/interop/WorldAccessor.java @@ -0,0 +1,20 @@ +package dev.imprex.orebfuscator.interop; + +public interface WorldAccessor { + + String getName(); + + int getHeight(); + + int getMinBuildHeight(); + + int getMaxBuildHeight(); + + int getSectionCount(); + + int getMinSection(); + + int getMaxSection(); + + int getSectionIndex(int y); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/LogLevel.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/LogLevel.java new file mode 100644 index 000000000..fc538f743 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/LogLevel.java @@ -0,0 +1,5 @@ +package dev.imprex.orebfuscator.logging; + +public enum LogLevel { + DEBUG, INFO, WARN, ERROR +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/LoggerAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/LoggerAccessor.java new file mode 100644 index 000000000..22bade655 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/LoggerAccessor.java @@ -0,0 +1,10 @@ +package dev.imprex.orebfuscator.logging; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface LoggerAccessor { + + void log(@NotNull LogLevel level, @NotNull String message, @Nullable Throwable throwable); + +} \ No newline at end of file diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/OfcLogger.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/OfcLogger.java new file mode 100644 index 000000000..00b16f334 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/OfcLogger.java @@ -0,0 +1,81 @@ +package dev.imprex.orebfuscator.logging; + +import java.util.Objects; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class OfcLogger { + + private static LoggerAccessor logger = new SystemLogger(); + + private static final Queue VERBOSE_LOG = new ConcurrentLinkedQueue<>(); + private static boolean verbose; + + public static void setLogger(@NotNull LoggerAccessor logger) { + if (OfcLogger.logger instanceof SystemLogger) { + OfcLogger.logger = Objects.requireNonNull(logger); + } + } + + public static void setVerboseLogging(boolean enabled) { + if (!verbose && enabled) { + verbose = true; + debug("Verbose logging has been enabled"); + } else { + verbose = enabled; + } + } + + @NotNull + public static String getLatestVerboseLog() { + return String.join("\n", VERBOSE_LOG); + } + + public static void debug(@NotNull String message) { + log(LogLevel.DEBUG, message); + } + + public static void info(@NotNull String message) { + log(LogLevel.INFO, message); + } + + public static void warn(@NotNull String message) { + log(LogLevel.WARN, message); + } + + public static void error(@NotNull Throwable throwable) { + log(LogLevel.ERROR, "An error occurred:", throwable); + } + + public static void error(@NotNull String message, @Nullable Throwable throwable) { + log(LogLevel.ERROR, message, throwable); + } + + public static void log(@NotNull LogLevel level, @NotNull String message) { + log(level, message, null); + } + + public static void log(@NotNull LogLevel level, @NotNull String message, @Nullable Throwable throwable) { + Objects.requireNonNull(level); + Objects.requireNonNull(message); + + if (level == LogLevel.DEBUG) { + // always store debug messages for system dumps + while (VERBOSE_LOG.size() >= 1000) { + VERBOSE_LOG.poll(); + } + + VERBOSE_LOG.offer(message); + + // filter out debug if verbose logging is disabled + if (!verbose) { + return; + } + } + + logger.log(level, message, throwable); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/SystemLogger.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/SystemLogger.java new file mode 100644 index 000000000..921a2f07e --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/logging/SystemLogger.java @@ -0,0 +1,19 @@ +package dev.imprex.orebfuscator.logging; + +import java.io.PrintStream; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class SystemLogger implements LoggerAccessor { + + @Override + public void log(@NotNull LogLevel level, @NotNull String message, @Nullable Throwable throwable) { + PrintStream stream = level == LogLevel.ERROR ? System.err : System.out; + stream.printf("[Orebfuscator - %s] %s%n", level, message); + + if (throwable != null) { + throwable.printStackTrace(stream); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/Reflector.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/Reflector.java new file mode 100644 index 000000000..6e823248d --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/Reflector.java @@ -0,0 +1,103 @@ +package dev.imprex.orebfuscator.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; +import org.jetbrains.annotations.NotNull; +import dev.imprex.orebfuscator.reflect.accessor.Accessors; +import dev.imprex.orebfuscator.reflect.accessor.ConstructorAccessor; +import dev.imprex.orebfuscator.reflect.accessor.FieldAccessor; +import dev.imprex.orebfuscator.reflect.accessor.MethodAccessor; +import dev.imprex.orebfuscator.reflect.predicate.ConstructorPredicate; +import dev.imprex.orebfuscator.reflect.predicate.FieldPredicate; +import dev.imprex.orebfuscator.reflect.predicate.MethodPredicate; + +public class Reflector { + + public static Reflector of(Class target) { + return new Reflector(target); + } + + private final @NotNull Class target; + private @NotNull Class recursiveUntil; + + private Reflector(@NotNull Class target) { + this.target = Objects.requireNonNull(target); + this.recursiveUntil = target; + } + + public Reflector recursive() { + return this.recursiveUntil(Object.class); + } + + public Reflector recursiveUntil(Class recursiveUntil) { + this.recursiveUntil = recursiveUntil != null + ? recursiveUntil + : this.target; + return this; + } + + private String className() { + if (target == recursiveUntil) { + return target.getTypeName(); + } else if (recursiveUntil == Object.class) { + return String.format("%s recursively", target.getTypeName()); + } else { + return String.format("%s recursively until %s", this.target.getTypeName(), + this.recursiveUntil.getTypeName()); + } + } + + private Stream get(Function, T[]> getter) { + Class current = this.target; + Stream stream = Stream.empty(); + + while (current != null) { + stream = Stream.concat(stream, Arrays.stream(getter.apply(current))); + if (current == recursiveUntil) { + break; + } + current = current.getSuperclass(); + } + + return stream; + } + + @NotNull + public Stream constructor(Predicate> predicate) { + Stream> stream = get(Class::getDeclaredConstructors); + return stream.filter(predicate).map(Accessors::wrap); + } + + @NotNull + public ConstructorPredicate constructor() { + return new ConstructorPredicate(this::constructor, this::className); + } + + @NotNull + public Stream field(Predicate predicate) { + Stream stream = get(Class::getDeclaredFields); + return stream.filter(predicate).map(Accessors::wrap); + } + + @NotNull + public FieldPredicate field() { + return new FieldPredicate(this::field, this::className); + } + + @NotNull + public Stream method(Predicate predicate) { + Stream stream = get(Class::getDeclaredMethods); + return stream.filter(predicate).map(Accessors::wrap); + } + + @NotNull + public MethodPredicate method() { + return new MethodPredicate(this::method, this::className); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/Accessors.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/Accessors.java new file mode 100644 index 000000000..2fc4b4502 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/Accessors.java @@ -0,0 +1,109 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodHandles.Lookup; +import java.lang.invoke.MethodType; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +public final class Accessors { + + private static final Lookup LOOKUP = MethodHandles.lookup(); + + private static final MethodType FIELD_GETTER = MethodType.methodType(Object.class, Object.class); + private static final MethodType FIELD_SETTER = MethodType.methodType(void.class, Object.class, Object.class); + + private Accessors() { + } + + public static @NotNull ConstructorAccessor wrap(@NotNull Constructor constructor) { + return create(constructor, () -> { + MethodHandle methodHandle = LOOKUP.unreflectConstructor(constructor); + methodHandle = generifyExecutable(methodHandle, false, true); + + return new DefaultConstructorAccessor(constructor, methodHandle); + }); + } + + public static @NotNull FieldAccessor wrap(@NotNull Field field) { + return create(field, () -> { + MethodHandle getter = LOOKUP.unreflectGetter(field); + MethodHandle setter = null; + + try { + setter = LOOKUP.unreflectSetter(field); + } catch (IllegalAccessException e) { + } + + if (Modifier.isStatic(field.getModifiers())) { + getter = MethodHandles.dropArguments(getter, 0, Object.class); + setter = setter != null ? MethodHandles.dropArguments(setter, 0, Object.class) : null; + } + + getter = getter.asType(FIELD_GETTER); + setter = setter != null ? setter.asType(FIELD_SETTER) : null; + + return new DefaultFieldAccessor(field, getter, setter); + }); + } + + public static @NotNull MethodAccessor wrap(@NotNull Method method) { + return create(method, () -> { + MethodHandle methodHandle = LOOKUP.unreflect(method); + methodHandle = generifyExecutable(methodHandle, Modifier.isStatic(method.getModifiers()), false); + + return new DefaultMethodAccessor(method, methodHandle); + }); + } + + private static @NotNull TAccessor create( + @NotNull TMember member, @NotNull AccessorFactory factory) { + Objects.requireNonNull(member); + + @SuppressWarnings("deprecation") + boolean accessible = member.isAccessible(); + try { + member.setAccessible(true); + + return factory.create(); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Unable to create accessor for " + member, e); + } finally { + member.setAccessible(accessible); + } + } + + private static MethodHandle generifyExecutable(MethodHandle handle, boolean isStatic, boolean isConstructor) { + // force the method to use a fixed arity, ensuring it accepts varargs directly as an array + MethodHandle target = handle.asFixedArity(); + + // determine the number of parameters to spread: subtract 1 for instance methods, as the first parameter is the + // receiver (the instance object) + int paramCount = handle.type().parameterCount() - (isConstructor || isStatic ? 0 : 1); + + // spread the Object[] arguments into individual parameters (after the instance object for non-static methods) + target = target.asSpreader(Object[].class, paramCount); + + if (isStatic) { + // add a dummy instance parameter at the beginning for static methods to unify the calling convention with + // instance methods + target = MethodHandles.dropArguments(target, 0, Object.class); + } + + // convert the MethodHandle to a generic signature: + // return Object, take Object instance (receiver) if needed, and an Object[] for the remaining arguments + return target.asType(MethodType.genericMethodType(isConstructor ? 0 : 1, true)); + } + + private interface AccessorFactory { + + T create() throws IllegalAccessException; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/ConstructorAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/ConstructorAccessor.java new file mode 100644 index 000000000..132538feb --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/ConstructorAccessor.java @@ -0,0 +1,9 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import java.lang.reflect.Constructor; + +public interface ConstructorAccessor extends MemberAccessor> { + + Object invoke(Object... args); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultConstrutorAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultConstrutorAccessor.java new file mode 100644 index 000000000..58b41fb7f --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultConstrutorAccessor.java @@ -0,0 +1,24 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import org.jetbrains.annotations.NotNull; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Constructor; + +record DefaultConstructorAccessor(@NotNull Constructor member, @NotNull MethodHandle methodHandle) implements + ConstructorAccessor { + + @Override + public Object invoke(Object... args) { + try { + return methodHandle.invokeExact(args); + } catch (Throwable throwable) { + throw new IllegalStateException("Unable to construct new instance using " + member, throwable); + } + } + + @Override + public @NotNull Constructor member() { + return member; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultFieldAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultFieldAccessor.java new file mode 100644 index 000000000..c91dab56e --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultFieldAccessor.java @@ -0,0 +1,42 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Field; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +record DefaultFieldAccessor(@NotNull Field member, @NotNull MethodHandle getterHandle, + @Nullable MethodHandle setterHandle) implements FieldAccessor { + + @Override + public boolean readonly() { + return setterHandle == null; + } + + @Override + public Object get(Object instance) { + try { + return getterHandle.invokeExact(instance); + } catch (Throwable throwable) { + throw new IllegalStateException("Unable to get field value of " + member, throwable); + } + } + + @Override + public void set(Object instance, Object value) { + if (readonly()) { + throw new IllegalStateException("Can't set value of trusted final field " + member); + } + + try { + setterHandle.invokeExact(instance, value); + } catch (Throwable throwable) { + throw new IllegalStateException("Unable to set value of field " + member, throwable); + } + } + + @Override + public @NotNull Field member() { + return this.member; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultMethodAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultMethodAccessor.java new file mode 100644 index 000000000..940bb2f59 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/DefaultMethodAccessor.java @@ -0,0 +1,23 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import java.lang.invoke.MethodHandle; +import java.lang.reflect.Method; +import org.jetbrains.annotations.NotNull; + +record DefaultMethodAccessor(@NotNull Method member, @NotNull MethodHandle methodHandle) implements + MethodAccessor { + + @Override + public Object invoke(Object instance, Object... args) { + try { + return methodHandle.invokeExact(instance, args); + } catch (Throwable throwable) { + throw new IllegalStateException("Unable to invoke method " + member, throwable); + } + } + + @Override + public @NotNull Method member() { + return member; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/FieldAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/FieldAccessor.java new file mode 100644 index 000000000..d69b72ff9 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/FieldAccessor.java @@ -0,0 +1,13 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import java.lang.reflect.Field; + +public interface FieldAccessor extends MemberAccessor { + + boolean readonly(); + + Object get(Object instance); + + void set(Object instance, Object value); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/MemberAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/MemberAccessor.java new file mode 100644 index 000000000..ce43b857f --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/MemberAccessor.java @@ -0,0 +1,10 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Member; + +public interface MemberAccessor { + + @NotNull TMember member(); +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/MethodAccessor.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/MethodAccessor.java new file mode 100644 index 000000000..11198eb80 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/accessor/MethodAccessor.java @@ -0,0 +1,9 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import java.lang.reflect.Method; + +public interface MethodAccessor extends MemberAccessor { + + Object invoke(Object target, Object... args); + +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/AbstractExecutablePredicate.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/AbstractExecutablePredicate.java new file mode 100644 index 000000000..d3c6722a3 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/AbstractExecutablePredicate.java @@ -0,0 +1,148 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import java.lang.reflect.Executable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import dev.imprex.orebfuscator.reflect.accessor.MemberAccessor; + +abstract sealed class AbstractExecutablePredicate< + TThis extends AbstractExecutablePredicate, + TAccessor extends MemberAccessor, + TExecutable extends Executable + > extends AbstractMemberPredicate permits ConstructorPredicate, MethodPredicate { + + private final @NotNull List exceptionClass = new ArrayList<>(); + private final @NotNull List parameterClass = new ArrayList<>(); + private int parameterCount = -1; + + public AbstractExecutablePredicate( + @NotNull Function> producer, + @NotNull Supplier error) { + super(producer, error); + } + + @Override + public boolean test(@NotNull TExecutable executable) { + return super.test(executable) + && IndexedClassMatcher.all(executable.getExceptionTypes(), exceptionClass) + && IndexedClassMatcher.all(executable.getParameterTypes(), parameterClass) + && (parameterCount < 0 || parameterCount == executable.getParameterCount()); + } + + @Override + void requirements(@NotNull RequirementCollector collector) { + super.requirements(collector); + + if (!exceptionClass.isEmpty()) { + collector.collect("exceptionClass", IndexedClassMatcher.toString(exceptionClass)); + } + if (!parameterClass.isEmpty()) { + collector.collect("parameterClass", IndexedClassMatcher.toString(parameterClass)); + } + if (parameterCount >= 0) { + collector.collect("parameterCount", parameterCount); + } + } + + public @NotNull TThis exception(@NotNull ClassPredicate matcher) { + this.exceptionClass.add(new IndexedClassMatcher(Objects.requireNonNull(matcher))); + return instance(); + } + + public @NotNull TThis exception(@NotNull ClassPredicate matcher, int index) { + this.exceptionClass.add(new IndexedClassMatcher(Objects.requireNonNull(matcher), index)); + return instance(); + } + + public @NotNull ClassPredicate.Builder exception() { + return new ClassPredicate.Builder<>(this::exception); + } + + public @NotNull ClassPredicate.Builder exception(int index) { + return new ClassPredicate.Builder<>(m -> this.exception(m, index)); + } + + public @NotNull TThis parameter(@NotNull ClassPredicate matcher) { + this.parameterClass.add(new IndexedClassMatcher(Objects.requireNonNull(matcher))); + return instance(); + } + + public @NotNull TThis parameter(@NotNull ClassPredicate matcher, int index) { + this.parameterClass.add(new IndexedClassMatcher(Objects.requireNonNull(matcher), index)); + return instance(); + } + + public @NotNull ClassPredicate.Builder parameter() { + return new ClassPredicate.Builder<>(this::parameter); + } + + public @NotNull ClassPredicate.Builder parameter(int index) { + return new ClassPredicate.Builder<>(m -> this.parameter(m, index)); + } + + public @NotNull TThis parameterCount(int parameterCount) { + this.parameterCount = parameterCount; + return instance(); + } + + private record IndexedClassMatcher(@NotNull ClassPredicate matcher, @Nullable Integer index) implements + Comparable { + + private static boolean all(@NotNull Class[] classArray, @NotNull List classMatchers) { + return classMatchers.stream().allMatch(matcher -> matcher.matches(classArray)); + } + + private static String toString(@NotNull List classMatchers) { + return classMatchers.stream() + .sorted() + .map(IndexedClassMatcher::toString) + .collect(Collectors.joining(",\n ", "{\n ", "\n }")); + } + + public IndexedClassMatcher(@NotNull ClassPredicate matcher) { + this(matcher, null); + } + + public boolean matches(@NotNull Class[] classArray) { + if (index() == null) { + for (Class entry : classArray) { + if (matcher().test(entry)) { + return true; + } + } + return false; + } + + return index() < classArray.length && matcher().test(classArray[index()]); + } + + @Override + public int compareTo(@NotNull IndexedClassMatcher other) { + if (this.index == null && other.index == null) { + return 0; + } + if (this.index == null) { + return -1; + } + if (other.index == null) { + return 1; + } + return this.index.compareTo(other.index); + } + + @Override + public @NotNull String toString() { + String key = index() == null ? "" : index().toString(); + return String.format("%s=%s", key, matcher().requirement()); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/AbstractMemberPredicate.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/AbstractMemberPredicate.java new file mode 100644 index 000000000..5cff71ac0 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/AbstractMemberPredicate.java @@ -0,0 +1,192 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import org.intellij.lang.annotations.MagicConstant; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import dev.imprex.orebfuscator.reflect.accessor.MemberAccessor; + +abstract sealed class AbstractMemberPredicate< + TThis extends AbstractMemberPredicate, + TAccessor extends MemberAccessor, + TMember extends Member + > implements Predicate permits AbstractExecutablePredicate, FieldPredicate { + + private final @NotNull Function> producer; + private final @NotNull Supplier error; + + private int requiredModifiers; + private int bannedModifiers; + private boolean includeSynthetic; + private @Nullable Pattern name; + private @Nullable ClassPredicate declaringClass; + + public AbstractMemberPredicate( + @NotNull Function> producer, + @NotNull Supplier error) { + this.producer = producer; + this.error = error; + } + + @Override + public boolean test(@NotNull TMember member) { + int modifiers = member.getModifiers(); + return (modifiers & requiredModifiers) == requiredModifiers + && (modifiers & bannedModifiers) == 0 + && (includeSynthetic || !member.isSynthetic()) + && (name == null || name.matcher(member.getName()).matches()) + && (declaringClass == null || declaringClass.test(member.getDeclaringClass())); + } + + void requirements(@NotNull RequirementCollector collector) { + if (requiredModifiers != 0) { + collector.collect("requiredModifiers", Modifier.toString(requiredModifiers)); + } + if (bannedModifiers != 0) { + collector.collect("bannedModifiers", Modifier.toString(bannedModifiers)); + } + if (includeSynthetic) { + collector.collect("includeSynthetic"); + } + if (name != null) { + collector.collect("name", name); + } + if (declaringClass != null) { + collector.collect("declaringClass", declaringClass.requirement()); + } + } + + private @NotNull IllegalArgumentException requirementException() { + var collector = new RequirementCollector(error.get()); + requirements(collector); + return new IllegalArgumentException(collector.get()); + } + + protected abstract @NotNull TThis instance(); + + public @NotNull TThis requireModifier(@MagicConstant(flagsFromClass = Modifier.class) int modifiers) { + this.requiredModifiers |= modifiers; + return instance(); + } + + public @NotNull TThis requirePublic() { + return requireModifier(Modifier.PUBLIC); + } + + public @NotNull TThis requireProtected() { + return requireModifier(Modifier.PROTECTED); + } + + public @NotNull TThis requirePrivate() { + return requireModifier(Modifier.PRIVATE); + } + + public @NotNull TThis requireStatic() { + return requireModifier(Modifier.STATIC); + } + + public @NotNull TThis requireFinal() { + return requireModifier(Modifier.FINAL); + } + + public @NotNull TThis banModifier(@MagicConstant(flagsFromClass = Modifier.class) int modifiers) { + this.bannedModifiers |= modifiers; + return instance(); + } + + public @NotNull TThis banPublic() { + return banModifier(Modifier.PUBLIC); + } + + public @NotNull TThis banProtected() { + return banModifier(Modifier.PROTECTED); + } + + public @NotNull TThis banPrivate() { + return banModifier(Modifier.PRIVATE); + } + + public @NotNull TThis banStatic() { + return banModifier(Modifier.STATIC); + } + + public @NotNull TThis banFinal() { + return banModifier(Modifier.FINAL); + } + + public @NotNull TThis includeSynthetic() { + this.includeSynthetic = true; + return instance(); + } + + public @NotNull TThis nameRegex(@NotNull Pattern pattern) { + this.name = Objects.requireNonNull(pattern); + return instance(); + } + + public @NotNull TThis nameIs(@NotNull String name) { + String pattern = Pattern.quote(Objects.requireNonNull(name)); + return nameRegex(Pattern.compile(pattern)); + } + + public @NotNull TThis nameIsIgnoreCase(@NotNull String name) { + String pattern = Pattern.quote(Objects.requireNonNull(name)); + return nameRegex(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE)); + } + + public @NotNull TThis declaringClass(@NotNull ClassPredicate matcher) { + this.declaringClass = Objects.requireNonNull(matcher); + return instance(); + } + + public @NotNull ClassPredicate.Builder declaringClass() { + return new ClassPredicate.Builder<>(this::declaringClass); + } + + @Contract(pure = true) + public @NotNull Stream stream() { + return producer.apply(instance()); + } + + @Contract(pure = true) + public @Nullable TAccessor get(int index) { + return stream().skip(index).findFirst().orElse(null); + } + + @Contract(pure = true) + public @NotNull TAccessor getOrThrow(int index) { + return stream().skip(index).findFirst().orElseThrow(this::requirementException); + } + + @Contract(pure = true) + public @Nullable TAccessor first() { + return stream().findFirst().orElse(null); + } + + @Contract(pure = true) + public @NotNull TAccessor firstOrThrow() { + return stream().findFirst().orElseThrow(this::requirementException); + } + + @Contract(pure = true) + public @Nullable TAccessor find(@NotNull Predicate predicate) { + return stream().filter(accessor -> predicate.test(accessor.member())) + .findFirst().orElse(null); + } + + @Contract(pure = true) + public @NotNull TAccessor findOrThrow(@NotNull Predicate predicate) { + return stream().filter(accessor -> predicate.test(accessor.member())) + .findFirst().orElseThrow(this::requirementException); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/ClassPredicate.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/ClassPredicate.java new file mode 100644 index 000000000..09e223674 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/ClassPredicate.java @@ -0,0 +1,192 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; + +public interface ClassPredicate extends Predicate> { + + boolean test(@NotNull Class type); + + @NotNull + String requirement(); + + class Builder { + + private final Function returnFunction; + + public Builder(Function returnFunction) { + this.returnFunction = returnFunction; + } + + @NotNull + public TParent is(@NotNull Class type) { + return returnFunction.apply(new IsClassPredicate(type)); + } + + @NotNull + public TParent superOf(@NotNull Class type) { + return returnFunction.apply(new SuperClassPredicate(type)); + } + + @NotNull + public TParent subOf(@NotNull Class type) { + return returnFunction.apply(new SubClassPredicate(type)); + } + + @NotNull + public TParent any(@NotNull Set> types) { + return returnFunction.apply(new AnyClassPredicate(types)); + } + + @NotNull + public TParent any(@NotNull Class... types) { + return any(Set.of(types)); + } + + @NotNull + public TParent regex(@NotNull Pattern pattern) { + return returnFunction.apply(new RegexClassPredicate(pattern)); + } + } + + class IsClassPredicate implements ClassPredicate { + + private final @NotNull Class expected; + + public IsClassPredicate(@NotNull Class expected) { + this.expected = Objects.requireNonNull(expected); + } + + @Override + public boolean test(@NotNull Class type) { + Objects.requireNonNull(type); + + return this.expected.equals(type); + } + + @NotNull + @Override + public String requirement() { + return String.format("{is %s}", this.expected.getTypeName()); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof IsClassPredicate other && Objects.equals(this.expected, other.expected)); + } + } + + class SuperClassPredicate implements ClassPredicate { + + private final @NotNull Class expected; + + public SuperClassPredicate(@NotNull Class expected) { + this.expected = Objects.requireNonNull(expected); + } + + @Override + public boolean test(@NotNull Class type) { + Objects.requireNonNull(type); + + return type.isAssignableFrom(this.expected); + } + + @NotNull + @Override + public String requirement() { + return String.format("{super-class-of %s}", this.expected.getTypeName()); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof SuperClassPredicate other && Objects.equals(this.expected, other.expected)); + } + } + + class SubClassPredicate implements ClassPredicate { + + private final @NotNull Class expected; + + public SubClassPredicate(@NotNull Class expected) { + this.expected = Objects.requireNonNull(expected); + } + + @Override + public boolean test(@NotNull Class type) { + Objects.requireNonNull(type); + + return this.expected.isAssignableFrom(type); + } + + @NotNull + @Override + public String requirement() { + return String.format("{sub-class-of %s}", this.expected.getTypeName()); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof SubClassPredicate other && Objects.equals(this.expected, other.expected)); + } + } + + class AnyClassPredicate implements ClassPredicate { + + private final @NotNull Set> expected; + + public AnyClassPredicate(@NotNull Set> expected) { + this.expected = Objects.requireNonNull(expected); + } + + @Override + public boolean test(@NotNull Class type) { + Objects.requireNonNull(type); + + return this.expected.contains(type); + } + + @NotNull + @Override + public String requirement() { + return String.format("{any %s}", + this.expected.stream().map(Class::getTypeName).collect(Collectors.joining(", ", "(", ")"))); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof AnyClassPredicate other && Objects.equals(this.expected, other.expected)); + } + } + + class RegexClassPredicate implements ClassPredicate { + + private final @NotNull Pattern expected; + + public RegexClassPredicate(@NotNull Pattern expected) { + this.expected = Objects.requireNonNull(expected); + } + + @Override + public boolean test(@NotNull Class type) { + Objects.requireNonNull(type); + + return this.expected.matcher(type.getTypeName()).matches(); + } + + @NotNull + @Override + public String requirement() { + return String.format("{regex %s}", this.expected); + } + + @Override + public boolean equals(Object obj) { + return this == obj || (obj instanceof RegexClassPredicate other && Objects.equals(this.expected, other.expected)); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/ConstructorPredicate.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/ConstructorPredicate.java new file mode 100644 index 000000000..d1e89d3a3 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/ConstructorPredicate.java @@ -0,0 +1,25 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import java.lang.reflect.Constructor; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.jetbrains.annotations.NotNull; + +import dev.imprex.orebfuscator.reflect.accessor.ConstructorAccessor; + +public final class ConstructorPredicate + extends AbstractExecutablePredicate> { + + public ConstructorPredicate( + @NotNull Function> producer, + @NotNull Supplier className) { + super(producer, () -> String.format("Can't find constructor in class %s matching: ", className.get())); + } + + @Override + protected @NotNull ConstructorPredicate instance() { + return this; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/FieldPredicate.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/FieldPredicate.java new file mode 100644 index 000000000..6af1db372 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/FieldPredicate.java @@ -0,0 +1,50 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import dev.imprex.orebfuscator.reflect.accessor.FieldAccessor; +import java.lang.reflect.Field; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class FieldPredicate extends AbstractMemberPredicate { + + private @Nullable ClassPredicate type; + + public FieldPredicate( + @NotNull Function> producer, + @NotNull Supplier className) { + super(producer, () -> String.format("Can't find field in class %s matching: ", className.get())); + } + + @Override + public boolean test(@NotNull Field field) { + return super.test(field) + && (type == null || type.test(field.getType())); + } + + @Override + void requirements(@NotNull RequirementCollector collector) { + super.requirements(collector); + + if (type != null) { + collector.collect("type", type.requirement()); + } + } + + public @NotNull FieldPredicate type(@NotNull ClassPredicate matcher) { + this.type = Objects.requireNonNull(matcher); + return this; + } + + public @NotNull ClassPredicate.Builder type() { + return new ClassPredicate.Builder<>(this::type); + } + + @Override + protected @NotNull FieldPredicate instance() { + return this; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/MethodPredicate.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/MethodPredicate.java new file mode 100644 index 000000000..462d6e83b --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/MethodPredicate.java @@ -0,0 +1,50 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import dev.imprex.orebfuscator.reflect.accessor.MethodAccessor; +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class MethodPredicate extends AbstractExecutablePredicate { + + private @Nullable ClassPredicate returnType; + + public MethodPredicate( + @NotNull Function> producer, + @NotNull Supplier className) { + super(producer, () -> String.format("Can't find constructor in class %s matching: ", className.get())); + } + + @Override + public boolean test(@NotNull Method method) { + return super.test(method) + && (returnType == null || returnType.test(method.getReturnType())); + } + + @Override + void requirements(@NotNull RequirementCollector collector) { + super.requirements(collector); + + if (returnType != null) { + collector.collect("returnType", returnType.requirement()); + } + } + + public @NotNull MethodPredicate returnType(@NotNull ClassPredicate matcher) { + this.returnType = Objects.requireNonNull(matcher); + return this; + } + + public @NotNull ClassPredicate.Builder returnType() { + return new ClassPredicate.Builder<>(this::returnType); + } + + @Override + protected @NotNull MethodPredicate instance() { + return this; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/RequirementCollector.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/RequirementCollector.java new file mode 100644 index 000000000..d1a91e771 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/reflect/predicate/RequirementCollector.java @@ -0,0 +1,36 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import java.util.Objects; +import java.util.StringJoiner; +import org.jetbrains.annotations.NotNull; + +class RequirementCollector { + + private final StringJoiner entries; + + public RequirementCollector(@NotNull String prefix) { + this.entries = new StringJoiner(",\n", prefix + "{\n", "\n}"); + } + + @NotNull + public RequirementCollector collect(@NotNull String name) { + Objects.requireNonNull(name); + + entries.add(" " + name); + return this; + } + + @NotNull + public RequirementCollector collect(@NotNull String name, @NotNull Object value) { + Objects.requireNonNull(name); + Objects.requireNonNull(value); + + entries.add(" " + name + ": " + value); + return this; + } + + @NotNull + public String get() { + return entries.toString(); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockPos.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockPos.java new file mode 100644 index 000000000..8e46b483f --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockPos.java @@ -0,0 +1,71 @@ +package dev.imprex.orebfuscator.util; + +public record BlockPos(int x, int y, int z) implements Comparable { + + // from net.minecraft.core.BlockPos + private static final int BITS_PER_X = 26; + private static final int BITS_PER_Z = BITS_PER_X; + private static final int BITS_PER_Y = 64 - BITS_PER_X - BITS_PER_Z; + + private static final int OFFSET_Y = 0; + private static final int OFFSET_Z = OFFSET_Y + BITS_PER_Y; + private static final int OFFSET_X = OFFSET_Z + BITS_PER_Z; + + private static final long MASK_X = (1L << BITS_PER_X) - 1L; + private static final long MASK_Y = (1L << BITS_PER_Y) - 1L; + private static final long MASK_Z = (1L << BITS_PER_Z) - 1L; + + // from net.minecraft.world.level.dimension.DimensionType + public static final int Y_SIZE = (1 << BITS_PER_Y) - 32; + public static final int MAX_Y = (Y_SIZE >> 1) - 1; + public static final int MIN_Y = MAX_Y - Y_SIZE + 1; + + public BlockPos add(int x, int y, int z) { + return x == 0 && y == 0 && z == 0 ? this : new BlockPos(this.x + x, this.y + y, this.z + z); + } + + public double distanceSquared(double x, double y, double z) { + double dx = this.x - x; + double dy = this.y - y; + double dz = this.z - z; + return dx * dx + dy * dy + dz * dz; + } + + public long toLong() { + return (this.x & MASK_X) << OFFSET_X | (this.y & MASK_Y) << OFFSET_Y | (this.z & MASK_Z) << OFFSET_Z; + } + + public static BlockPos fromLong(long value) { + int x = (int) (value << (64 - BITS_PER_X - OFFSET_X) >> (64 - BITS_PER_X)); + int y = (int) (value << (64 - BITS_PER_Y - OFFSET_Y) >> (64 - BITS_PER_Y)); + int z = (int) (value << (64 - BITS_PER_Z - OFFSET_Z) >> (64 - BITS_PER_Z)); + return new BlockPos(x, y, z); + } + + public int toSectionPos() { + return (this.x & 0xF) << 12 | (this.y & 0xFFF) << 0 | (this.z & 0xF) << 16; + } + + public static BlockPos fromSectionPos(int x, int z, int sectionPos) { + x += (sectionPos >> 12) & 0xF; + int y = (sectionPos << 20 >> 20); + z += (sectionPos >> 16) & 0xF; + return new BlockPos(x, y, z); + } + + @Override + public int compareTo(BlockPos other) { + if (this.y == other.y) { + if (this.z == other.z) { + return this.x - other.x; + } + return this.z - other.z; + } + return this.y - other.y; + } + + @Override + public String toString() { + return "BlockPos [x=" + x + ", y=" + y + ", z=" + z + "]"; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockProperties.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockProperties.java new file mode 100644 index 000000000..30a0561ce --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockProperties.java @@ -0,0 +1,98 @@ +package dev.imprex.orebfuscator.util; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import com.google.common.collect.ImmutableList; + +public class BlockProperties { + + public static Builder builder(NamespacedKey key) { + return new Builder(key); + } + + private final NamespacedKey key; + private final BlockStateProperties defaultBlockState; + private final ImmutableList blockStates; + + private BlockProperties(Builder builder) { + this.key = builder.key; + this.defaultBlockState = builder.defaultBlockState; + this.blockStates = ImmutableList.copyOf(builder.blockStates); + } + + public NamespacedKey getKey() { + return key; + } + + public BlockStateProperties getDefaultBlockState() { + return defaultBlockState; + } + + public ImmutableList getBlockStates() { + return blockStates; + } + + @Override + public int hashCode() { + return this.key.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BlockProperties other)) { + return false; + } + return Objects.equals(key, other.key); + } + + @Override + public String toString() { + return "BlockProperties [key=" + key + ", defaultBlockState=" + defaultBlockState + ", blockStates=" + blockStates + + "]"; + } + + public static class Builder { + + private final NamespacedKey key; + + private BlockStateProperties defaultBlockState; + private final Set blockStates = new HashSet<>(); + + private Builder(NamespacedKey key) { + this.key = key; + } + + public Builder withBlockState(BlockStateProperties blockState) { + if (!blockStates.add(blockState)) { + throw new IllegalStateException( + String.format("duplicate block state id (%s) for block: %s", blockState.getId(), key)); + } + + if (blockState.isDefaultState()) { + // check for multiple default blocks + if (this.defaultBlockState != null) { + throw new IllegalStateException( + String.format("multiple default block states for block: %s", blockState.getId(), key)); + } + + this.defaultBlockState = blockState; + } + + return this; + } + + public BlockProperties build() { + Objects.requireNonNull(this.defaultBlockState, "missing default block state for block: " + this.key); + + if (this.blockStates.size() == 0) { + throw new IllegalStateException("missing block states for block: " + this.key); + } + + return new BlockProperties(this); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockStateProperties.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockStateProperties.java new file mode 100644 index 000000000..9c53c5f33 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockStateProperties.java @@ -0,0 +1,103 @@ +package dev.imprex.orebfuscator.util; + +public class BlockStateProperties { + + public static Builder builder(int id) { + return new Builder(id); + } + + private final int id; + + private final boolean isAir; + private final boolean isOccluding; + private final boolean isBlockEntity; + private final boolean isDefaultState; + + private BlockStateProperties(Builder builder) { + this.id = builder.id; + this.isAir = builder.isAir; + this.isOccluding = builder.isOccluding; + this.isBlockEntity = builder.isBlockEntity; + this.isDefaultState = builder.isDefaultState; + } + + public int getId() { + return id; + } + + public boolean isAir() { + return isAir; + } + + public boolean isOccluding() { + return isOccluding; + } + + public boolean isBlockEntity() { + return isBlockEntity; + } + + public boolean isDefaultState() { + return isDefaultState; + } + + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BlockStateProperties other)) { + return false; + } + return id == other.id; + } + + @Override + public String toString() { + return "BlockStateProperties [id=" + id + ", isDefaultState=" + isDefaultState + ", isAir=" + isAir + + ", isOccluding=" + isOccluding + ", isBlockEntity=" + isBlockEntity + "]"; + } + + public static class Builder { + + private final int id; + + private boolean isAir; + private boolean isOccluding; + private boolean isBlockEntity; + private boolean isDefaultState; + + private Builder(int id) { + this.id = id; + } + + public Builder withIsAir(boolean isAir) { + this.isAir = isAir; + return this; + } + + public Builder withIsOccluding(boolean isOccluding) { + this.isOccluding = isOccluding; + return this; + } + + public Builder withIsBlockEntity(boolean isBlockEntity) { + this.isBlockEntity = isBlockEntity; + return this; + } + + public Builder withIsDefaultState(boolean isDefaultState) { + this.isDefaultState = isDefaultState; + return this; + } + + public BlockStateProperties build() { + return new BlockStateProperties(this); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockTag.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockTag.java new file mode 100644 index 000000000..5769bda45 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/BlockTag.java @@ -0,0 +1,30 @@ +package dev.imprex.orebfuscator.util; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; +import org.jetbrains.annotations.NotNull; + +public record BlockTag(@NotNull NamespacedKey key, @NotNull Set blocks) { + + public BlockTag(@NotNull NamespacedKey key, @NotNull Set blocks) { + this.key = key; + this.blocks = Collections.unmodifiableSet(blocks); + } + + @Override + public int hashCode() { + return this.key.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BlockTag other)) { + return false; + } + return Objects.equals(key, other.key); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/ChunkCacheKey.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/ChunkCacheKey.java new file mode 100644 index 000000000..2de791560 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/ChunkCacheKey.java @@ -0,0 +1,15 @@ +package dev.imprex.orebfuscator.util; + +import org.jetbrains.annotations.NotNull; +import dev.imprex.orebfuscator.interop.WorldAccessor; + +public record ChunkCacheKey(@NotNull String world, int x, int z) { + + public ChunkCacheKey(@NotNull WorldAccessor world, BlockPos position) { + this(world.getName(), position.x() >> 4, position.z() >> 4); + } + + public ChunkCacheKey(@NotNull WorldAccessor world, int x, int z) { + this(world.getName(), x, z); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/ChunkDirection.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/ChunkDirection.java new file mode 100644 index 000000000..a9f65cb33 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/ChunkDirection.java @@ -0,0 +1,40 @@ +package dev.imprex.orebfuscator.util; + +public enum ChunkDirection { + + NORTH(1, 0), EAST(0, 1), SOUTH(-1, 0), WEST(0, -1); + + private final int offsetX; + private final int offsetZ; + + ChunkDirection(int offsetX, int offsetZ) { + this.offsetX = offsetX; + this.offsetZ = offsetZ; + } + + public int getOffsetX() { + return offsetX; + } + + public int getOffsetZ() { + return offsetZ; + } + + public static ChunkDirection fromPosition(ChunkCacheKey key, int targetX, int targetZ) { + int offsetX = (targetX >> 4) - key.x(); + int offsetZ = (targetZ >> 4) - key.z(); + + if (offsetX == 1 && offsetZ == 0) { + return NORTH; + } else if (offsetX == 0 && offsetZ == 1) { + return EAST; + } else if (offsetX == -1 && offsetZ == 0) { + return SOUTH; + } else if (offsetX == 0 && offsetZ == -1) { + return WEST; + } + + throw new IllegalArgumentException( + String.format("invalid offset (origin: %s, x: %d, z: %d)", key, targetX, targetZ)); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/JavaVersion.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/JavaVersion.java new file mode 100644 index 000000000..fdf6e155b --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/JavaVersion.java @@ -0,0 +1,36 @@ +package dev.imprex.orebfuscator.util; + +public final class JavaVersion { + + private static final int JAVA_VERSION = javaMajorVersion(); + + public static int get() { + return JAVA_VERSION; + } + + private static int javaMajorVersion() { + return majorVersion(System.getProperty("java.specification.version", "1.6")); + } + + /** + * taken from: + * https://github.com/netty/netty/blob/7ad2b91515b3affaeadb4b2975cd6d2a8342c403/common/src/main/java/io/netty/util/internal/PlatformDependent0.java#L1037 + */ + private static int majorVersion(final String javaSpecVersion) { + final String[] components = javaSpecVersion.split("\\."); + final int[] version = new int[components.length]; + for (int i = 0; i < components.length; i++) { + version[i] = Integer.parseInt(components[i]); + } + + if (version[0] == 1) { + assert version[1] >= 6; + return version[1]; + } else { + return version[0]; + } + } + + private JavaVersion() { + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/MathUtil.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/MathUtil.java new file mode 100644 index 000000000..095a8fa87 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/MathUtil.java @@ -0,0 +1,25 @@ +package dev.imprex.orebfuscator.util; + +public class MathUtil { + + public static int ceilToPowerOfTwo(int value) { + value--; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + value++; + return value; + } + + public static int clamp(int value, int min, int max) { + return Math.max(min, Math.min(max, value)); + } + + public static int ceilLog2(int value) { + int result = 31 - Integer.numberOfLeadingZeros(value); + // add 1 if value is NOT a power of 2 (to do the ceil) + return result + (value != 0 && (value & value - 1) == 0 ? 0 : 1); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/NamespacedKey.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/NamespacedKey.java new file mode 100644 index 000000000..3a029d3b1 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/NamespacedKey.java @@ -0,0 +1,152 @@ +package dev.imprex.orebfuscator.util; + +import org.jetbrains.annotations.NotNull; + +/** + * Represents a String based key which consists of two components - a namespace and a key. + *

+ * Namespaces may only contain lowercase alphanumeric characters, periods, underscores, and hyphens. + *

+ * Keys may only contain lowercase alphanumeric characters, periods, underscores, hyphens, and forward slashes. + * + * @author org.bukkit.NamespacedKey from 1.19.4 + * + */ +public record NamespacedKey(String namespace, String key) { + + /** + * The namespace representing all inbuilt keys. + */ + public static final String MINECRAFT = "minecraft"; + + private static boolean isValidNamespaceChar(char c) { + return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-'; + } + + private static boolean isValidKeyChar(char c) { + return isValidNamespaceChar(c) || c == '/'; + } + + private static boolean isValidNamespace(String namespace) { + int len = namespace.length(); + if (len == 0) { + return false; + } + + for (int i = 0; i < len; i++) { + if (!isValidNamespaceChar(namespace.charAt(i))) { + return false; + } + } + + return true; + } + + private static boolean isValidKey(String key) { + int len = key.length(); + if (len == 0) { + return false; + } + + for (int i = 0; i < len; i++) { + if (!isValidKeyChar(key.charAt(i))) { + return false; + } + } + + return true; + } + + /** + * Create a key in a specific namespace. + * + * @param namespace namespace + * @param key key + * @deprecated should never be used by plugins, for internal use only!! + */ + @Deprecated + public NamespacedKey(String namespace, String key) { + if (namespace == null || !isValidNamespace(namespace)) { + throw new IllegalArgumentException(String.format("Invalid namespace. Must be [a-z0-9._-]: %s", namespace)); + } else if (key == null || !isValidKey(key)) { + throw new IllegalArgumentException(String.format("Invalid key. Must be [a-z0-9/._-]: %s", key)); + } + + this.namespace = namespace; + this.key = key; + + String string = toString(); + if (string.length() >= 256) { + throw new IllegalArgumentException(String.format("NamespacedKey must be less than 256 characters (%s)", string)); + } + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final NamespacedKey other = (NamespacedKey) obj; + return this.namespace.equals(other.namespace) && this.key.equals(other.key); + } + + @Override + public String toString() { + return this.namespace + ":" + this.key; + } + + /** + * Get a key in the Minecraft namespace. + * + * @param key the key to use + * @return new key in the Minecraft namespace + */ + public static NamespacedKey minecraft(String key) { + return new NamespacedKey(MINECRAFT, key); + } + + + /** + * Get a NamespacedKey from the supplied string. + *

+ * The default namespace will be Minecraft's (i.e. {@link #minecraft(String)}). + * + * @return the created NamespacedKey. null if invalid + */ + public static NamespacedKey fromString(@NotNull String string) { + if (string == null || string.isEmpty()) { + throw new IllegalArgumentException("Input string must not be empty or null"); + } + + String[] components = string.split(":", 3); + if (components.length > 2) { + return null; + } + + String key = (components.length == 2) ? components[1] : ""; + if (components.length == 1) { + String value = components[0]; + if (value.isEmpty() || !isValidKey(value)) { + return null; + } + + return minecraft(value); + } else if (components.length == 2 && !isValidKey(key)) { + return null; + } + + String namespace = components[0]; + if (namespace.isEmpty()) { + return minecraft(key); + } + + if (!isValidNamespace(namespace)) { + return null; + } + + return new NamespacedKey(namespace, key); + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/SimpleCache.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/SimpleCache.java new file mode 100644 index 000000000..d51221387 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/SimpleCache.java @@ -0,0 +1,32 @@ +package dev.imprex.orebfuscator.util; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Simple cache implementation that removes the oldest element once a certain size is reached + */ +public class SimpleCache extends LinkedHashMap { + + private static final long serialVersionUID = -2732738355560313649L; + + private final int maximumSize; + private final Consumer> remove; + + public SimpleCache(int maximumSize, Consumer> remove) { + super(16, 0.75f, true); + + this.maximumSize = maximumSize; + this.remove = remove; + } + + @Override + protected boolean removeEldestEntry(Map.Entry entry) { + if (this.size() > this.maximumSize) { + this.remove.accept(entry); + return true; + } + return false; + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/Version.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/Version.java new file mode 100644 index 000000000..6a0b4b886 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/Version.java @@ -0,0 +1,120 @@ +package dev.imprex.orebfuscator.util; + +import java.io.IOException; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +public record Version(int major, int minor, int patch) implements Comparable { + + private static final Pattern VERSION_PATTERN = + Pattern.compile("(?\\d+)(?:\\.(?\\d+))?(?:\\.(?\\d+))?"); + + public static Version parse(String version) { + return tryParse(version) + .orElseThrow(() -> new IllegalArgumentException("Can't parse version: " + version)); + } + + public static Optional tryParse(String version) { + Matcher matcher = VERSION_PATTERN.matcher(version); + + if (!matcher.find()) { + return Optional.empty(); + } + + int major = Integer.parseInt(matcher.group("major")); + + String minorGroup = matcher.group("minor"); + int minor = minorGroup != null ? Integer.parseInt(minorGroup) : 0; + + String patchGroup = matcher.group("patch"); + int patch = patchGroup != null ? Integer.parseInt(patchGroup) : 0; + + return Optional.of(new Version(major, minor, patch)); + } + + public boolean isAbove(String version) { + return this.isAbove(Version.parse(version)); + } + + public boolean isAbove(Version version) { + return this.compareTo(version) > 0; + } + + public boolean isAtOrAbove(String version) { + return this.isAtOrAbove(Version.parse(version)); + } + + public boolean isAtOrAbove(Version version) { + return this.compareTo(version) >= 0; + } + + public boolean isAtOrBelow(String version) { + return this.isAtOrBelow(Version.parse(version)); + } + + public boolean isAtOrBelow(Version version) { + return this.compareTo(version) <= 0; + } + + public boolean isBelow(String version) { + return this.isBelow(Version.parse(version)); + } + + public boolean isBelow(Version version) { + return this.compareTo(version) < 0; + } + + @Override + public int compareTo(Version other) { + int major = Integer.compare(this.major, other.major); + if (major != 0) { + return major; + } + + int minor = Integer.compare(this.minor, other.minor); + if (minor != 0) { + return minor; + } + + return Integer.compare(this.patch, other.patch); + } + + @Override + public int hashCode() { + return Objects.hash(major, minor, patch); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof Version other)) { + return false; + } + return major == other.major && minor == other.minor && patch == other.patch; + } + + @Override + public String toString() { + return String.format("%s.%s.%s", this.major, this.minor, this.patch); + } + + public static final class Json extends TypeAdapter { + + @Override + public void write(JsonWriter out, Version value) throws IOException { + out.value(value.toString()); + } + + @Override + public Version read(JsonReader in) throws IOException { + return Version.parse(in.nextString()); + } + } +} diff --git a/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/WeightedRandom.java b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/WeightedRandom.java new file mode 100644 index 000000000..ac40fd346 --- /dev/null +++ b/orebfuscator-core/src/main/java/dev/imprex/orebfuscator/util/WeightedRandom.java @@ -0,0 +1,208 @@ +package dev.imprex.orebfuscator.util; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; +import java.util.random.RandomGenerator; +import org.jetbrains.annotations.NotNull; + +/** + * Weighted random integer sampler using the + * alias method. + *

+ * This data structure allows efficient sampling of integer values according to arbitrary (positive) weights: + *

    + *
  • Preprocessing/build time: {@code O(n)}
  • + *
  • Sampling time: {@code O(1)}
  • + *
  • Memory usage: {@code O(n)}
  • + *
+ *

+ * Usage example: + *

{@code
+ * WeightedRandom wr = WeightedRandom.builder()
+ *     .add(1, 0.5)   // value 1 with weight 0.5
+ *     .add(2, 1.5)   // value 2 with weight 1.5
+ *     .add(3, 3.0)   // value 3 with weight 3.0
+ *     .build();
+ *
+ * int sample = wr.next(); // sample ~10% chance of 1, 30% of 2, 60% of 3
+ * }
+ * + *

+ * This implementation is immutable and thread-safe for concurrent calls to + * {@link #next()} after construction. + */ +public final class WeightedRandom { + + /** + * Creates a new builder for constructing a {@link WeightedRandom}. + * + * @return a new {@link Builder} instance + */ + public static Builder builder() { + return new Builder(); + } + + private final int n; + private final double totalWeight; + private final boolean allWeightsEqual; + + private final int[] values; + private final double[] probabilities; + private final int[] alias; + + /** + * Constructs a weighted random sampler from the given builder. Implements the alias method preprocessing algorithm. + * + * @param builder the builder containing values and weights + */ + private WeightedRandom(@NotNull Builder builder) { + double minWeight = Double.POSITIVE_INFINITY; + double maxWeight = Double.NEGATIVE_INFINITY; + double totalWeight = 0d; + + // implementation from: https://www.keithschwarz.com/darts-dice-coins/ + // STEP 1 + this.n = builder.entries.size(); + this.values = new int[n]; + this.probabilities = new double[n]; + this.alias = new int[n]; + + // STEP 2 + double[] scaled = new double[n]; + Deque small = new ArrayDeque<>(); + Deque large = new ArrayDeque<>(); + + // STEP 3 + int index = 0; + for (var entry : builder.entries.entrySet()) { + values[index] = entry.getKey(); + + double weight = entry.getValue(); + minWeight = Math.min(minWeight, weight); + maxWeight = Math.max(maxWeight, weight); + totalWeight += weight; + + scaled[index++] = weight * n; + } + + this.totalWeight = totalWeight; + + // treat near-equal weights as equal + double span = maxWeight - minWeight; + if (span <= Math.ulp(maxWeight) * 4 || span <= 1e-12 * Math.max(1.0, maxWeight)) { + this.allWeightsEqual = true; + return; + } + this.allWeightsEqual = false; + + // STEP 4 + for (int i = 0; i < n; i++) { + if (scaled[i] < totalWeight) { + small.addLast(i); + } else { + large.addLast(i); + } + } + + // STEP 5 + while (!small.isEmpty() && !large.isEmpty()) { + int l = small.removeLast(); + int g = large.removeLast(); + probabilities[l] = scaled[l]; + alias[l] = g; + scaled[g] = scaled[g] + scaled[l] - totalWeight; + if (scaled[g] < totalWeight) { + small.addLast(g); + } else { + large.addLast(g); + } + } + + // STEP 6 + while (!large.isEmpty()) { + probabilities[large.removeLast()] = totalWeight; + } + + // STEP 7 + while (!small.isEmpty()) { + probabilities[small.removeLast()] = totalWeight; + } + } + + /** + * Samples a random value using {@link ThreadLocalRandom}. + * + * @return a sampled integer according to the configured weights + */ + public int next() { + return next(ThreadLocalRandom.current()); + } + + /** + * Samples a random value using the given random generator. + * + * @param random a {@link RandomGenerator} to use for randomness + * @return a sampled integer according to the configured weights + */ + public int next(@NotNull RandomGenerator random) { + Objects.requireNonNull(random); + + int i = random.nextInt(this.n); + if (this.allWeightsEqual) { + return values[i]; + } + + int pick = random.nextDouble(totalWeight) < probabilities[i] ? i : alias[i]; + return values[pick]; + } + + /** + * Builder for {@link WeightedRandom}. + *

+ * Values are unique; adding the same value multiple times will merge and sum their weights. + */ + public static class Builder { + + private final Map entries = new LinkedHashMap<>(); + + private Builder() { + } + + /** + * Adds a value with the given weight to the distribution. + *

+ * If the value already exists, its weight is increased by the given amount. + * + * @param value the integer value to add + * @param weight the weight associated with this value (must be positive and finite) + * @return this builder for chaining + * @throws IllegalArgumentException if {@code weight <= 0} or not finite + */ + public Builder add(int value, double weight) { + if (weight <= 0.0d || !Double.isFinite(weight)) { + throw new IllegalArgumentException("Weight has to be greater zero and finite!"); + } + + this.entries.merge(value, weight, (a, b) -> a + b); + return this; + } + + /** + * Builds a {@link WeightedRandom} from the current set of values and weights. + * + * @return a new {@link WeightedRandom} instance + * @throws IllegalStateException if no values have been added + */ + public WeightedRandom build() { + if (this.entries.isEmpty()) { + throw new IllegalStateException("No entries added!"); + } + + return new WeightedRandom(this); + } + } +} diff --git a/orebfuscator-plugin/src/main/resources/config/config-1.16.yml b/orebfuscator-core/src/main/resources/config/config-1.16.0.yml similarity index 57% rename from orebfuscator-plugin/src/main/resources/config/config-1.16.yml rename to orebfuscator-core/src/main/resources/config/config-1.16.0.yml index aca8f0ea5..648d6ef6d 100644 --- a/orebfuscator-plugin/src/main/resources/config/config-1.16.yml +++ b/orebfuscator-core/src/main/resources/config/config-1.16.0.yml @@ -1,4 +1,4 @@ -version: 4 +version: 5 general: checkForUpdates: true updateOnBlockDamage: true @@ -36,41 +36,27 @@ obfuscation: - world layerObfuscation: false hiddenBlocks: - - minecraft:chest - - minecraft:ender_chest - - minecraft:trapped_chest - - minecraft:spawner - - minecraft:emerald_ore + - tag(minecraft:base_stone_overworld) + - minecraft:bedrock + - minecraft:coal_ore - minecraft:diamond_ore + - minecraft:emerald_ore - minecraft:gold_ore - minecraft:iron_ore - - minecraft:coal_ore - minecraft:lapis_ore - minecraft:redstone_ore - - minecraft:stone - - minecraft:clay - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:bedrock + - tag(minecraft:shulker_boxes) + - minecraft:barrel + - minecraft:chest + - minecraft:dispenser + - minecraft:dropper + - minecraft:ender_chest + - minecraft:hopper + - minecraft:trapped_chest randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:cave_air: 5 minecraft:cobblestone: 1 @@ -97,39 +83,24 @@ obfuscation: - world_nether layerObfuscation: false hiddenBlocks: + - tag(minecraft:base_stone_nether) + - tag(minecraft:soul_fire_base_blocks) + - minecraft:ancient_debris + - minecraft:bedrock + - minecraft:nether_gold_ore + - minecraft:nether_quartz_ore + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:spawner - - minecraft:netherrack - - minecraft:nether_quartz_ore - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:ancient_debris - - minecraft:nether_gold_ore - - minecraft:basalt - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:bedrock randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 4 minecraft:nether_quartz_ore: 1 @@ -153,31 +124,19 @@ obfuscation: - world_the_end layerObfuscation: false hiddenBlocks: + - minecraft:end_stone + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:spawner - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 minecraft:end_stone_bricks: 1 @@ -187,190 +146,155 @@ obfuscation: proximity: proximity-overworld: enabled: true - minY: 0 + minY: -2032 maxY: 2031 worlds: - world distance: 24 frustumCulling: enabled: true - minDistance: 3 - fov: 80 + minDistance: 3.0 + fov: 80.0 rayCastCheck: enabled: false onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:mossy_cobblestone: {} minecraft:diamond_ore: {} minecraft:emerald_ore: {} - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + minecraft:mossy_cobblestone: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:cauldron: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:stone: 1 proximity-nether: enabled: true - minY: 0 + minY: -2032 maxY: 2031 worlds: - world_nether distance: 24 frustumCulling: enabled: true - minDistance: 3 - fov: 80 + minDistance: 3.0 + fov: 80.0 rayCastCheck: enabled: false onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + minecraft:ancient_debris: {} + minecraft:lodestone: {} + minecraft:nether_gold_ore: {} + minecraft:respawn_anchor: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:cauldron: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - minecraft:ancient_debris: {} - minecraft:nether_gold_ore: {} - minecraft:respawn_anchor: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 1 proximity-end: enabled: true - minY: 0 + minY: -2032 maxY: 2031 worlds: - world_the_end distance: 24 frustumCulling: enabled: true - minDistance: 3 - fov: 80 + minDistance: 3.0 + fov: 80.0 rayCastCheck: enabled: false onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:cauldron: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 diff --git a/orebfuscator-plugin/src/main/resources/config/config-1.20.yml b/orebfuscator-core/src/main/resources/config/config-1.17.0.yml similarity index 57% rename from orebfuscator-plugin/src/main/resources/config/config-1.20.yml rename to orebfuscator-core/src/main/resources/config/config-1.17.0.yml index 02455a163..913da5494 100644 --- a/orebfuscator-plugin/src/main/resources/config/config-1.20.yml +++ b/orebfuscator-core/src/main/resources/config/config-1.17.0.yml @@ -1,4 +1,4 @@ -version: 4 +version: 5 general: checkForUpdates: true updateOnBlockDamage: true @@ -36,48 +36,27 @@ obfuscation: - world layerObfuscation: false hiddenBlocks: - - minecraft:emerald_ore - - minecraft:deepslate_emerald_ore - - minecraft:diamond_ore - - minecraft:deepslate_diamond_ore - - minecraft:gold_ore - - minecraft:deepslate_gold_ore + - tag(minecraft:base_stone_overworld) + - tag(minecraft:coal_ores) + - tag(minecraft:copper_ores) + - tag(minecraft:diamond_ores) + - tag(minecraft:emerald_ores) + - tag(minecraft:gold_ores) + - tag(minecraft:iron_ores) + - tag(minecraft:lapis_ores) + - tag(minecraft:redstone_ores) + - minecraft:bedrock + - minecraft:raw_copper_block - minecraft:raw_gold_block - - minecraft:iron_ore - - minecraft:deepslate_iron_ore - minecraft:raw_iron_block - - minecraft:coal_ore - - minecraft:deepslate_coal_ore - - minecraft:lapis_ore - - minecraft:deepslate_lapis_ore - - minecraft:redstone_ore - - minecraft:deepslate_redstone_ore - - minecraft:copper_ore - - minecraft:raw_copper_block - - minecraft:deepslate_copper_ore - - minecraft:stone - - minecraft:deepslate + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:bedrock randomBlocks: section-global: minY: -2032 @@ -114,8 +93,8 @@ obfuscation: minecraft:deepslate_lapis_ore: 5 minecraft:deepslate_redstone_ore: 5 section-bedrock: - minY: -64 - maxY: -60 + minY: 0 + maxY: 5 blocks: minecraft:bedrock: 20 obfuscation-nether: @@ -126,38 +105,24 @@ obfuscation: - world_nether layerObfuscation: false hiddenBlocks: - - minecraft:netherrack + - tag(minecraft:base_stone_nether) + - tag(minecraft:soul_fire_base_blocks) + - minecraft:ancient_debris + - minecraft:bedrock + - minecraft:nether_gold_ore - minecraft:nether_quartz_ore + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:ancient_debris - - minecraft:nether_gold_ore - - minecraft:basalt - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:bedrock randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 4 minecraft:nether_quartz_ore: 1 @@ -181,30 +146,19 @@ obfuscation: - world_the_end layerObfuscation: false hiddenBlocks: + - minecraft:end_stone + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 minecraft:end_stone_bricks: 1 @@ -228,50 +182,36 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: + tag(minecraft:diamond_ores): {} + tag(minecraft:emerald_ores): {} minecraft:mossy_cobblestone: {} - minecraft:diamond_ore: {} - minecraft:deepslate_diamond_ore: {} - minecraft:emerald_ore: {} - minecraft:deepslate_emerald_ore: {} - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-stone: minY: -5 @@ -299,52 +239,41 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + minecraft:ancient_debris: {} + minecraft:lodestone: {} + minecraft:nether_gold_ore: {} + minecraft:respawn_anchor: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - minecraft:ancient_debris: {} - minecraft:nether_gold_ore: {} - minecraft:respawn_anchor: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 1 proximity-end: @@ -363,48 +292,36 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 diff --git a/orebfuscator-plugin/src/main/resources/config/config-1.19.yml b/orebfuscator-core/src/main/resources/config/config-1.18.0.yml similarity index 58% rename from orebfuscator-plugin/src/main/resources/config/config-1.19.yml rename to orebfuscator-core/src/main/resources/config/config-1.18.0.yml index 02455a163..221c5b5f3 100644 --- a/orebfuscator-plugin/src/main/resources/config/config-1.19.yml +++ b/orebfuscator-core/src/main/resources/config/config-1.18.0.yml @@ -1,4 +1,4 @@ -version: 4 +version: 5 general: checkForUpdates: true updateOnBlockDamage: true @@ -36,48 +36,27 @@ obfuscation: - world layerObfuscation: false hiddenBlocks: - - minecraft:emerald_ore - - minecraft:deepslate_emerald_ore - - minecraft:diamond_ore - - minecraft:deepslate_diamond_ore - - minecraft:gold_ore - - minecraft:deepslate_gold_ore + - tag(minecraft:base_stone_overworld) + - tag(minecraft:coal_ores) + - tag(minecraft:copper_ores) + - tag(minecraft:diamond_ores) + - tag(minecraft:emerald_ores) + - tag(minecraft:gold_ores) + - tag(minecraft:iron_ores) + - tag(minecraft:lapis_ores) + - tag(minecraft:redstone_ores) + - minecraft:bedrock + - minecraft:raw_copper_block - minecraft:raw_gold_block - - minecraft:iron_ore - - minecraft:deepslate_iron_ore - minecraft:raw_iron_block - - minecraft:coal_ore - - minecraft:deepslate_coal_ore - - minecraft:lapis_ore - - minecraft:deepslate_lapis_ore - - minecraft:redstone_ore - - minecraft:deepslate_redstone_ore - - minecraft:copper_ore - - minecraft:raw_copper_block - - minecraft:deepslate_copper_ore - - minecraft:stone - - minecraft:deepslate + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:bedrock randomBlocks: section-global: minY: -2032 @@ -126,38 +105,24 @@ obfuscation: - world_nether layerObfuscation: false hiddenBlocks: - - minecraft:netherrack + - tag(minecraft:base_stone_nether) + - tag(minecraft:soul_fire_base_blocks) + - minecraft:ancient_debris + - minecraft:bedrock + - minecraft:nether_gold_ore - minecraft:nether_quartz_ore + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:ancient_debris - - minecraft:nether_gold_ore - - minecraft:basalt - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:bedrock randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 4 minecraft:nether_quartz_ore: 1 @@ -181,30 +146,19 @@ obfuscation: - world_the_end layerObfuscation: false hiddenBlocks: + - minecraft:end_stone + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 minecraft:end_stone_bricks: 1 @@ -228,50 +182,36 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: + tag(minecraft:diamond_ores): {} + tag(minecraft:emerald_ores): {} minecraft:mossy_cobblestone: {} - minecraft:diamond_ore: {} - minecraft:deepslate_diamond_ore: {} - minecraft:emerald_ore: {} - minecraft:deepslate_emerald_ore: {} - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-stone: minY: -5 @@ -299,52 +239,41 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + minecraft:ancient_debris: {} + minecraft:lodestone: {} + minecraft:nether_gold_ore: {} + minecraft:respawn_anchor: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - minecraft:ancient_debris: {} - minecraft:nether_gold_ore: {} - minecraft:respawn_anchor: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 1 proximity-end: @@ -363,48 +292,36 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 diff --git a/orebfuscator-plugin/src/main/resources/config/config-1.21.yml b/orebfuscator-core/src/main/resources/config/config-1.20.3.yml similarity index 58% rename from orebfuscator-plugin/src/main/resources/config/config-1.21.yml rename to orebfuscator-core/src/main/resources/config/config-1.20.3.yml index a66c3002b..b00acd8a6 100644 --- a/orebfuscator-plugin/src/main/resources/config/config-1.21.yml +++ b/orebfuscator-core/src/main/resources/config/config-1.20.3.yml @@ -1,4 +1,4 @@ -version: 4 +version: 5 general: checkForUpdates: true updateOnBlockDamage: true @@ -36,48 +36,28 @@ obfuscation: - world layerObfuscation: false hiddenBlocks: - - minecraft:emerald_ore - - minecraft:deepslate_emerald_ore - - minecraft:diamond_ore - - minecraft:deepslate_diamond_ore - - minecraft:gold_ore - - minecraft:deepslate_gold_ore + - tag(minecraft:base_stone_overworld) + - tag(minecraft:coal_ores) + - tag(minecraft:copper_ores) + - tag(minecraft:diamond_ores) + - tag(minecraft:emerald_ores) + - tag(minecraft:gold_ores) + - tag(minecraft:iron_ores) + - tag(minecraft:lapis_ores) + - tag(minecraft:redstone_ores) + - minecraft:bedrock + - minecraft:raw_copper_block - minecraft:raw_gold_block - - minecraft:iron_ore - - minecraft:deepslate_iron_ore - minecraft:raw_iron_block - - minecraft:coal_ore - - minecraft:deepslate_coal_ore - - minecraft:lapis_ore - - minecraft:deepslate_lapis_ore - - minecraft:redstone_ore - - minecraft:deepslate_redstone_ore - - minecraft:copper_ore - - minecraft:raw_copper_block - - minecraft:deepslate_copper_ore - - minecraft:stone - - minecraft:deepslate + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:crafter + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:bedrock randomBlocks: section-global: minY: -2032 @@ -126,38 +106,25 @@ obfuscation: - world_nether layerObfuscation: false hiddenBlocks: - - minecraft:netherrack + - tag(minecraft:base_stone_nether) + - tag(minecraft:soul_fire_base_blocks) + - minecraft:ancient_debris + - minecraft:bedrock + - minecraft:nether_gold_ore - minecraft:nether_quartz_ore + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:crafter + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:ancient_debris - - minecraft:nether_gold_ore - - minecraft:basalt - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:bedrock randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 4 minecraft:nether_quartz_ore: 1 @@ -181,30 +148,20 @@ obfuscation: - world_the_end layerObfuscation: false hiddenBlocks: + - minecraft:end_stone + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:crafter + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 minecraft:end_stone_bricks: 1 @@ -228,51 +185,37 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: + tag(minecraft:diamond_ores): {} + tag(minecraft:emerald_ores): {} minecraft:mossy_cobblestone: {} - minecraft:diamond_ore: {} - minecraft:deepslate_diamond_ore: {} - minecraft:emerald_ore: {} - minecraft:deepslate_emerald_ore: {} - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafter: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:crafter: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-stone: minY: -5 @@ -300,53 +243,42 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafter: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + minecraft:ancient_debris: {} + minecraft:lodestone: {} + minecraft:nether_gold_ore: {} + minecraft:respawn_anchor: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - minecraft:ancient_debris: {} - minecraft:nether_gold_ore: {} - minecraft:respawn_anchor: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:crafter: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 1 proximity-end: @@ -365,49 +297,37 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafter: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:crafter: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 diff --git a/orebfuscator-plugin/src/main/resources/config/config-1.18.yml b/orebfuscator-core/src/main/resources/config/config-1.21.9.yml similarity index 58% rename from orebfuscator-plugin/src/main/resources/config/config-1.18.yml rename to orebfuscator-core/src/main/resources/config/config-1.21.9.yml index 02455a163..de013be60 100644 --- a/orebfuscator-plugin/src/main/resources/config/config-1.18.yml +++ b/orebfuscator-core/src/main/resources/config/config-1.21.9.yml @@ -1,4 +1,4 @@ -version: 4 +version: 5 general: checkForUpdates: true updateOnBlockDamage: true @@ -36,48 +36,29 @@ obfuscation: - world layerObfuscation: false hiddenBlocks: - - minecraft:emerald_ore - - minecraft:deepslate_emerald_ore - - minecraft:diamond_ore - - minecraft:deepslate_diamond_ore - - minecraft:gold_ore - - minecraft:deepslate_gold_ore + - tag(minecraft:base_stone_overworld) + - tag(minecraft:coal_ores) + - tag(minecraft:copper_ores) + - tag(minecraft:diamond_ores) + - tag(minecraft:emerald_ores) + - tag(minecraft:gold_ores) + - tag(minecraft:iron_ores) + - tag(minecraft:lapis_ores) + - tag(minecraft:redstone_ores) + - minecraft:bedrock + - minecraft:raw_copper_block - minecraft:raw_gold_block - - minecraft:iron_ore - - minecraft:deepslate_iron_ore - minecraft:raw_iron_block - - minecraft:coal_ore - - minecraft:deepslate_coal_ore - - minecraft:lapis_ore - - minecraft:deepslate_lapis_ore - - minecraft:redstone_ore - - minecraft:deepslate_redstone_ore - - minecraft:copper_ore - - minecraft:raw_copper_block - - minecraft:deepslate_copper_ore - - minecraft:stone - - minecraft:deepslate + - tag(minecraft:copper_chests) + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:crafter + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:bedrock randomBlocks: section-global: minY: -2032 @@ -126,38 +107,26 @@ obfuscation: - world_nether layerObfuscation: false hiddenBlocks: - - minecraft:netherrack + - tag(minecraft:base_stone_nether) + - tag(minecraft:soul_fire_base_blocks) + - minecraft:ancient_debris + - minecraft:bedrock + - minecraft:nether_gold_ore - minecraft:nether_quartz_ore + - tag(minecraft:copper_chests) + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:crafter + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:ancient_debris - - minecraft:nether_gold_ore - - minecraft:basalt - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:bedrock randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 4 minecraft:nether_quartz_ore: 1 @@ -181,30 +150,21 @@ obfuscation: - world_the_end layerObfuscation: false hiddenBlocks: + - minecraft:end_stone + - tag(minecraft:copper_chests) + - tag(minecraft:shulker_boxes) + - minecraft:barrel - minecraft:chest + - minecraft:crafter + - minecraft:dispenser + - minecraft:dropper - minecraft:ender_chest + - minecraft:hopper - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 minecraft:end_stone_bricks: 1 @@ -228,50 +188,38 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: + tag(minecraft:diamond_ores): {} + tag(minecraft:emerald_ores): {} minecraft:mossy_cobblestone: {} - minecraft:diamond_ore: {} - minecraft:deepslate_diamond_ore: {} - minecraft:emerald_ore: {} - minecraft:deepslate_emerald_ore: {} - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:copper_chests): {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:crafter: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-stone: minY: -5 @@ -299,52 +247,43 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + minecraft:ancient_debris: {} + minecraft:lodestone: {} + minecraft:nether_gold_ore: {} + minecraft:respawn_anchor: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - minecraft:ancient_debris: {} - minecraft:nether_gold_ore: {} - minecraft:respawn_anchor: {} + tag(minecraft:copper_chests): {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:crafter: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:netherrack: 1 proximity-end: @@ -363,48 +302,38 @@ proximity: onlyCheckCenter: false useBlockBelow: true hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} + tag(minecraft:anvil): {} + tag(minecraft:beehives): {} + tag(minecraft:beds): {} + tag(minecraft:cauldrons): {} minecraft:blast_furnace: {} + minecraft:brewing_stand: {} minecraft:cartography_table: {} + minecraft:composter: {} + minecraft:crafting_table: {} + minecraft:enchanting_table: {} minecraft:fletching_table: {} + minecraft:furnace: {} minecraft:grindstone: {} - minecraft:composter: {} minecraft:lectern: {} minecraft:loom: {} minecraft:smithing_table: {} minecraft:smoker: {} minecraft:stonecutter: {} - minecraft:hopper: {} minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} + tag(minecraft:copper_chests): {} + tag(minecraft:shulker_boxes): {} + minecraft:barrel: {} + minecraft:chest: {} + minecraft:crafter: {} + minecraft:dispenser: {} + minecraft:dropper: {} + minecraft:ender_chest: {} + minecraft:hopper: {} + minecraft:trapped_chest: {} randomBlocks: section-global: minY: -2032 - maxY: 2032 + maxY: 2031 blocks: minecraft:end_stone: 1 diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/DummyException.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/DummyException.java new file mode 100644 index 000000000..5fbfb7afc --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/DummyException.java @@ -0,0 +1,27 @@ +package dev.imprex.orebfuscator; + +/** + * DummyException is a singleton exception used in tests to ensure a consistent stack trace. The stack trace is fixed + * and shouldn't be changed, even if the class is modified. + */ +public class DummyException extends Exception { + + /** + * The single instance of DummyException used across all tests. + */ + public static final DummyException INSTANCE = new DummyException(); + + /** + * Private constructor to prevent additional instances. The stack trace is fixed and the message is set to "dummy + * exception" to maintain consistency in tests. + */ + private DummyException() { + super("dummy exception"); + + // Set a fixed stack trace + this.setStackTrace(new StackTraceElement[]{ + new StackTraceElement("dev.imprex.orebfuscator.DummyException", "", "DummyException.java", 26), + new StackTraceElement("dev.imprex.orebfuscator.DummyException", "", "DummyException.java", 18) + }); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/ProximityHeightConditionTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/ProximityHeightConditionTest.java new file mode 100644 index 000000000..25012d37b --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/ProximityHeightConditionTest.java @@ -0,0 +1,51 @@ +package dev.imprex.orebfuscator.config; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +public class ProximityHeightConditionTest { + + private static final int TEST_MIN = ProximityHeightCondition.clampY(-0xFFF); + private static final int TEST_MAX = ProximityHeightCondition.clampY(0xFFF); + + @Test + public void testCreateRemove() { + final int minY = -10; + final int maxY = 10; + + int flag = 0b101; + assertFalse(ProximityHeightCondition.isPresent(flag)); + + flag |= ProximityHeightCondition.create(minY, maxY); + assertTrue(ProximityHeightCondition.isPresent(flag)); + assertEquals(minY, ProximityHeightCondition.getMinY(flag)); + assertEquals(maxY, ProximityHeightCondition.getMaxY(flag)); + + for (int y = TEST_MIN; y <= TEST_MAX; y++) { + boolean expected = minY <= y && maxY >= y; + assertEquals(expected, ProximityHeightCondition.match(flag, y), "failed for " + y); + } + + int other = ProximityHeightCondition.create(minY, maxY); + assertTrue(ProximityHeightCondition.equals(flag, other)); + + flag = ProximityHeightCondition.remove(flag); + assertEquals(0b101, flag); + } + + @Test + public void testReadWrite() { + for (int minY = TEST_MIN; minY <= TEST_MAX; minY++) { + for (int maxY = minY; maxY <= TEST_MAX; maxY++) { + int flag = ProximityHeightCondition.create(minY, maxY); + + assertTrue(ProximityHeightCondition.isPresent(flag)); + assertEquals(minY, ProximityHeightCondition.getMinY(flag)); + assertEquals(maxY, ProximityHeightCondition.getMaxY(flag)); + } + } + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/context/ConfigMessageTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/context/ConfigMessageTest.java new file mode 100644 index 000000000..132580796 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/context/ConfigMessageTest.java @@ -0,0 +1,20 @@ +package dev.imprex.orebfuscator.config.context; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +class ConfigMessageTest { + + @Test + void testFixedMessageFormat() { + String formatted = ConfigMessage.MISSING_OR_EMPTY.format("ignored"); + assertEquals("is missing or empty", formatted); + } + + @Test + void testDynamicMessageFormat() { + String formatted = ConfigMessage.VALUE_MIN.format(10, 5); + assertEquals("value too low {value(10) < min(5)}", formatted); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/context/DefaultConfigParsingContextTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/context/DefaultConfigParsingContextTest.java new file mode 100644 index 000000000..b02cfe9c3 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/context/DefaultConfigParsingContextTest.java @@ -0,0 +1,159 @@ +package dev.imprex.orebfuscator.config.context; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +class DefaultConfigParsingContextTest { + + @Test + void testNoErrorsNoWarnings() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + + assertFalse(context.hasErrors()); + assertNull(context.report()); + } + + @Test + void testWarnDoesNotSetHasErrors() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + context.warn(ConfigMessage.MISSING_OR_EMPTY); + + assertFalse(context.hasErrors()); + assertNotNull(context.report()); + } + + @Test + void testErrorSetsHasErrors() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + context.error(ConfigMessage.MISSING_OR_EMPTY); + + assertTrue(context.hasErrors()); + assertNotNull(context.report()); + } + + @Test + void testErrorMinValue() { + // value below min + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + context.errorMinValue("value", 10, 5); + assertTrue(context.hasErrors()); + + // value equals min + DefaultConfigParsingContext context2 = new DefaultConfigParsingContext(); + context2.errorMinValue("value", 10, 10); + assertFalse(context2.hasErrors()); + + // value above min + DefaultConfigParsingContext context3 = new DefaultConfigParsingContext(); + context3.errorMinValue("value", 10, 15); + assertFalse(context3.hasErrors()); + } + + @Test + void testErrorMinMaxValue() { + // value below allowed range + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + context.errorMinMaxValue("value", 5, 10, 3); + assertTrue(context.hasErrors()); + + // value within range (min-bound) + DefaultConfigParsingContext context2 = new DefaultConfigParsingContext(); + context2.errorMinMaxValue("value", 5, 10, 5); + assertFalse(context2.hasErrors()); + + // value within range + DefaultConfigParsingContext context3 = new DefaultConfigParsingContext(); + context3.errorMinMaxValue("value", 5, 10, 7); + assertFalse(context3.hasErrors()); + + // value within range (max-bound) + DefaultConfigParsingContext context4 = new DefaultConfigParsingContext(); + context4.errorMinMaxValue("value", 5, 10, 10); + assertFalse(context4.hasErrors()); + + // value above allowed range + DefaultConfigParsingContext context5 = new DefaultConfigParsingContext(); + context5.errorMinMaxValue("value", 5, 10, 15); + assertTrue(context5.hasErrors()); + } + + @Test + void testNestedChildContextNonIsolated() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + context.section("child").error(ConfigMessage.MISSING_OR_EMPTY); + assertTrue(context.hasErrors()); + + DefaultConfigParsingContext context2 = new DefaultConfigParsingContext(); + context2.error("child", ConfigMessage.MISSING_OR_EMPTY); + assertTrue(context2.hasErrors()); + } + + @Test + void testNestedChildContextIsolated() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + ConfigParsingContext child = context.section("child", true); + + child.error(ConfigMessage.MISSING_OR_EMPTY); + context.error("child.b", ConfigMessage.MISSING_OR_EMPTY); + + assertFalse(context.hasErrors()); + assertTrue(child.hasErrors()); + } + + @Test + void testSectionPathValidation() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + // Paths with blank (non-empty) segments (e.g. "\t" or "a. .b") should throw an exception. + assertThrows(IllegalArgumentException.class, () -> context.section("\t")); + assertThrows(IllegalArgumentException.class, () -> context.section("a. .b")); + } + + @Test + void testDisableIfError() { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + assertTrue(context.disableIfError(true)); + assertFalse(context.disableIfError(false)); + + context.error("child", ConfigMessage.MISSING_OR_EMPTY); + assertFalse(context.disableIfError(true)); + assertFalse(context.disableIfError(false)); + + // test if error isolation also works for disableIfError + ConfigParsingContext child = context.section("child", true); + assertTrue(context.disableIfError(true)); + assertFalse(child.disableIfError(true)); + } + + @Test + void testReportWithNestedMessages() throws IOException { + DefaultConfigParsingContext context = new DefaultConfigParsingContext(); + ConfigParsingContext child = context.section("child", true); + + // test all variations of warn + context.warn(ConfigMessage.MISSING_OR_EMPTY); + context.warn("a.b.c", ConfigMessage.MISSING_OR_EMPTY); + child.warn(ConfigMessage.MISSING_OR_EMPTY); + context.warn("child.a", ConfigMessage.MISSING_OR_EMPTY); + + // test all variations of error + context.error(ConfigMessage.MISSING_OR_EMPTY); + context.error("a.b.c", ConfigMessage.MISSING_OR_EMPTY); + child.error(ConfigMessage.MISSING_OR_EMPTY); + context.error("child.a", ConfigMessage.MISSING_OR_EMPTY); + + assertFalse(child.disableIfError(true)); + + String expected = Files.readString(Paths.get("src/test/resources/config/context-test-report.txt")); + assertEquals(expected, context.report()); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/yaml/ConfigurationSectionTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/yaml/ConfigurationSectionTest.java new file mode 100644 index 000000000..9267e220b --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/yaml/ConfigurationSectionTest.java @@ -0,0 +1,471 @@ +package dev.imprex.orebfuscator.config.yaml; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +class ConfigurationSectionTest { + + @Test + void testGetName() { + ConfigurationSection section = new ConfigurationSection(""); + assertEquals("", section.getName()); + + ConfigurationSection child = section.createSection("child"); + assertEquals("child", child.getName()); + } + + @Test + void testGetKeys() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("a", 0); + section.set("b.a", 0); + section.set("b.b", 0); + section.set("b.b.a", 0); + + assertArrayEquals(new String[]{"a", "b"}, section.getKeys().toArray()); + } + + @Test + void testContains() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("a", 0); + section.set("b.c", 0); + section.set("c", null); + + assertTrue(section.contains("a")); + assertTrue(section.contains("b")); + assertFalse(section.contains("c")); + + assertFalse(section.contains("b.a")); + assertFalse(section.contains("b.b")); + assertTrue(section.contains("b.c")); + } + + @Test + void testSet() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("boolean", true); + + assertTrue(section.contains("boolean")); + assertEquals(true, section.get("boolean")); + + section.set("boolean", null); + + assertFalse(section.contains("boolean")); + assertNull(section.get("boolean")); + } + + @Test + void testSetNullDoesNotCreateSubSection() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("int.32", null); + + assertFalse(section.contains("int")); + assertNull(section.get("int")); + } + + @Test + void testGet() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("boolean", true); + section.set("int.32", Integer.MAX_VALUE); + section.set("int.64", Long.MAX_VALUE); + + assertEquals(section, section.get("")); + assertEquals(true, section.get("boolean")); + assertEquals(Integer.MAX_VALUE, section.get("int.32")); + assertEquals(Long.MAX_VALUE, section.get("int.64")); + } + + @Test + void testGetWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("boolean", true); + assertEquals(true, section.get("boolean")); + assertEquals(true, section.get("boolean", false)); + + assertNull(section.get("string")); + assertEquals("foo", section.get("string", "foo")); + + assertNull(section.get("int.32")); + assertEquals(Integer.MAX_VALUE, section.get("int.32", Integer.MAX_VALUE)); + } + + @Test + void testCreateSection() { + ConfigurationSection section = new ConfigurationSection(""); + + assertEquals(section, section.createSection("")); + assertEquals(section.createSection("should.be.same.instance"), section.createSection("should.be.same.instance")); + + assertNotNull(section.createSection("int")); + assertNotNull(section.createSection("foo.bar")); + assertNotNull(section.createSection("foo.baz")); + } + + @Test + void testIsBoolean() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("boolean", true); + section.set("booleanObject", Boolean.TRUE); + section.set("other", 1); + + assertTrue(section.isBoolean("boolean")); + assertTrue(section.isBoolean("booleanObject")); + assertFalse(section.isBoolean("other")); + assertFalse(section.isBoolean("unknown")); + } + + @Test + void testGetBoolean() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("boolean", true); + section.set("booleanObject", Boolean.TRUE); + section.set("other", 1); + + assertEquals(true, section.getBoolean("boolean")); + assertEquals(true, section.getBoolean("booleanObject")); + assertNull(section.getBoolean("other")); + assertNull(section.getBoolean("unknown")); + } + + @Test + void testGetBooleanWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("boolean", true); + section.set("booleanObject", Boolean.TRUE); + section.set("other", 1); + + assertEquals(true, section.getBoolean("boolean", false)); + assertEquals(true, section.getBoolean("booleanObject", false)); + assertEquals(false, section.getBoolean("other", false)); + assertEquals(false, section.getBoolean("unknown", false)); + } + + @Test + void testIsInt() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("int", 1); + section.set("intObject", Integer.valueOf(1)); + section.set("number", 1f); + section.set("other", true); + + assertTrue(section.isInt("int")); + assertTrue(section.isInt("intObject")); + assertFalse(section.isInt("number")); + assertFalse(section.isInt("other")); + assertFalse(section.isInt("unknown")); + } + + @Test + void testGetInt() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("int", 1); + section.set("intObject", Integer.valueOf(1)); + section.set("number", 1f); + section.set("other", true); + + assertEquals(1, section.getInt("int")); + assertEquals(1, section.getInt("intObject")); + assertEquals(1, section.getInt("number")); + assertNull(section.getInt("other")); + assertNull(section.getInt("unknown")); + } + + @Test + void testGetIntWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("int", 1); + section.set("intObject", Integer.valueOf(1)); + section.set("number", 1f); + section.set("other", true); + + assertEquals(1, section.getInt("int", -1)); + assertEquals(1, section.getInt("intObject", -1)); + assertEquals(1, section.getInt("number", -1)); + assertEquals(-1, section.getInt("other", -1)); + assertEquals(-1, section.getInt("unknown", -1)); + } + + @Test + void testIsLong() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("long", 1L); + section.set("longObject", Long.valueOf(1L)); + section.set("number", 1); + section.set("other", true); + + assertTrue(section.isLong("long")); + assertTrue(section.isLong("longObject")); + assertFalse(section.isLong("number")); + assertFalse(section.isLong("other")); + assertFalse(section.isLong("unknown")); + } + + @Test + void testGetLong() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("long", 1L); + section.set("longObject", Long.valueOf(1L)); + section.set("number", 1); + section.set("other", true); + + assertEquals(1L, section.getLong("long")); + assertEquals(1L, section.getLong("longObject")); + assertEquals(1L, section.getLong("number")); + assertNull(section.getLong("other")); + assertNull(section.getLong("unknown")); + } + + @Test + void testGetLongWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("long", 1L); + section.set("longObject", Long.valueOf(1L)); + section.set("number", 1); + section.set("other", true); + + assertEquals(1L, section.getLong("long", -1L)); + assertEquals(1L, section.getLong("longObject", -1L)); + assertEquals(1L, section.getLong("number", -1L)); + assertEquals(-1L, section.getLong("other", -1L)); + assertEquals(-1L, section.getLong("unknown", -1L)); + } + + @Test + void testIsDouble() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("double", 1d); + section.set("doubleObject", Double.valueOf(1d)); + section.set("number", 1); + section.set("other", true); + + assertTrue(section.isDouble("double")); + assertTrue(section.isDouble("doubleObject")); + assertFalse(section.isDouble("number")); + assertFalse(section.isDouble("other")); + assertFalse(section.isDouble("unknown")); + } + + @Test + void testGetDouble() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("double", 1d); + section.set("doubleObject", Double.valueOf(1d)); + section.set("number", 1); + section.set("other", true); + + assertEquals(1d, section.getDouble("double")); + assertEquals(1d, section.getDouble("doubleObject")); + assertEquals(1d, section.getDouble("number")); + assertNull(section.getDouble("other")); + assertNull(section.getDouble("unknown")); + } + + @Test + void testGetDoubleWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("double", 1d); + section.set("doubleObject", Double.valueOf(1d)); + section.set("number", 1); + section.set("other", true); + + assertEquals(1d, section.getDouble("double", -1d)); + assertEquals(1d, section.getDouble("doubleObject", -1d)); + assertEquals(1d, section.getDouble("number", -1d)); + assertEquals(-1d, section.getDouble("other", -1d)); + assertEquals(-1d, section.getDouble("unknown", -1d)); + } + + @Test + void testIsNumber() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("int8", (byte) 1); + section.set("int16", (short) 1); + section.set("int32", 1); + section.set("int64", 1L); + section.set("float32", 1f); + section.set("float64", 1d); + section.set("other", true); + + assertTrue(section.isNumber("int8")); + assertTrue(section.isNumber("int16")); + assertTrue(section.isNumber("int32")); + assertTrue(section.isNumber("int64")); + assertTrue(section.isNumber("float32")); + assertTrue(section.isNumber("float64")); + assertFalse(section.isNumber("other")); + } + + @Test + void testIsString() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("string", "foo"); + section.set("number", 1); + section.set("other", true); + + assertTrue(section.isString("string")); + assertFalse(section.isString("number")); + assertFalse(section.isString("other")); + assertFalse(section.isString("unknown")); + } + + @Test + void testGetString() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("string", "foo"); + section.set("number", 1); + section.set("other", true); + + assertEquals("foo", section.getString("string")); + assertNull(section.getString("number")); + assertNull(section.getString("other")); + assertNull(section.getString("unknown")); + } + + @Test + void testGetStringWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("string", "foo"); + section.set("number", 1); + section.set("other", true); + + assertEquals("foo", section.getString("string", "bar")); + assertEquals("bar", section.getString("number", "bar")); + assertEquals("bar", section.getString("other", "bar")); + assertEquals("bar", section.getString("unknown", "bar")); + } + + @Test + void testIsList() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("list", List.of()); + section.set("other", true); + + assertTrue(section.isList("list")); + assertFalse(section.isList("other")); + assertFalse(section.isList("unknown")); + } + + @Test + void testGetList() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("list", List.of()); + section.set("other", true); + + assertEquals(List.of(), section.getList("list")); + assertNull(section.getList("other")); + assertNull(section.getList("unknown")); + } + + @Test + void testGetListWithDefaultValue() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("list", List.of()); + section.set("other", true); + + assertEquals(List.of(), section.getList("list", List.of(true))); + assertEquals(List.of(true), section.getList("other", List.of(true))); + assertEquals(List.of(true), section.getList("unknown", List.of(true))); + } + + @Test + void testGetStringList() { + ConfigurationSection section = new ConfigurationSection(""); + + section.set("list", List.of( + true, + (byte) 1, + (short) 2, + 3, + 4L, + 5.5f, + 6.6d, + 'a', + "foo", + List.of())); + section.set("other", true); + + assertEquals(List.of( + "true", + "1", + "2", + "3", + "4", + "5.5", + "6.6", + "a", + "foo"), section.getStringList("list")); + assertEquals(Collections.EMPTY_LIST, section.getStringList("other")); + assertEquals(Collections.EMPTY_LIST, section.getStringList("unknown")); + } + + @Test + void testIsSection() { + ConfigurationSection section = new ConfigurationSection(""); + + section.createSection("section"); + section.set("other", true); + + assertTrue(section.isSection("section")); + assertFalse(section.isSection("other")); + assertFalse(section.isSection("unknown")); + } + + @Test + void testGetSection() { + ConfigurationSection section = new ConfigurationSection(""); + + ConfigurationSection child = section.createSection("section"); + section.set("other", true); + + assertEquals(child, section.getSection("section")); + assertNull(section.getSection("other")); + assertNull(section.getSection("unknown")); + } + + @Test + void testGetSubSection() { + ConfigurationSection section = new ConfigurationSection(""); + + ConfigurationSection sectionA = section.createSection("sectionA"); + ConfigurationSection sectionB = section.createSection("sectionB"); + section.set("other", true); + + assertEquals(List.of(sectionA, sectionB), section.getSubSections()); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/yaml/YamlConfigurationTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/yaml/YamlConfigurationTest.java new file mode 100644 index 000000000..016494b17 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/config/yaml/YamlConfigurationTest.java @@ -0,0 +1,104 @@ +package dev.imprex.orebfuscator.config.yaml; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class YamlConfigurationTest { + + private static final String SEQUENCE_COMMENT_EXAMPLE = "#header\nlist:\n- a # inline a\n- b # inline b\n- c #inline c\n#footer\n"; + + @Test + void testSaveEmpty() throws IOException { + assertEquals("", new YamlConfiguration().withoutComments()); + } + + @Test + void testInvalidConfig() throws IOException, InvalidConfigurationException { + try (InputStream inputStream = toInputStream("\tboolean: true")) { + assertThrows(InvalidConfigurationException.class, () -> YamlConfiguration.loadConfig(inputStream)); + } + + try (InputStream inputStream = toInputStream("- a\n- b\n")) { + assertThrows(InvalidConfigurationException.class, () -> YamlConfiguration.loadConfig(inputStream)); + } + + try (InputStream inputStream = toInputStream("key.with.path: 1")) { + assertThrows(InvalidConfigurationException.class, () -> YamlConfiguration.loadConfig(inputStream)); + } + + try (InputStream inputStream = toInputStream("root: \n '': 1")) { + assertThrows(InvalidConfigurationException.class, () -> YamlConfiguration.loadConfig(inputStream)); + } + + try (InputStream inputStream = toInputStream("root: \n '\t': 1")) { + assertThrows(InvalidConfigurationException.class, () -> YamlConfiguration.loadConfig(inputStream)); + } + } + + @Test + void testSerializationRoundTrip(@TempDir Path tempDir) throws IOException, InvalidConfigurationException { + YamlConfiguration original = new YamlConfiguration(); + + original.set("boolean", true); + original.set("child.int", -1); + original.set("child.double", -1.2d); + original.set("strings.foo", "foo"); + original.set("strings.bar", "bar"); + original.set("strings.baz.qux", "qux"); + original.set("list", List.of("a", "b", "c", "d")); + + Path filePath = tempDir.resolve("test-serialization-round-trip.yml"); + original.save(filePath); + + YamlConfiguration read = YamlConfiguration.loadConfig(filePath); + assertEquals(original, read); + } + + @Test + void testCommentPreservation(@TempDir Path tempDir) throws IOException, InvalidConfigurationException { + Path expectedPath = Paths.get("src/test/resources/config/example-config.yml"); + YamlConfiguration configuration = YamlConfiguration.loadConfig(expectedPath); + + Path actualPath = tempDir.resolve("test-deserialization-round-trip.yml"); + configuration.save(actualPath); + + assertEquals(Files.readString(expectedPath), Files.readString(actualPath)); + } + + @Test + void testSequenceCommentPreservation(@TempDir Path tempDir) throws IOException, InvalidConfigurationException { + try (InputStream inputStream = toInputStream(SEQUENCE_COMMENT_EXAMPLE)) { + YamlConfiguration configuration = YamlConfiguration.loadConfig(inputStream); + configuration.set("list", List.of("a", "c")); + + Path actualPath = tempDir.resolve("test-sequence-comment-preservation.yml"); + configuration.save(actualPath); + + assertEquals("#header\nlist:\n- a # inline a\n- c #inline c\n#footer\n", Files.readString(actualPath)); + } + } + + @Test + void testWithoutComments() throws IOException, InvalidConfigurationException { + try (InputStream inputStream = toInputStream(SEQUENCE_COMMENT_EXAMPLE)) { + YamlConfiguration configuration = YamlConfiguration.loadConfig(inputStream); + assertEquals("list:\n- a\n- b\n- c\n", configuration.withoutComments()); + } + } + + private static InputStream toInputStream(String value) { + return new ByteArrayInputStream(value.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/ReflectorTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/ReflectorTest.java new file mode 100644 index 000000000..0653d6644 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/ReflectorTest.java @@ -0,0 +1,85 @@ +package dev.imprex.orebfuscator.reflect; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; +import dev.imprex.orebfuscator.reflect.dummy.Entity; +import dev.imprex.orebfuscator.reflect.dummy.Player; + +class ReflectorTest { + + private static final String REQUIREMENTS_FIELD = decode( + "ewogIHJlcXVpcmVkTW9kaWZpZXJzOiBwcml2YXRlIHN5bmNocm9uaXplZCwKICBiYW5uZWRNb2RpZmllcnM6IHN0YXRpYywKICBpbmNsdWRlU3ludGhldGljLAogIG5hbWU6IFxRdGVzdFxFLAogIGRlY2xhcmluZ0NsYXNzOiB7aXMgZGV2LmltcHJleC5vcmViZnVzY2F0b3IucmVmbGVjdC5kdW1teS5FbnRpdHl9LAogIHR5cGU6IHtpcyB2b2lkfQp9"); + private static final String REQUIREMENTS_METHOD = decode( + "ewogIHJlcXVpcmVkTW9kaWZpZXJzOiBwcml2YXRlIHN5bmNocm9uaXplZCwKICBiYW5uZWRNb2RpZmllcnM6IHN0YXRpYywKICBpbmNsdWRlU3ludGhldGljLAogIG5hbWU6IFxRdGVzdFxFLAogIGRlY2xhcmluZ0NsYXNzOiB7aXMgZGV2LmltcHJleC5vcmViZnVzY2F0b3IucmVmbGVjdC5kdW1teS5FbnRpdHl9LAogIGV4Y2VwdGlvbkNsYXNzOiB7CiAgICA8YW55Pj17c3ViLWNsYXNzLW9mIGphdmEubGFuZy5SdW50aW1lRXhjZXB0aW9ufSwKICAgIDA9e2lzIGphdmEubGFuZy5OdWxsUG9pbnRlckV4Y2VwdGlvbn0KICB9LAogIHBhcmFtZXRlckNsYXNzOiB7CiAgICA8YW55Pj17aXMgaW50fSwKICAgIDA9e2lzIGludH0sCiAgICAyPXtzdXBlci1jbGFzcy1vZiBkZXYuaW1wcmV4Lm9yZWJmdXNjYXRvci5yZWZsZWN0LmR1bW15LkVudGl0eX0sCiAgICA0PXtyZWdleCAuKlR5cGV9CiAgfSwKICBwYXJhbWV0ZXJDb3VudDogNiwKICByZXR1cm5UeXBlOiB7aXMgdm9pZH0KfQ=="); + + private static String decode(String input) { + return new String(Base64.getDecoder().decode(input), StandardCharsets.UTF_8); + } + + @Test + void testRecursive() { + Reflector reflector = Reflector.of(Player.class) + .recursive(); + + assertEquals(4, reflector.constructor() + .stream().count()); + assertEquals(1, reflector.constructor() + .declaringClass().is(Object.class) + .stream().count()); + } + + @Test + void testRecursiveUntil() { + Reflector reflector = Reflector.of(Player.class) + .recursiveUntil(Entity.class); + + assertEquals(3, reflector.constructor() + .stream().count()); + assertEquals(0, reflector.constructor() + .declaringClass().is(Object.class) + .stream().count()); + } + + @Test + void testRequirementFormatting() { + Reflector reflector = Reflector.of(Entity.class); + + var exception = assertThrows(IllegalArgumentException.class, () -> { + reflector.field() + .requireModifier(Modifier.PRIVATE | Modifier.SYNCHRONIZED) + .banStatic() + .declaringClass().is(Entity.class) + .nameIs("test") + .type().is(void.class) + .includeSynthetic() + .firstOrThrow(); + }); + assertTrue(exception.getMessage().endsWith(REQUIREMENTS_FIELD)); + + exception = assertThrows(IllegalArgumentException.class, () -> { + reflector.method() + .requireModifier(Modifier.PRIVATE | Modifier.SYNCHRONIZED) + .banStatic() + .declaringClass().is(Entity.class) + .nameIs("test") + .exception(0).is(NullPointerException.class) + .exception().subOf(RuntimeException.class) + .parameter().is(int.class) + .parameter(2).superOf(Entity.class) + .parameter(0).is(int.class) + .parameter(4).regex(Pattern.compile(".*Type", Pattern.DOTALL)) + .returnType().is(void.class) + .includeSynthetic() + .parameterCount(6) + .firstOrThrow(); + }); + assertTrue(exception.getMessage().endsWith(REQUIREMENTS_METHOD)); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/ConstructorAccessorTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/ConstructorAccessorTest.java new file mode 100644 index 000000000..f566f277c --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/ConstructorAccessorTest.java @@ -0,0 +1,59 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.List; +import org.junit.jupiter.api.Test; + +class ConstructorAccessorTest { + + @Test + @SuppressWarnings("deprecation") + void testWrapConstructor() throws Exception { + var constructor = ConstructorTest.class.getDeclaredConstructor(String.class); + assertFalse(constructor.isAccessible()); + + var accessor = Accessors.wrap(constructor); + + assertFalse(constructor.isAccessible()); + assertEquals(constructor, accessor.member()); + } + + @Test + void testInvoke() throws Exception { + var constructor = ConstructorTest.class.getDeclaredConstructor(String.class); + var accessor = Accessors.wrap(constructor); + + var instance = (ConstructorTest) accessor.invoke("a"); + assertEquals(List.of("a"), instance.values); + + assertThrows(IllegalStateException.class, () -> accessor.invoke(1)); + } + + @Test + void testInvokeVarargs() throws Exception { + var constructor = ConstructorTest.class.getDeclaredConstructor(String[].class); + var accessor = Accessors.wrap(constructor); + + var values = new String[]{"a", "b", "c"}; + var instance = (ConstructorTest) accessor.invoke(new Object[]{values}); + assertEquals(List.of(values), instance.values); + + assertThrows(IllegalStateException.class, () -> accessor.invoke(1)); + } + + public static class ConstructorTest { + + final List values; + + public ConstructorTest(String value) { + this.values = List.of(value); + } + + public ConstructorTest(String... values) { + this.values = List.of(values); + } + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/FieldAccessorTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/FieldAccessorTest.java new file mode 100644 index 000000000..0ed961048 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/FieldAccessorTest.java @@ -0,0 +1,88 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.junit.jupiter.api.Test; + +class FieldAccessorTest { + + @Test + @SuppressWarnings("deprecation") + void testWrapField() throws Exception { + var field = FieldTest.class.getDeclaredField("value"); + assertFalse(field.isAccessible()); + + var accessor = Accessors.wrap(field); + + assertFalse(field.isAccessible()); + assertEquals(field, accessor.member()); + } + + @Test + @SuppressWarnings("deprecation") + void testWrapStaticField() throws Exception { + var field = FieldTest.class.getDeclaredField("staticValue"); + assertFalse(field.isAccessible()); + + var accessor = Accessors.wrap(field); + + assertFalse(field.isAccessible()); + assertEquals(field, accessor.member()); + } + + @Test + void testGet() throws Exception { + var field = FieldTest.class.getDeclaredField("value"); + var accessor = Accessors.wrap(field); + + var instance = new FieldTest(1); + assertEquals(instance.value, accessor.get(instance)); + assertThrows(IllegalStateException.class, () -> accessor.get(null)); + } + + @Test + void testSet() throws Exception { + var field = FieldTest.class.getDeclaredField("value"); + var accessor = Accessors.wrap(field); + + var instance = new FieldTest(1); + accessor.set(instance, -1); + assertEquals(-1, instance.value); + assertThrows(IllegalStateException.class, () -> accessor.set(null, -1)); + } + + @Test + void testStaticGet() throws Exception { + var field = FieldTest.class.getDeclaredField("staticValue"); + var accessor = Accessors.wrap(field); + + FieldTest.staticValue = 1; + assertEquals(FieldTest.staticValue, accessor.get(null)); + assertEquals(FieldTest.staticValue, accessor.get("ab")); + } + + @Test + void testStaticSet() throws Exception { + var field = FieldTest.class.getDeclaredField("staticValue"); + var accessor = Accessors.wrap(field); + + accessor.set(null, -1); + assertEquals(-1, FieldTest.staticValue); + + accessor.set("ab", -2); + assertEquals(-2, FieldTest.staticValue); + } + + public static class FieldTest { + + public static int staticValue; + + public int value; + + public FieldTest(int value) { + this.value = value; + } + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/MethodAccessorTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/MethodAccessorTest.java new file mode 100644 index 000000000..ff48df2d1 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/accessor/MethodAccessorTest.java @@ -0,0 +1,92 @@ +package dev.imprex.orebfuscator.reflect.accessor; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class MethodAccessorTest { + + @Test + @SuppressWarnings("deprecation") + void testWrapMethod() throws Exception { + var method = MethodTest.class.getMethod("sum", int.class, int.class); + assertFalse(method.isAccessible()); + + var accessor = Accessors.wrap(method); + + assertFalse(method.isAccessible()); + assertEquals(method, accessor.member()); + } + + @Test + @SuppressWarnings("deprecation") + void testWrapStaticMethod() throws Exception { + var method = MethodTest.class.getMethod("staticSum", int.class, int.class); + assertFalse(method.isAccessible()); + + var accessor = Accessors.wrap(method); + + assertFalse(method.isAccessible()); + assertEquals(method, accessor.member()); + } + + @Test + void testInvoke() throws Exception { + var method = MethodTest.class.getDeclaredMethod("sum", int.class, int.class); + var accessor = Accessors.wrap(method); + + assertEquals(3, accessor.invoke(MethodTest.INSTANCE, 1, 2)); + assertThrows(IllegalStateException.class, () -> accessor.invoke(null, 1, 2)); + } + + @Test + void testInvokeVarargs() throws Exception { + var method = MethodTest.class.getDeclaredMethod("sum", int.class, int[].class); + var accessor = Accessors.wrap(method); + + assertEquals(10, accessor.invoke(MethodTest.INSTANCE, 1, new int[]{2, 3, 4})); + assertThrows(IllegalStateException.class, () -> accessor.invoke(null, 1, new int[]{2, 3, 4})); + } + + @Test + void testStaticInvoke() throws Exception { + var method = MethodTest.class.getDeclaredMethod("staticSum", int.class, int.class); + var accessor = Accessors.wrap(method); + + assertEquals(3, accessor.invoke(null, 1, 2)); + assertEquals(3, accessor.invoke("ab", 1, 2)); + } + + @Test + void testStaticInvokeVarargs() throws Exception { + var method = MethodTest.class.getDeclaredMethod("staticSum", int.class, int[].class); + var accessor = Accessors.wrap(method); + + assertEquals(10, accessor.invoke(null, 1, new int[]{2, 3, 4})); + assertEquals(10, accessor.invoke("ab", 1, new int[]{2, 3, 4})); + } + + public static class MethodTest { + + public static final MethodTest INSTANCE = new MethodTest(); + + public static int staticSum(int a, int b) { + return a + b; + } + + public static int staticSum(int a, int... ints) { + return a + Arrays.stream(ints).sum(); + } + + public int sum(int a, int b) { + return a + b; + } + + public int sum(int a, int... ints) { + return a + Arrays.stream(ints).sum(); + } + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Entity.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Entity.java new file mode 100644 index 000000000..53bb1e741 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Entity.java @@ -0,0 +1,57 @@ +package dev.imprex.orebfuscator.reflect.dummy; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Objects; + +public class Entity implements Id { + + public static final String DEFAULT_WOLRD = "default"; + + private static int nextEntityId = 0; + + public static int nextId() { + return nextEntityId++; + } + + protected final int entityId; + + private volatile String world; + private int position; + + public Entity(String world) { + this.entityId = nextId(); + this.world = world; + } + + @Override + public final int id() { + return entityId; + } + + public String getWorld() { + return world; + } + + public int getPosition() { + return position; + } + + public synchronized boolean teleport(String world, int position) throws IOException, NullPointerException { + Objects.requireNonNull(world); + + try (InputStream inputStream = Files.newInputStream(Paths.get(world))) { + return position == 0; + } + } + + public void destroy() throws NullPointerException, ClassNotFoundException { + this.world = null; + } + + protected void move(int offset) { + this.position += offset; + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Id.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Id.java new file mode 100644 index 000000000..85ab195f8 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Id.java @@ -0,0 +1,7 @@ +package dev.imprex.orebfuscator.reflect.dummy; + +public interface Id { + + int id(); + +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Player.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Player.java new file mode 100644 index 000000000..5c690ce3b --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/dummy/Player.java @@ -0,0 +1,28 @@ +package dev.imprex.orebfuscator.reflect.dummy; + +public class Player extends Entity { + + private float health = 20f; + + public Player() { + this(Entity.DEFAULT_WOLRD); + } + + public Player(String world) { + super(world); + } + + public void heal(float health) { + health += health; + } + + public boolean damage(float damage) { + health -= damage; + return health > 0; + } + + @Override + protected void move(int offset) { + super.move(offset + 1); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/AbstractExecutablePredicateTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/AbstractExecutablePredicateTest.java new file mode 100644 index 000000000..b1bdf3e46 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/AbstractExecutablePredicateTest.java @@ -0,0 +1,75 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import org.junit.jupiter.api.Test; +import dev.imprex.orebfuscator.reflect.Reflector; +import dev.imprex.orebfuscator.reflect.dummy.Entity; + +class AbstractExecutablePredicateTest { + + @Test + void testException() { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(1, reflector.method() + .exception(0).is(NullPointerException.class) + .stream().count()); + assertEquals(1, reflector.method() + .exception(1).is(NullPointerException.class) + .stream().count()); + assertEquals(0, reflector.method() + .exception(2).is(NullPointerException.class) + .stream().count()); + + assertEquals(2, reflector.method() + .exception().is(NullPointerException.class) + .stream().count()); + assertEquals(1, reflector.method() + .exception().is(NullPointerException.class) + .exception().is(IOException.class) + .stream().count()); + } + + @Test + void testParameter() { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(1, reflector.method() + .parameter(0).is(int.class) + .stream().count()); + assertEquals(1, reflector.method() + .parameter(1).is(int.class) + .stream().count()); + assertEquals(0, reflector.method() + .parameter(2).is(int.class) + .stream().count()); + + assertEquals(2, reflector.method() + .parameter().is(int.class) + .stream().count()); + assertEquals(1, reflector.method() + .parameter().is(int.class) + .parameter().is(String.class) + .stream().count()); + } + + @Test + void testParameterCount() { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(5, reflector.method() + .parameterCount(0) + .stream().count()); + assertEquals(1, reflector.method() + .parameterCount(1) + .stream().count()); + assertEquals(1, reflector.method() + .parameterCount(2) + .stream().count()); + assertEquals(0, reflector.method() + .parameterCount(3) + .stream().count()); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/AbstractMemberPredicateTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/AbstractMemberPredicateTest.java new file mode 100644 index 000000000..84518b126 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/AbstractMemberPredicateTest.java @@ -0,0 +1,158 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.lang.reflect.Modifier; +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; +import dev.imprex.orebfuscator.reflect.Reflector; +import dev.imprex.orebfuscator.reflect.dummy.Entity; +import dev.imprex.orebfuscator.reflect.dummy.Player; +import dev.imprex.orebfuscator.reflect.predicate.ClassPredicate.IsClassPredicate; + +class AbstractMemberPredicateTest { + + @Test + void testRequireModifier() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(1, reflector.field() + .requireModifier(Modifier.VOLATILE) + .stream().count()); + assertEquals(1, reflector.field() + .requirePublic() + .stream().count()); + assertEquals(1, reflector.field() + .requireProtected() + .stream().count()); + assertEquals(3, reflector.field() + .requirePrivate() + .stream().count()); + assertEquals(2, reflector.field() + .requireStatic() + .stream().count()); + assertEquals(2, reflector.field() + .requireFinal() + .stream().count()); + } + + @Test + void testBanModifier() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(4, reflector.field() + .banModifier(Modifier.VOLATILE) + .stream().count()); + assertEquals(4, reflector.field() + .banPublic() + .stream().count()); + assertEquals(4, reflector.field() + .banProtected() + .stream().count()); + assertEquals(2, reflector.field() + .banPrivate() + .stream().count()); + assertEquals(3, reflector.field() + .banStatic() + .stream().count()); + assertEquals(3, reflector.field() + .banFinal() + .stream().count()); + } + + @Test + void testName() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(2, reflector.field() + .nameRegex(Pattern.compile(".*id", Pattern.CASE_INSENSITIVE | Pattern.DOTALL)) + .stream().count()); + assertEquals(1, reflector.field() + .nameIs("entityId") + .stream().count()); + assertEquals(1, reflector.field() + .nameIsIgnoreCase("entityid") + .stream().count()); + } + + @Test + void testDeclaringClass() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(5, reflector.field() + .declaringClass().is(Entity.class) + .stream().count()); + assertEquals(5, reflector.field() + .declaringClass(new IsClassPredicate(Entity.class)) + .stream().count()); + + assertEquals(0, reflector.field() + .declaringClass().is(Player.class) + .stream().count()); + assertEquals(0, reflector.field() + .declaringClass(new IsClassPredicate(Player.class)) + .stream().count()); + } + + @Test + void testAll() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(1, reflector.constructor().stream().count()); + assertEquals(5, reflector.field().stream().count()); + assertEquals(7, reflector.method().stream().count()); + } + + @Test + void testGet() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertNotNull(reflector.constructor().get(0)); + assertNull(reflector.constructor().get(1)); + } + + @Test + void testGetOrThrow() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertDoesNotThrow(() -> reflector.constructor().getOrThrow(0)); + assertThrows(IllegalArgumentException.class, () -> reflector.constructor().getOrThrow(1)); + } + + @Test + void testFirst() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertNotNull(reflector.constructor().first()); + assertNull(reflector.constructor().parameterCount(0).first()); + } + + @Test + void testFirstOrThrow() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertDoesNotThrow(() -> reflector.constructor().firstOrThrow()); + assertThrows(IllegalArgumentException.class, () -> reflector.constructor().parameterCount(0).firstOrThrow()); + } + + @Test + void testFind() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertNotNull(reflector.constructor().find(c -> c.getParameterCount() != 0)); + assertNull(reflector.constructor().find(c -> c.getParameterCount() == 0)); + } + + @Test + void testFindOrThrow() throws Exception { + Reflector reflector = Reflector.of(Entity.class); + + assertDoesNotThrow(() -> reflector.constructor().findOrThrow(c -> c.getParameterCount() != 0)); + assertThrows(IllegalArgumentException.class, + () -> reflector.constructor().findOrThrow(c -> c.getParameterCount() == 0)); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/ClassPredicateTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/ClassPredicateTest.java new file mode 100644 index 000000000..c4de77416 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/ClassPredicateTest.java @@ -0,0 +1,109 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.regex.Pattern; +import org.junit.jupiter.api.Test; +import dev.imprex.orebfuscator.reflect.dummy.Entity; +import dev.imprex.orebfuscator.reflect.dummy.Id; +import dev.imprex.orebfuscator.reflect.dummy.Player; + +class ClassPredicateTest { + + @Test + void testIsClassPredicate() { + ClassPredicate predicate = new ClassPredicate.IsClassPredicate(Entity.class); + + assertFalse(predicate.test(Id.class)); + assertTrue(predicate.test(Entity.class)); + assertFalse(predicate.test(Player.class)); + + assertThrows(NullPointerException.class, () -> new ClassPredicate.IsClassPredicate(null)); + assertThrows(NullPointerException.class, () -> predicate.test(null)); + + assertEquals("{is dev.imprex.orebfuscator.reflect.dummy.Entity}", predicate.requirement()); + } + + @Test + void testSuperClassPredicate() { + ClassPredicate predicate = new ClassPredicate.SuperClassPredicate(Entity.class); + + assertTrue(predicate.test(Id.class)); + assertTrue(predicate.test(Entity.class)); + assertFalse(predicate.test(Player.class)); + + assertThrows(NullPointerException.class, () -> new ClassPredicate.SuperClassPredicate(null)); + assertThrows(NullPointerException.class, () -> predicate.test(null)); + + assertEquals("{super-class-of dev.imprex.orebfuscator.reflect.dummy.Entity}", predicate.requirement()); + } + + @Test + void testSubClassPredicate() { + ClassPredicate predicate = new ClassPredicate.SubClassPredicate(Entity.class); + + assertFalse(predicate.test(Id.class)); + assertTrue(predicate.test(Entity.class)); + assertTrue(predicate.test(Player.class)); + + assertThrows(NullPointerException.class, () -> new ClassPredicate.SubClassPredicate(null)); + assertThrows(NullPointerException.class, () -> predicate.test(null)); + + assertEquals("{sub-class-of dev.imprex.orebfuscator.reflect.dummy.Entity}", predicate.requirement()); + } + + @Test + void testAnyClassPredicate() { + // we use a LinkedHashSet here to preserve iteration order for the requirement string + Set> set = new LinkedHashSet<>(List.of(Id.class, Entity.class)); + ClassPredicate predicate = new ClassPredicate.AnyClassPredicate(set); + + assertTrue(predicate.test(Id.class)); + assertTrue(predicate.test(Entity.class)); + assertFalse(predicate.test(Player.class)); + + assertThrows(NullPointerException.class, () -> new ClassPredicate.AnyClassPredicate(null)); + assertThrows(NullPointerException.class, () -> predicate.test(null)); + + assertEquals("{any (dev.imprex.orebfuscator.reflect.dummy.Id, dev.imprex.orebfuscator.reflect.dummy.Entity)}", + predicate.requirement()); + } + + @Test + void testRegexClassPredicate() { + Pattern pattern = Pattern.compile(".*y", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); + ClassPredicate predicate = new ClassPredicate.RegexClassPredicate(pattern); + + assertFalse(predicate.test(Id.class)); + assertTrue(predicate.test(Entity.class)); + assertFalse(predicate.test(Player.class)); + + assertThrows(NullPointerException.class, () -> new ClassPredicate.RegexClassPredicate(null)); + assertThrows(NullPointerException.class, () -> predicate.test(null)); + + assertEquals("{regex .*y}", predicate.requirement()); + } + + @Test + void testBuilder() { + ClassPredicate.Builder builder = new ClassPredicate.Builder<>(Function.identity()); + + assertEquals(new ClassPredicate.IsClassPredicate(Entity.class), builder.is(Entity.class)); + assertEquals(new ClassPredicate.SuperClassPredicate(Entity.class), builder.superOf(Entity.class)); + assertEquals(new ClassPredicate.SubClassPredicate(Entity.class), builder.subOf(Entity.class)); + + ClassPredicate expected = new ClassPredicate.AnyClassPredicate(Set.of(Id.class, Entity.class)); + assertEquals(expected, builder.any(Id.class, Entity.class)); + assertEquals(expected, builder.any(Set.of(Id.class, Entity.class))); + + Pattern pattern = Pattern.compile("foo"); + assertEquals(new ClassPredicate.RegexClassPredicate(pattern), builder.regex(pattern)); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/FieldPredicateTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/FieldPredicateTest.java new file mode 100644 index 000000000..d8ccc33d8 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/FieldPredicateTest.java @@ -0,0 +1,22 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import dev.imprex.orebfuscator.reflect.Reflector; +import dev.imprex.orebfuscator.reflect.dummy.Entity; + +class FieldPredicateTest { + + @Test + void testType() { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(3, reflector.field() + .type().is(int.class) + .stream().count()); + assertEquals(2, reflector.field() + .type().is(String.class) + .stream().count()); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/MethodPredicateTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/MethodPredicateTest.java new file mode 100644 index 000000000..85e3efa27 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/reflect/predicate/MethodPredicateTest.java @@ -0,0 +1,25 @@ +package dev.imprex.orebfuscator.reflect.predicate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import dev.imprex.orebfuscator.reflect.Reflector; +import dev.imprex.orebfuscator.reflect.dummy.Entity; + +class MethodPredicateTest { + + @Test + void testReturnType() { + Reflector reflector = Reflector.of(Entity.class); + + assertEquals(2, reflector.method() + .returnType().is(void.class) + .stream().count()); + assertEquals(3, reflector.method() + .returnType().is(int.class) + .stream().count()); + assertEquals(1, reflector.method() + .returnType().is(String.class) + .stream().count()); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/util/BlockPosTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/util/BlockPosTest.java new file mode 100644 index 000000000..54fdc2ba3 --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/util/BlockPosTest.java @@ -0,0 +1,44 @@ +package dev.imprex.orebfuscator.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class BlockPosTest { + + @Test + public void testLongFormat() { + BlockPos positionA = new BlockPos(-52134, BlockPos.MAX_Y, 6243234); + BlockPos positionB = new BlockPos(0, BlockPos.MIN_Y, -4); + BlockPos positionC = new BlockPos(15, 0, -5663423); + BlockPos positionD = new BlockPos(21523, 16, -5663423); + + long valueA = positionA.toLong(); + long valueB = positionB.toLong(); + long valueC = positionC.toLong(); + long valueD = positionD.toLong(); + + assertEquals(positionA, BlockPos.fromLong(valueA)); + assertEquals(positionB, BlockPos.fromLong(valueB)); + assertEquals(positionC, BlockPos.fromLong(valueC)); + assertEquals(positionD, BlockPos.fromLong(valueD)); + } + + @Test + public void testSectionPos() { + final int chunkX = -42 << 4; + final int chunkZ = 6521 << 4; + + BlockPos positionA = new BlockPos(chunkX + 8, BlockPos.MAX_Y, chunkZ); + BlockPos positionB = new BlockPos(chunkX, BlockPos.MIN_Y, chunkZ + 15); + BlockPos positionC = new BlockPos(chunkX + 15, 0, chunkZ + 4); + + int sectionPosA = positionA.toSectionPos(); + int sectionPosB = positionB.toSectionPos(); + int sectionPosC = positionC.toSectionPos(); + + assertEquals(positionA, BlockPos.fromSectionPos(chunkX, chunkZ, sectionPosA)); + assertEquals(positionB, BlockPos.fromSectionPos(chunkX, chunkZ, sectionPosB)); + assertEquals(positionC, BlockPos.fromSectionPos(chunkX, chunkZ, sectionPosC)); + } +} diff --git a/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/util/WeightedRandomTest.java b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/util/WeightedRandomTest.java new file mode 100644 index 000000000..7dd6e5d0d --- /dev/null +++ b/orebfuscator-core/src/test/java/dev/imprex/orebfuscator/util/WeightedRandomTest.java @@ -0,0 +1,128 @@ +package dev.imprex.orebfuscator.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import org.junit.jupiter.api.Test; + +public class WeightedRandomTest { + + private static final long SEED = 1337; + private static final int SAMPLES = 250_000; + private static final double SIGMA = 2.0; + + private WeightedRandom createWeightedRandom(Map weights) { + var builder = WeightedRandom.builder(); + for (var entry : weights.entrySet()) { + builder.add(entry.getKey(), entry.getValue()); + } + return builder.build(); + } + + private void assertDistribution(WeightedRandom weightedRandom, Map weights, int n, long seed, + double sigma) { + Random random = new Random(seed); + + Map counts = new HashMap<>(); + for (int i = 0; i < n; i++) { + counts.merge(weightedRandom.next(random), 1, Integer::sum); + } + + double totalWeight = weights.values().stream().mapToDouble(Double::doubleValue).sum(); + + for (var entry : weights.entrySet()) { + int key = entry.getKey(); + double prob = entry.getValue() / totalWeight; + int count = counts.getOrDefault(key, 0); + double observed = count / (double) n; + + // Binomial standard deviation for frequency + double sd = Math.sqrt(prob * (1 - prob) / n); + double tol = sigma * sd; + + assertTrue(Math.abs(observed - prob) <= tol, () -> String + .format("freq for %d off: obs=%.6f exp=%.6f tol=%.6f (N=%d, sd=%.6g)", key, observed, prob, tol, n, sd)); + } + } + + @Test + public void testEmptyBuilderThrows() { + var builder = WeightedRandom.builder(); + assertThrows(IllegalStateException.class, builder::build); + } + + @Test + public void testInvalidWeightsThrow() { + var builder = WeightedRandom.builder(); + assertThrows(IllegalArgumentException.class, () -> builder.add(0, 0.0)); + assertThrows(IllegalArgumentException.class, () -> builder.add(0, -1.0)); + assertThrows(IllegalArgumentException.class, () -> builder.add(0, Double.POSITIVE_INFINITY)); + assertThrows(IllegalArgumentException.class, () -> builder.add(0, Double.NEGATIVE_INFINITY)); + } + + @Test + public void testSingleEntry() { + var weightedRandom = WeightedRandom.builder().add(42, 1.0).build(); + + for (int i = 0; i < 1000; i++) { + assertEquals(42, weightedRandom.next()); + } + } + + @Test + public void testSameSeeds() { + var distribution = Map.of(1, 0.1, 2, 0.2, 3, 0.9); + var weightedRandom = createWeightedRandom(distribution); + + Random randomA = new Random(42); + Random randomB = new Random(42); + + List sequenceA = new ArrayList<>(); + List sequenceB = new ArrayList<>(); + for (int i = 0; i < 50; i++) { + sequenceA.add(weightedRandom.next(randomA)); + sequenceB.add(weightedRandom.next(randomB)); + } + + assertEquals(sequenceA, sequenceB, "Sequences with same seed should match exactly"); + } + + @Test + public void testUniformWeights() { + var distribution = Map.of(1, 1.0, 2, 1.0, 3, 1.0, 4, 1.0); + var weightedRandom = createWeightedRandom(distribution); + assertDistribution(weightedRandom, distribution, SAMPLES, SEED, SIGMA); + } + + @Test + public void testRandomWeights() { + var distribution = Map.of(1, 0.1, 2, 0.2, 3, 0.9); + var weightedRandom = createWeightedRandom(distribution); + assertDistribution(weightedRandom, distribution, SAMPLES, SEED, SIGMA); + } + + @Test + public void testMergedWeights() { + var weightedRandom = WeightedRandom.builder() + .add(1, 0.1) + .add(2, 0.3) + .add(1, 0.9) + .build(); + + var distribution = Map.of(1, 1.0, 2, 0.3); + assertDistribution(weightedRandom, distribution, SAMPLES, SEED, SIGMA); + } + + @Test + public void testLargeWeights() { + var distribution = Map.of(1, 0.1, 2, 0.2, 3, 100.0, 4, 0.5); + var weightedRandom = createWeightedRandom(distribution); + assertDistribution(weightedRandom, distribution, SAMPLES, SEED, SIGMA); + } +} diff --git a/orebfuscator-core/src/test/resources/config/context-test-report.txt b/orebfuscator-core/src/test/resources/config/context-test-report.txt new file mode 100644 index 000000000..82fb594b9 --- /dev/null +++ b/orebfuscator-core/src/test/resources/config/context-test-report.txt @@ -0,0 +1,16 @@ +Encountered 9 issue(s) while parsing the config: +- is missing or empty +- is missing or empty +child: + - is missing or empty + - is missing or empty + - got disabled due to errors + a: + - is missing or empty + - is missing or empty +a: + b: + c: + - is missing or empty + - is missing or empty + \ No newline at end of file diff --git a/orebfuscator-core/src/test/resources/config/example-config.yml b/orebfuscator-core/src/test/resources/config/example-config.yml new file mode 100644 index 000000000..b9ddb9f07 --- /dev/null +++ b/orebfuscator-core/src/test/resources/config/example-config.yml @@ -0,0 +1,13 @@ +# boolean start +boolean: true # boolean inline +# section start +section: # section inline + # string start + string: abc # string inline + # list start + list: + - a # inline a 1 + # inline a 2 + - b # inline b + - c # inline c +# config end diff --git a/orebfuscator-nms/orebfuscator-nms-api/pom.xml b/orebfuscator-nms/orebfuscator-nms-api/pom.xml index 90338621f..172b872c8 100644 --- a/orebfuscator-nms/orebfuscator-nms-api/pom.xml +++ b/orebfuscator-nms/orebfuscator-nms-api/pom.xml @@ -18,19 +18,10 @@ provided - net.imprex - orebfuscator-common + dev.imprex.orebfuscator + orebfuscator-core ${revision} compile - - - - - org.apache.maven.plugins - maven-shade-plugin - - - \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorNms.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorNms.java index dc011f8ce..0bc9664bd 100644 --- a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorNms.java +++ b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/OrebfuscatorNms.java @@ -5,22 +5,21 @@ import org.bukkit.World; import org.bukkit.entity.Player; -import net.imprex.orebfuscator.config.Config; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.nms.NmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.BlockProperties; import net.imprex.orebfuscator.util.MinecraftVersion; -import net.imprex.orebfuscator.util.NamespacedKey; -import net.imprex.orebfuscator.util.OFCLogger; import net.imprex.orebfuscator.util.ServerVersion; public class OrebfuscatorNms { private static NmsManager instance; - public static void initialize(Config config) { + public static void initialize() { if (OrebfuscatorNms.instance != null) { throw new IllegalStateException("NMS adapter is already initialized!"); } @@ -30,24 +29,28 @@ public static void initialize(Config config) { nmsVersion += "_mojang"; } - OFCLogger.debug("Searching NMS adapter for server version \"" + nmsVersion + "\"!"); + OfcLogger.debug("Searching NMS adapter for server version \"" + nmsVersion + "\"!"); try { String className = "net.imprex.orebfuscator.nms." + nmsVersion + ".NmsManager"; Class nmsManager = Class.forName(className).asSubclass(NmsManager.class); - Constructor constructor = nmsManager.getConstructor(Config.class); - OrebfuscatorNms.instance = constructor.newInstance(config); + Constructor constructor = nmsManager.getConstructor(); + OrebfuscatorNms.instance = constructor.newInstance(); } catch (ClassNotFoundException e) { throw new RuntimeException("Server version \"" + nmsVersion + "\" is currently not supported!", e); } catch (Exception e) { throw new RuntimeException("Couldn't initialize NMS adapter", e); } - OFCLogger.debug("NMS adapter for server version \"" + nmsVersion + "\" found!"); + OfcLogger.debug("NMS adapter for server version \"" + nmsVersion + "\" found!"); } - public static AbstractRegionFileCache getRegionFileCache() { - return instance.getRegionFileCache(); + public static RegistryAccessor registry() { + return instance; + } + + public static AbstractRegionFileCache createRegionFileCache(Config config) { + return instance.createRegionFileCache(config); } public static int getUniqueBlockStateCount() { @@ -58,10 +61,6 @@ public static int getMaxBitsPerBlockState() { return instance.getMaxBitsPerBlockState(); } - public static BlockProperties getBlockByName(String key) { - return instance.getBlockByName(NamespacedKey.fromString(key)); - } - public static boolean isAir(int blockId) { return instance.isAir(blockId); } @@ -79,7 +78,7 @@ public static ReadOnlyChunk getReadOnlyChunk(World world, int chunkX, int chunkZ } public static int getBlockState(World world, BlockPos position) { - return getBlockState(world, position.x, position.y, position.z); + return getBlockState(world, position.x(), position.y(), position.z()); } public static int getBlockState(World world, int x, int y, int z) { @@ -93,11 +92,4 @@ public static void sendBlockUpdates(World world, Iterable iterable) { public static void sendBlockUpdates(Player player, Iterable iterable) { instance.sendBlockUpdates(player, iterable); } - - public static void close() { - if (instance != null) { - instance.close(); - instance = null; - } - } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractNmsManager.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractNmsManager.java index 3bc532fe2..eb427ad1a 100644 --- a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractNmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractNmsManager.java @@ -3,24 +3,25 @@ import java.util.HashMap; import java.util.Map; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.MathUtil; -import net.imprex.orebfuscator.util.NamespacedKey; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public abstract class AbstractNmsManager implements NmsManager { +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.MathUtil; +import dev.imprex.orebfuscator.util.NamespacedKey; - private final AbstractRegionFileCache regionFileCache; +public abstract class AbstractNmsManager implements NmsManager { private final int uniqueBlockStateCount; private final int maxBitsPerBlockState; private final BlockStateProperties[] blockStates; private final Map blocks = new HashMap<>(); + protected final Map tags = new HashMap<>(); - public AbstractNmsManager(int uniqueBlockStateCount, AbstractRegionFileCache regionFileCache) { - this.regionFileCache = regionFileCache; - + public AbstractNmsManager(int uniqueBlockStateCount) { this.uniqueBlockStateCount = uniqueBlockStateCount; this.maxBitsPerBlockState = MathUtil.ceilLog2(uniqueBlockStateCount); @@ -35,9 +36,8 @@ protected final void registerBlockProperties(BlockProperties block) { } } - @Override - public final AbstractRegionFileCache getRegionFileCache() { - return this.regionFileCache; + protected final void registerBlockTag(BlockTag tag) { + this.tags.put(tag.key(), tag); } @Override @@ -51,8 +51,13 @@ public final int getMaxBitsPerBlockState() { } @Override - public final BlockProperties getBlockByName(NamespacedKey key) { - return this.blocks.get(key); + public final @Nullable BlockProperties getBlockByName(@NotNull String name) { + return this.blocks.get(NamespacedKey.fromString(name)); + } + + @Override + public final @Nullable BlockTag getBlockTagByName(@NotNull String name) { + return this.tags.get(NamespacedKey.fromString(name)); } @Override @@ -69,9 +74,4 @@ public final boolean isOccluding(int id) { public final boolean isBlockEntity(int id) { return this.blockStates[id].isBlockEntity(); } - - @Override - public final void close() { - this.regionFileCache.clear(); - } } diff --git a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java deleted file mode 100644 index 44b974057..000000000 --- a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/AbstractRegionFileCache.java +++ /dev/null @@ -1,116 +0,0 @@ -package net.imprex.orebfuscator.nms; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.util.ChunkPosition; -import net.imprex.orebfuscator.util.SimpleCache; - -public abstract class AbstractRegionFileCache { - - protected final ReadWriteLock lock = new ReentrantReadWriteLock(true); - protected final Map regionFiles; - - protected final CacheConfig cacheConfig; - - public AbstractRegionFileCache(CacheConfig cacheConfig) { - this.cacheConfig = cacheConfig; - - this.regionFiles = new SimpleCache<>(cacheConfig.maximumOpenRegionFiles(), this::remove); - } - - protected abstract T createRegionFile(Path path) throws IOException; - - protected abstract void closeRegionFile(T t) throws IOException; - - protected abstract DataInputStream createInputStream(T t, ChunkPosition key) throws IOException; - - protected abstract DataOutputStream createOutputStream(T t, ChunkPosition key) throws IOException; - - public final DataInputStream createInputStream(ChunkPosition key) throws IOException { - T t = this.get(this.cacheConfig.regionFile(key)); - return t != null ? this.createInputStream(t, key) : null; - } - - public final DataOutputStream createOutputStream(ChunkPosition key) throws IOException { - T t = this.get(this.cacheConfig.regionFile(key)); - return t != null ? this.createOutputStream(t, key) : null; - } - - private final void remove(Map.Entry entry) { - try { - this.closeRegionFile(entry.getValue()); - } catch (IOException e) { - e.printStackTrace(); - } - } - - protected final T get(Path path) throws IOException { - this.lock.readLock().lock(); - try { - T t = this.regionFiles.get(path); - if (t != null) { - return t; - } - } finally { - this.lock.readLock().unlock(); - } - - if (Files.notExists(path.getParent())) { - Files.createDirectories(path.getParent()); - } - - if (this.regionFiles.size() > this.cacheConfig.maximumOpenRegionFiles()) { - throw new IllegalStateException(String.format("RegionFileCache got bigger than expected (%d > %d)", - this.regionFiles.size(), this.cacheConfig.maximumOpenRegionFiles())); - } - - T t = Objects.requireNonNull(this.createRegionFile(path)); - - this.lock.writeLock().lock(); - try { - this.regionFiles.putIfAbsent(path, t); - return this.regionFiles.get(path); - } finally { - this.lock.writeLock().unlock(); - } - } - - public final void close(Path path) throws IOException { - this.lock.writeLock().lock(); - try { - T t = this.regionFiles.remove(path); - if (t != null) { - this.closeRegionFile(t); - } - } finally { - this.lock.writeLock().unlock(); - } - } - - public final void clear() { - this.lock.writeLock().lock(); - try { - for (T t : this.regionFiles.values()) { - try { - if (t != null) { - this.closeRegionFile(t); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - this.regionFiles.clear(); - } finally { - this.lock.writeLock().unlock(); - } - } -} diff --git a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/NmsManager.java index 7df81e57b..a5b95fb14 100644 --- a/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/nms/NmsManager.java @@ -3,25 +3,14 @@ import org.bukkit.World; import org.bukkit.entity.Player; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.NamespacedKey; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import dev.imprex.orebfuscator.util.BlockPos; -public interface NmsManager { +public interface NmsManager extends RegistryAccessor { - AbstractRegionFileCache getRegionFileCache(); - - int getUniqueBlockStateCount(); - - int getMaxBitsPerBlockState(); - - BlockProperties getBlockByName(NamespacedKey key); - - boolean isAir(int blockId); - - boolean isOccluding(int blockId); - - boolean isBlockEntity(int blockId); + AbstractRegionFileCache createRegionFileCache(Config config); ReadOnlyChunk getReadOnlyChunk(World world, int chunkX, int chunkZ); @@ -30,6 +19,4 @@ public interface NmsManager { void sendBlockUpdates(World world, Iterable iterable); void sendBlockUpdates(Player player, Iterable iterable); - - void close(); } \ No newline at end of file diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java similarity index 87% rename from orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java rename to orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java index a19d01c1b..d6e6cc08a 100644 --- a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java +++ b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/util/MinecraftVersion.java @@ -7,6 +7,9 @@ import org.bukkit.Bukkit; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.Version; + public final class MinecraftVersion { private static final class NmsMapping { @@ -26,7 +29,7 @@ public static String get(Version version) { for (NmsMapping mapping : MAPPINGS) { if (version.isAtOrAbove(mapping.version)) { if (mapping.version.minor() != version.minor()) { - OFCLogger.warn(String.format("Using nms mapping with mismatched minor versions: %s - %s", + OfcLogger.warn(String.format("Using nms mapping with mismatched minor versions: %s - %s", mapping.version, version)); } @@ -83,18 +86,18 @@ public static int patchVersion() { } public static boolean isAbove(String version) { - return CURRENT_VERSION.isAbove(Version.parse(version)); + return CURRENT_VERSION.isAbove(version); } public static boolean isAtOrAbove(String version) { - return CURRENT_VERSION.isAtOrAbove(Version.parse(version)); + return CURRENT_VERSION.isAtOrAbove(version); } public static boolean isAtOrBelow(String version) { - return CURRENT_VERSION.isAtOrBelow(Version.parse(version)); + return CURRENT_VERSION.isAtOrBelow(version); } public static boolean isBelow(String version) { - return CURRENT_VERSION.isBelow(Version.parse(version)); + return CURRENT_VERSION.isBelow(version); } } diff --git a/orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ServerVersion.java b/orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/util/ServerVersion.java similarity index 100% rename from orebfuscator-common/src/main/java/net/imprex/orebfuscator/util/ServerVersion.java rename to orebfuscator-nms/orebfuscator-nms-api/src/main/java/net/imprex/orebfuscator/util/ServerVersion.java diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/NmsManager.java index a0e293086..49fa951d0 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/NmsManager.java @@ -2,14 +2,15 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; +import java.util.Set; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_16_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_16_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer; import org.bukkit.entity.Player; @@ -20,14 +21,17 @@ import com.comphenix.protocol.wrappers.WrappedBlockData; import com.google.common.collect.ImmutableList; -import net.imprex.orebfuscator.config.Config; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.server.v1_16_R1.Block; +import net.minecraft.server.v1_16_R1.BlockAccessAir; import net.minecraft.server.v1_16_R1.BlockPosition; import net.minecraft.server.v1_16_R1.Blocks; import net.minecraft.server.v1_16_R1.Chunk; @@ -36,9 +40,12 @@ import net.minecraft.server.v1_16_R1.EntityPlayer; import net.minecraft.server.v1_16_R1.IBlockData; import net.minecraft.server.v1_16_R1.IRegistry; +import net.minecraft.server.v1_16_R1.MinecraftKey; import net.minecraft.server.v1_16_R1.Packet; import net.minecraft.server.v1_16_R1.PacketListenerPlayOut; import net.minecraft.server.v1_16_R1.ResourceKey; +import net.minecraft.server.v1_16_R1.Tag; +import net.minecraft.server.v1_16_R1.TagsBlock; import net.minecraft.server.v1_16_R1.TileEntity; import net.minecraft.server.v1_16_R1.WorldServer; @@ -68,8 +75,8 @@ private static EntityPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.REGISTRY_ID.a(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.REGISTRY_ID.a()); for (Map.Entry, Block> entry : IRegistry.BLOCK.c()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().a().toString()); @@ -79,15 +86,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (IBlockData blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getCombinedId(blockState)) .withIsAir(blockState.isAir()) - /** - * l -> for barrier/slime_block/spawner/leaves - * isOccluding -> for every other block - */ - .withIsOccluding(material.isOccluding() && blockState.l()/*canOcclude*/) + .withIsOccluding(blockState.i(BlockAccessAir.INSTANCE, BlockPosition.ZERO)/*isSolidRender*/) .withIsBlockEntity(block.isTileEntity()) .withIsDefaultState(Objects.equals(block.getBlockData(), blockState)) .build(); @@ -95,8 +96,27 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + for (Entry> entry : TagsBlock.b().b().entrySet()) { + NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().toString()); + + Set blocks = new HashSet<>(); + for (Block block : entry.getValue().getTagged()) { + BlockProperties properties = getBlockByName(IRegistry.BLOCK.getKey(block).toString()); + if (properties != null) { + blocks.add(properties); + } + } + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + } + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -122,12 +142,12 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ChunkProviderServer serverChunkCache = level(world).getChunkProvider(); BlockPosition.MutableBlockPosition position = new BlockPosition.MutableBlockPosition(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.c(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.c(pos.x(), pos.y(), pos.z()); serverChunkCache.flagDirty(position); } } @@ -142,16 +162,16 @@ public void sendBlockUpdates(Player player, Iterable iterable) { Map> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.c(pos.x, pos.y, pos.z); + position.c(pos.x(), pos.y(), pos.z()); IBlockData blockState = level.getType(position); - ChunkCoordIntPair chunkCoord = new ChunkCoordIntPair(pos.x >> 4, pos.z >> 4); - short location = (short) ((pos.x & 0xF) << 12 | (pos.z & 0xF) << 8 | pos.y); + ChunkCoordIntPair chunkCoord = new ChunkCoordIntPair(pos.x() >> 4, pos.z() >> 4); + short location = (short) ((pos.x() & 0xF) << 12 | (pos.z() & 0xF) << 8 | pos.y()); sectionPackets.computeIfAbsent(chunkCoord, key -> new ArrayList<>()) .add(new MultiBlockChangeInfo(location, WrappedBlockData.fromHandle(blockState), chunkCoord)); diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/RegionFileCache.java index 177887ec0..619342823 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R1/src/main/java/net/imprex/orebfuscator/nms/v1_16_R1/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_16_R1.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.server.v1_16_R1.ChunkCoordIntPair; import net.minecraft.server.v1_16_R1.RegionFile; import net.minecraft.server.v1_16_R1.RegionFileCompression; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.a(new ChunkCoordIntPair(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.a(new ChunkCoordIntPair(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.c(new ChunkCoordIntPair(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.c(new ChunkCoordIntPair(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/NmsManager.java index 6de27bda7..633139c8e 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/NmsManager.java @@ -2,29 +2,32 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_16_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_16_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_16_R2.entity.CraftPlayer; import org.bukkit.entity.Player; import com.comphenix.protocol.events.PacketContainer; import com.google.common.collect.ImmutableList; -import net.imprex.orebfuscator.config.Config; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.server.v1_16_R2.Block; +import net.minecraft.server.v1_16_R2.BlockAccessAir; import net.minecraft.server.v1_16_R2.BlockPosition; import net.minecraft.server.v1_16_R2.Blocks; import net.minecraft.server.v1_16_R2.Chunk; @@ -33,12 +36,15 @@ import net.minecraft.server.v1_16_R2.EntityPlayer; import net.minecraft.server.v1_16_R2.IBlockData; import net.minecraft.server.v1_16_R2.IRegistry; +import net.minecraft.server.v1_16_R2.MinecraftKey; import net.minecraft.server.v1_16_R2.Packet; import net.minecraft.server.v1_16_R2.PacketListenerPlayOut; import net.minecraft.server.v1_16_R2.PacketPlayOutBlockChange; import net.minecraft.server.v1_16_R2.PacketPlayOutMultiBlockChange; import net.minecraft.server.v1_16_R2.ResourceKey; import net.minecraft.server.v1_16_R2.SectionPosition; +import net.minecraft.server.v1_16_R2.Tag; +import net.minecraft.server.v1_16_R2.TagsBlock; import net.minecraft.server.v1_16_R2.TileEntity; import net.minecraft.server.v1_16_R2.WorldServer; @@ -68,8 +74,8 @@ private static EntityPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.REGISTRY_ID.a(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.REGISTRY_ID.a()); for (Map.Entry, Block> entry : IRegistry.BLOCK.d()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().a().toString()); @@ -79,15 +85,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (IBlockData blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getCombinedId(blockState)) .withIsAir(blockState.isAir()) - /** - * l -> for barrier/slime_block/spawner/leaves - * isOccluding -> for every other block - */ - .withIsOccluding(material.isOccluding() && blockState.l()/*canOcclude*/) + .withIsOccluding(blockState.i(BlockAccessAir.INSTANCE, BlockPosition.ZERO)/*isSolidRender*/) .withIsBlockEntity(block.isTileEntity()) .withIsDefaultState(Objects.equals(block.getBlockData(), blockState)) .build(); @@ -95,8 +95,27 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + for (Entry> entry : TagsBlock.a().a().entrySet()) { + NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().toString()); + + Set blocks = new HashSet<>(); + for (Block block : entry.getValue().getTagged()) { + BlockProperties properties = getBlockByName(IRegistry.BLOCK.getKey(block).toString()); + if (properties != null) { + blocks.add(properties); + } + } + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + } + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -122,12 +141,12 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ChunkProviderServer serverChunkCache = level(world).getChunkProvider(); BlockPosition.MutableBlockPosition position = new BlockPosition.MutableBlockPosition(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.c(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.c(pos.x(), pos.y(), pos.z()); serverChunkCache.flagDirty(position); } } @@ -142,12 +161,12 @@ public void sendBlockUpdates(Player player, Iterable iterable) { Map> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.c(pos.x, pos.y, pos.z); + position.c(pos.x(), pos.y(), pos.z()); IBlockData blockState = level.getType(position); sectionPackets.computeIfAbsent(SectionPosition.a(position), key -> new HashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/RegionFileCache.java index 86f0fcbb2..63d076ee0 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R2/src/main/java/net/imprex/orebfuscator/nms/v1_16_R2/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_16_R2.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.server.v1_16_R2.ChunkCoordIntPair; import net.minecraft.server.v1_16_R2.RegionFile; import net.minecraft.server.v1_16_R2.RegionFileCompression; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.a(new ChunkCoordIntPair(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.a(new ChunkCoordIntPair(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.c(new ChunkCoordIntPair(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.c(new ChunkCoordIntPair(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/NmsManager.java index 6b1a040e3..e1952aff3 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/NmsManager.java @@ -2,29 +2,32 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_16_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import com.comphenix.protocol.events.PacketContainer; import com.google.common.collect.ImmutableList; -import net.imprex.orebfuscator.config.Config; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.server.v1_16_R3.Block; +import net.minecraft.server.v1_16_R3.BlockAccessAir; import net.minecraft.server.v1_16_R3.BlockPosition; import net.minecraft.server.v1_16_R3.Blocks; import net.minecraft.server.v1_16_R3.Chunk; @@ -33,12 +36,15 @@ import net.minecraft.server.v1_16_R3.EntityPlayer; import net.minecraft.server.v1_16_R3.IBlockData; import net.minecraft.server.v1_16_R3.IRegistry; +import net.minecraft.server.v1_16_R3.MinecraftKey; import net.minecraft.server.v1_16_R3.Packet; import net.minecraft.server.v1_16_R3.PacketListenerPlayOut; import net.minecraft.server.v1_16_R3.PacketPlayOutBlockChange; import net.minecraft.server.v1_16_R3.PacketPlayOutMultiBlockChange; import net.minecraft.server.v1_16_R3.ResourceKey; import net.minecraft.server.v1_16_R3.SectionPosition; +import net.minecraft.server.v1_16_R3.Tag; +import net.minecraft.server.v1_16_R3.TagsBlock; import net.minecraft.server.v1_16_R3.TileEntity; import net.minecraft.server.v1_16_R3.WorldServer; @@ -68,8 +74,8 @@ private static EntityPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.REGISTRY_ID.a(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.REGISTRY_ID.a()); for (Map.Entry, Block> entry : IRegistry.BLOCK.d()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().a().toString()); @@ -79,15 +85,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (IBlockData blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getCombinedId(blockState)) .withIsAir(blockState.isAir()) - /** - * l -> for barrier/slime_block/spawner/leaves - * isOccluding -> for every other block - */ - .withIsOccluding(material.isOccluding() && blockState.l()/*canOcclude*/) + .withIsOccluding(blockState.i(BlockAccessAir.INSTANCE, BlockPosition.ZERO)/*isSolidRender*/) .withIsBlockEntity(block.isTileEntity()) .withIsDefaultState(Objects.equals(block.getBlockData(), blockState)) .build(); @@ -95,8 +95,27 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + for (Entry> entry : TagsBlock.a().a().entrySet()) { + NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().toString()); + + Set blocks = new HashSet<>(); + for (Block block : entry.getValue().getTagged()) { + BlockProperties properties = getBlockByName(IRegistry.BLOCK.getKey(block).toString()); + if (properties != null) { + blocks.add(properties); + } + } + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + } + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -122,12 +141,12 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ChunkProviderServer serverChunkCache = level(world).getChunkProvider(); BlockPosition.MutableBlockPosition position = new BlockPosition.MutableBlockPosition(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.c(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.c(pos.x(), pos.y(), pos.z()); serverChunkCache.flagDirty(position); } } @@ -142,12 +161,12 @@ public void sendBlockUpdates(Player player, Iterable iterable) { Map> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.c(pos.x, pos.y, pos.z); + position.c(pos.x(), pos.y(), pos.z()); IBlockData blockState = level.getType(position); sectionPackets.computeIfAbsent(SectionPosition.a(position), key -> new HashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/RegionFileCache.java index 41fd19731..e483dd424 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_16_R3/src/main/java/net/imprex/orebfuscator/nms/v1_16_R3/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_16_R3.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.server.v1_16_R3.ChunkCoordIntPair; import net.minecraft.server.v1_16_R3.RegionFile; import net.minecraft.server.v1_16_R3.RegionFileCompression; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.a(new ChunkCoordIntPair(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.a(new ChunkCoordIntPair(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.c(new ChunkCoordIntPair(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.c(new ChunkCoordIntPair(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/NmsManager.java index a4733ccf2..354af82f1 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/NmsManager.java @@ -2,15 +2,14 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_17_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_17_R1.entity.CraftPlayer; import org.bukkit.entity.Player; @@ -18,12 +17,14 @@ import com.comphenix.protocol.events.PacketContainer; import com.google.common.collect.ImmutableList; -import net.imprex.orebfuscator.config.Config; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; @@ -31,9 +32,13 @@ import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.Tag; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : Registry.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,27 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + for (Map.Entry> entry : BlockTags.getAllTags().getAllTags().entrySet()) { + NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().toString()); + + Set blocks = new HashSet<>(); + for (Block block : entry.getValue().getValues()) { + BlockProperties properties = getBlockByName(Registry.BLOCK.getKey(block).toString()); + if (properties != null) { + blocks.add(properties); + } + } + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + } + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +139,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkProvider(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.getLevel(); ServerChunkCache serverChunkCache = level.getChunkProvider(); @@ -139,12 +160,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new HashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/RegionFileCache.java index 272d4d842..1b0957932 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_17_R1/src/main/java/net/imprex/orebfuscator/nms/v1_17_R1/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_17_R1.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/NmsManager.java index a5f590590..0f97c77a5 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/NmsManager.java @@ -2,28 +2,30 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_18_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_18_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import com.comphenix.protocol.events.PacketContainer; import com.google.common.collect.ImmutableList; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; @@ -32,9 +34,13 @@ import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket; import net.minecraft.network.protocol.game.ClientboundSectionBlocksUpdatePacket; import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.Tag; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -68,8 +74,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : Registry.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -79,12 +85,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -92,8 +95,27 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + for (Map.Entry> entry : BlockTags.getAllTags().getAllTags().entrySet()) { + NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().toString()); + + Set blocks = new HashSet<>(); + for (Block block : entry.getValue().getValues()) { + BlockProperties properties = getBlockByName(Registry.BLOCK.getKey(block).toString()); + if (properties != null) { + blocks.add(properties); + } + } + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + } + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -119,18 +141,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.getLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -139,12 +161,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/RegionFileCache.java index fb00d21e7..4644ce155 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_18_R1/src/main/java/net/imprex/orebfuscator/nms/v1_18_R1/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_18_R1.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/NmsManager.java index 9d2ef2653..31dc13326 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/NmsManager.java @@ -5,25 +5,29 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_18_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_18_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer; import org.bukkit.entity.Player; import com.comphenix.protocol.events.PacketContainer; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectOpenHashMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; @@ -35,6 +39,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -68,8 +73,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : Registry.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -79,12 +84,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -94,6 +96,23 @@ public NmsManager(Config config) { this.registerBlockProperties(builder.build()); } + + Registry.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -119,18 +138,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.getLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -139,12 +158,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/RegionFileCache.java index f85e8aa76..347b54bd7 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_18_R2/src/main/java/net/imprex/orebfuscator/nms/v1_18_R2/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_18_R2.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/NmsManager.java index cb5fcb959..a2c90a75f 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_19_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_19_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.core.SectionPos; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : Registry.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + Registry.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.getLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/RegionFileCache.java index 1354208ea..4ac48770e 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_19_R1/src/main/java/net/imprex/orebfuscator/nms/v1_19_R1/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_19_R1.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/NmsManager.java index 4a6a9479a..b8195d365 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.getLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/RegionFileCache.java index c6ed9c4ed..c1c016f1f 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_19_R2/src/main/java/net/imprex/orebfuscator/nms/v1_19_R2/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_19_R2.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/NmsManager.java index 4f81a4cc8..600b2d106 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_19_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.getLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/RegionFileCache.java index c7fdc4541..503ae7bf3 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_19_R3/src/main/java/net/imprex/orebfuscator/nms/v1_19_R3/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_19_R3.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/NmsManager.java index cc1b5f3cf..c26dfd020 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/RegionFileCache.java index 8463f080e..deeee278c 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R1/src/main/java/net/imprex/orebfuscator/nms/v1_20_R1/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R1.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/NmsManager.java index 1b026f685..91bec71a6 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R2.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/RegionFileCache.java index 80099778d..ea8bbc7a6 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R2/src/main/java/net/imprex/orebfuscator/nms/v1_20_R2/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R2.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/NmsManager.java index 8d73c0360..aaba94744 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/RegionFileCache.java index 5b6265980..3e83fbb44 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R3/src/main/java/net/imprex/orebfuscator/nms/v1_20_R3/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R3.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/NmsManager.java index f37d98f86..9b60b1c97 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; -import org.bukkit.craftbukkit.v1_20_R4.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_20_R4.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/RegionFileCache.java index 8f4117628..8737f6e70 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_20_R4/src/main/java/net/imprex/orebfuscator/nms/v1_20_R4/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_20_R4.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/NmsManager.java index 274930570..da6a448a9 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/NmsManager.java @@ -5,24 +5,28 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R1.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_21_R1.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; - +import com.mojang.datafixers.util.Pair; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -34,6 +38,7 @@ import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; @@ -67,8 +72,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +83,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender(EmptyBlockGetter.INSTANCE, BlockPos.ZERO)) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +93,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().map(Pair::getSecond).forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +137,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +157,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/RegionFileCache.java index 01ff525aa..f26b915e3 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R1/src/main/java/net/imprex/orebfuscator/nms/v1_21_R1/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_21_R1.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/NmsManager.java index 1e8050794..53e409bd2 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/NmsManager.java @@ -5,24 +5,27 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R2.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R2.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_21_R2.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -67,8 +70,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +81,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender()) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +91,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +135,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +155,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/RegionFileCache.java index 964e50314..d1e1830ad 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R2/src/main/java/net/imprex/orebfuscator/nms/v1_21_R2/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_21_R2.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/NmsManager.java index c5fa68c74..52a6b30e1 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/NmsManager.java @@ -5,24 +5,27 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R3.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R3.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_21_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -67,8 +70,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +81,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender()) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +91,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +135,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +155,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/RegionFileCache.java index 9526e412a..244f4fbea 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R3/src/main/java/net/imprex/orebfuscator/nms/v1_21_R3/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_21_R3.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/NmsManager.java index a24d1178f..a3f718f89 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/NmsManager.java @@ -5,24 +5,27 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R4.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R4.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_21_R4.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -67,8 +70,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +81,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender()) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +91,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +135,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.serverLevel(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +155,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/RegionFileCache.java index 70b247bde..44fc46867 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R4/src/main/java/net/imprex/orebfuscator/nms/v1_21_R4/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_21_R4.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/NmsManager.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/NmsManager.java index 0f4810c2b..b1877dace 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/NmsManager.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/NmsManager.java @@ -5,24 +5,27 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; -import org.bukkit.Material; import org.bukkit.World; import org.bukkit.craftbukkit.v1_21_R5.CraftWorld; -import org.bukkit.craftbukkit.v1_21_R5.block.data.CraftBlockData; import org.bukkit.craftbukkit.v1_21_R5.entity.CraftPlayer; import org.bukkit.entity.Player; import com.google.common.collect.ImmutableList; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.Config; +import dev.imprex.orebfuscator.util.BlockProperties; +import dev.imprex.orebfuscator.util.BlockStateProperties; +import dev.imprex.orebfuscator.util.BlockTag; +import dev.imprex.orebfuscator.util.NamespacedKey; import it.unimi.dsi.fastutil.shorts.Short2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.shorts.Short2ObjectMap; -import net.imprex.orebfuscator.config.Config; import net.imprex.orebfuscator.nms.AbstractNmsManager; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; -import net.imprex.orebfuscator.util.NamespacedKey; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; import net.minecraft.core.registries.BuiltInRegistries; @@ -67,8 +70,8 @@ private static ServerPlayer player(Player player) { return ((CraftPlayer) player).getHandle(); } - public NmsManager(Config config) { - super(Block.BLOCK_STATE_REGISTRY.size(), new RegionFileCache(config.cache())); + public NmsManager() { + super(Block.BLOCK_STATE_REGISTRY.size()); for (Map.Entry, Block> entry : BuiltInRegistries.BLOCK.entrySet()) { NamespacedKey namespacedKey = NamespacedKey.fromString(entry.getKey().location().toString()); @@ -78,12 +81,9 @@ public NmsManager(Config config) { BlockProperties.Builder builder = BlockProperties.builder(namespacedKey); for (BlockState blockState : possibleBlockStates) { - Material material = CraftBlockData.fromData(blockState).getMaterial(); - BlockStateProperties properties = BlockStateProperties.builder(Block.getId(blockState)) .withIsAir(blockState.isAir()) - // check if material is occluding and use blockData check for rare edge cases like barrier, spawner, slime_block, ... - .withIsOccluding(material.isOccluding() && blockState.canOcclude()) + .withIsOccluding(blockState.isSolidRender()) .withIsBlockEntity(blockState.hasBlockEntity()) .withIsDefaultState(Objects.equals(block.defaultBlockState(), blockState)) .build(); @@ -91,8 +91,25 @@ public NmsManager(Config config) { builder.withBlockState(properties); } - this.registerBlockProperties(builder.build()); + registerBlockProperties(builder.build()); } + + BuiltInRegistries.BLOCK.getTags().forEach(tag -> { + NamespacedKey namespacedKey = NamespacedKey.fromString(tag.key().location().toString()); + + Set blocks = tag.stream() + .map(holder -> holder.unwrapKey().map(key -> getBlockByName(key.location().toString()))) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toUnmodifiableSet()); + + registerBlockTag(new BlockTag(namespacedKey, blocks)); + }); + } + + @Override + public AbstractRegionFileCache createRegionFileCache(Config config) { + return new RegionFileCache(config.cache()); } @Override @@ -118,18 +135,18 @@ public int getBlockState(World world, int x, int y, int z) { } @Override - public void sendBlockUpdates(World world, Iterable iterable) { + public void sendBlockUpdates(World world, Iterable iterable) { ServerChunkCache serverChunkCache = level(world).getChunkSource(); BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - position.set(pos.x, pos.y, pos.z); + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + position.set(pos.x(), pos.y(), pos.z()); serverChunkCache.blockChanged(position); } } @Override - public void sendBlockUpdates(Player player, Iterable iterable) { + public void sendBlockUpdates(Player player, Iterable iterable) { ServerPlayer serverPlayer = player(player); ServerLevel level = serverPlayer.level(); ServerChunkCache serverChunkCache = level.getChunkSource(); @@ -138,12 +155,12 @@ public void sendBlockUpdates(Player player, Iterable> sectionPackets = new HashMap<>(); List> blockEntityPackets = new ArrayList<>(); - for (net.imprex.orebfuscator.util.BlockPos pos : iterable) { - if (!serverChunkCache.isChunkLoaded(pos.x >> 4, pos.z >> 4)) { + for (dev.imprex.orebfuscator.util.BlockPos pos : iterable) { + if (!serverChunkCache.isChunkLoaded(pos.x() >> 4, pos.z() >> 4)) { continue; } - position.set(pos.x, pos.y, pos.z); + position.set(pos.x(), pos.y(), pos.z()); BlockState blockState = level.getBlockState(position); sectionPackets.computeIfAbsent(SectionPos.of(position), key -> new Short2ObjectLinkedOpenHashMap<>()) diff --git a/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/RegionFileCache.java b/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/RegionFileCache.java index 092f96d01..30ff8e6ad 100644 --- a/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/RegionFileCache.java +++ b/orebfuscator-nms/orebfuscator-nms-v1_21_R5/src/main/java/net/imprex/orebfuscator/nms/v1_21_R5/RegionFileCache.java @@ -8,9 +8,9 @@ import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_21_R5.CraftServer; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFileVersion; @@ -33,12 +33,12 @@ protected void closeRegionFile(RegionFile t) throws IOException { } @Override - protected DataInputStream createInputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataInputStream(new ChunkPos(key.x, key.z)); + protected DataInputStream createInputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataInputStream(new ChunkPos(key.x(), key.z())); } @Override - protected DataOutputStream createOutputStream(RegionFile t, ChunkPosition key) throws IOException { - return t.getChunkDataOutputStream(new ChunkPos(key.x, key.z)); + protected DataOutputStream createOutputStream(RegionFile t, ChunkCacheKey key) throws IOException { + return t.getChunkDataOutputStream(new ChunkPos(key.x(), key.z())); } } \ No newline at end of file diff --git a/orebfuscator-plugin/pom.xml b/orebfuscator-plugin/pom.xml index 6925b175b..d6f39b8cd 100644 --- a/orebfuscator-plugin/pom.xml +++ b/orebfuscator-plugin/pom.xml @@ -53,6 +53,10 @@ net.jpountz net.imprex.shaded.net.jpountz + + org.jetbrains + net.imprex.shaded.org.jetbrains + @@ -87,12 +91,24 @@ ${dependency.bstats.version} compile + + org.joml + joml + ${dependency.joml.version} + compile + org.lz4 lz4-java ${dependency.lz4.version} compile + + org.jetbrains + annotations + ${dependency.jetannotations.version} + compile + @@ -102,12 +118,30 @@ compile - net.imprex - orebfuscator-common + dev.imprex.orebfuscator + orebfuscator-core ${revision} - provided + compile + + + com.google.guava + guava + + + org.yaml + snakeyaml + + + org.joml + joml + + + org.lz4 + lz4-java + + - + net.imprex diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/MetricsSystem.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/MetricsSystem.java index 3989b7ddb..8bf8b08c2 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/MetricsSystem.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/MetricsSystem.java @@ -10,8 +10,8 @@ import org.bstats.charts.SimplePie; import org.bukkit.Bukkit; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; -import net.imprex.orebfuscator.util.MathUtil; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.util.MathUtil; public class MetricsSystem { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java index 6522c2c69..c152b0839 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/Orebfuscator.java @@ -1,5 +1,8 @@ package net.imprex.orebfuscator; +import java.nio.file.Path; +import java.util.List; + import org.bukkit.Bukkit; import org.bukkit.event.Event; import org.bukkit.event.EventPriority; @@ -10,18 +13,24 @@ import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; +import dev.imprex.orebfuscator.chunk.ChunkFactory; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.interop.RegistryAccessor; +import dev.imprex.orebfuscator.interop.ServerAccessor; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.Version; import net.imprex.orebfuscator.api.OrebfuscatorService; import net.imprex.orebfuscator.cache.ObfuscationCache; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; +import net.imprex.orebfuscator.iterop.BukkitLoggerAccessor; +import net.imprex.orebfuscator.iterop.BukkitWorldAccessor; import net.imprex.orebfuscator.obfuscation.ObfuscationSystem; import net.imprex.orebfuscator.player.OrebfuscatorPlayerMap; import net.imprex.orebfuscator.proximity.ProximityDirectorThread; import net.imprex.orebfuscator.proximity.ProximityPacketListener; -import net.imprex.orebfuscator.util.HeightAccessor; import net.imprex.orebfuscator.util.MinecraftVersion; -import net.imprex.orebfuscator.util.OFCLogger; -public class Orebfuscator extends JavaPlugin implements Listener { +public class Orebfuscator extends JavaPlugin implements Listener, ServerAccessor { public static final ThreadGroup THREAD_GROUP = new ThreadGroup("orebfuscator"); @@ -33,10 +42,11 @@ public class Orebfuscator extends JavaPlugin implements Listener { private ObfuscationSystem obfuscationSystem; private ProximityDirectorThread proximityThread; private ProximityPacketListener proximityPacketListener; + private ChunkFactory chunkFactory; @Override public void onLoad() { - OFCLogger.LOGGER = getLogger(); + OfcLogger.setLogger(new BukkitLoggerAccessor(getLogger())); } @Override @@ -53,16 +63,17 @@ public void onEnable() { throw new RuntimeException("ProtocolLib can't be found or is disabled! Orebfuscator can't be enabled."); } + BukkitWorldAccessor.registerListener(this); + this.statistics = new OrebfuscatorStatistics(); // Load configurations + OrebfuscatorNms.initialize(); this.config = new OrebfuscatorConfig(this); + OrebfuscatorCompatibility.initialize(this, config); this.playerMap = new OrebfuscatorPlayerMap(this); - // register cleanup listener - HeightAccessor.registerListener(this); - // Initialize metrics new MetricsSystem(this); @@ -73,6 +84,7 @@ public void onEnable() { this.obfuscationCache = new ObfuscationCache(this); // Load obfuscater + this.chunkFactory = new ChunkFactory(this); this.obfuscationSystem = new ObfuscationSystem(this); // Load proximity hider @@ -98,7 +110,7 @@ public void onEnable() { // add commands getCommand("orebfuscator").setExecutor(new OrebfuscatorCommand(this)); } catch (Exception e) { - OFCLogger.error("An error occurred while enabling plugin", e); + OfcLogger.error("An error occurred while enabling plugin", e); this.getServer().getPluginManager().registerEvent(PluginEnableEvent.class, this, EventPriority.NORMAL, this::onEnableFailed, this); @@ -121,7 +133,6 @@ public void onDisable() { } OrebfuscatorCompatibility.close(); - OrebfuscatorNms.close(); this.config = null; } @@ -162,4 +173,40 @@ public ObfuscationSystem getObfuscationSystem() { public ProximityPacketListener getProximityPacketListener() { return this.proximityPacketListener; } + + public ChunkFactory getChunkFactory() { + return chunkFactory; + } + + @Override + public Path getConfigDirectory() { + return getDataFolder().toPath(); + } + + @Override + public Path getWorldDirectory() { + return Bukkit.getWorldContainer().toPath(); + } + + @Override + public String getOrebfuscatorVersion() { + return getDescription().getVersion(); + } + + @Override + public Version getMinecraftVersion() { + return MinecraftVersion.current(); + } + + @Override + public RegistryAccessor getRegistry() { + return OrebfuscatorNms.registry(); + } + + @Override + public List getWorlds() { + return BukkitWorldAccessor.getWorlds().stream() + .map(WorldAccessor.class::cast) + .toList(); + } } \ No newline at end of file diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorCommand.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorCommand.java index dccdc8d85..f3f2e8c73 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorCommand.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorCommand.java @@ -29,10 +29,10 @@ import com.google.gson.internal.Streams; import com.google.gson.stream.JsonWriter; -import net.imprex.orebfuscator.util.HeightAccessor; -import net.imprex.orebfuscator.util.JavaVersion; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.JavaVersion; +import net.imprex.orebfuscator.iterop.BukkitWorldAccessor; import net.imprex.orebfuscator.util.MinecraftVersion; -import net.imprex.orebfuscator.util.OFCLogger; import net.imprex.orebfuscator.util.PermissionUtil; public class OrebfuscatorCommand implements CommandExecutor, TabCompleter { @@ -94,7 +94,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St for (World bukkitWorld : Bukkit.getWorlds()) { JsonObject world = new JsonObject(); world.addProperty("uuid", bukkitWorld.getUID().toString()); - world.addProperty("heightAccessor", HeightAccessor.get(bukkitWorld).toString()); + world.addProperty("heightAccessor", BukkitWorldAccessor.get(bukkitWorld).toString()); worlds.add(bukkitWorld.getName(), world); } root.add("worlds", worlds); @@ -109,10 +109,12 @@ public boolean onCommand(CommandSender sender, Command command, String label, St listeners.add(key, listener); } root.add("listeners", listeners); + + root.add("blocks", orebfuscator.getOrebfuscatorConfig().toJson()); Base64.Encoder encoder = Base64.getUrlEncoder(); - String latestLog = OFCLogger.getLatestVerboseLog(); + String latestLog = OfcLogger.getLatestVerboseLog(); root.addProperty("verboseLog", encoder.encodeToString(latestLog.getBytes(StandardCharsets.UTF_8))); try { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorStatistics.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorStatistics.java index d786c0083..adbf1bb5f 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorStatistics.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/OrebfuscatorStatistics.java @@ -45,6 +45,8 @@ private static String formatBytes(long bytes) { private LongSupplier obfuscationProcessTime = () -> 0; private LongSupplier proximityWaitTime = () -> 0; private LongSupplier proximityProcessTime = () -> 0; + private LongSupplier originalChunkSize = () -> 0; + private LongSupplier obfuscatedChunkSize = () -> 0; public void onCacheHitMemory() { this.cacheHitCountMemory.incrementAndGet(); @@ -90,6 +92,14 @@ public void setProximityProcessTime(LongSupplier supplier) { this.proximityProcessTime = Objects.requireNonNull(supplier); } + public void setOriginalChunkSize(LongSupplier supplier) { + this.originalChunkSize = Objects.requireNonNull(supplier); + } + + public void setObfuscatedChunkSize(LongSupplier supplier) { + this.obfuscatedChunkSize = Objects.requireNonNull(supplier); + } + @Override public String toString() { long cacheHitCountMemory = this.cacheHitCountMemory.get(); @@ -108,7 +118,7 @@ public String toString() { memoryCacheHitRate = (double) cacheHitCountMemory / totalCacheRequest; diskCacheHitRate = (double) cacheHitCountDisk / totalCacheRequest; } - + long memoryCacheBytesPerEntry = 0; if (memoryCacheSize > 0) { memoryCacheBytesPerEntry = cacheEstimatedSize / memoryCacheSize; @@ -134,9 +144,9 @@ public String toString() { } builder.append(" - obfuscation (wait/process/utilization): ") - .append(formatNanos(obfuscationWaitTime)).append(" | ") - .append(formatNanos(obfuscationProcessTime)).append(" | ") - .append(formatPrecent(obfuscationUtilization)).append('\n'); + .append(formatNanos(obfuscationWaitTime)).append(" | ") + .append(formatNanos(obfuscationProcessTime)).append(" | ") + .append(formatPrecent(obfuscationUtilization)).append('\n'); long proximityWaitTime = this.proximityWaitTime.getAsLong(); long proximityProcessTime = this.proximityProcessTime.getAsLong(); @@ -148,9 +158,22 @@ public String toString() { } builder.append(" - proximity (wait/process/utilization): ") - .append(formatNanos(proximityWaitTime)).append(" | ") - .append(formatNanos(proximityProcessTime)).append(" | ") - .append(formatPrecent(proximityUtilization)).append('\n'); + .append(formatNanos(proximityWaitTime)).append(" | ") + .append(formatNanos(proximityProcessTime)).append(" | ") + .append(formatPrecent(proximityUtilization)).append('\n'); + + long originalChunkSize = this.originalChunkSize.getAsLong(); + long obfuscatedChunkSize = this.obfuscatedChunkSize.getAsLong(); + + double ratio = 1; + if (originalChunkSize > 0) { + ratio = (double) obfuscatedChunkSize / originalChunkSize; + } + + builder.append(" - chunk size (original/obfuscated/ratio): ") + .append(formatBytes(originalChunkSize)).append(" | ") + .append(formatBytes(obfuscatedChunkSize)).append(" | ") + .append(formatPrecent(ratio)).append('\n'); return builder.toString(); } @@ -169,6 +192,8 @@ public JsonObject toJson() { object.addProperty("obfuscationProcessTime", this.obfuscationProcessTime.getAsLong()); object.addProperty("proximityWaitTime", this.proximityWaitTime.getAsLong()); object.addProperty("proximityProcessTime", this.proximityProcessTime.getAsLong()); + object.addProperty("originalChunkSize", this.originalChunkSize.getAsLong()); + object.addProperty("obfuscatedChunkSize", this.obfuscatedChunkSize.getAsLong()); return object; } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/UpdateSystem.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/UpdateSystem.java index 9e0d58f67..c1c573144 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/UpdateSystem.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/UpdateSystem.java @@ -10,7 +10,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import java.util.logging.Level; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -18,12 +17,13 @@ import com.google.gson.annotations.SerializedName; -import net.imprex.orebfuscator.config.GeneralConfig; +import dev.imprex.orebfuscator.config.api.GeneralConfig; +import dev.imprex.orebfuscator.logging.LogLevel; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.Version; import net.imprex.orebfuscator.util.AbstractHttpService; import net.imprex.orebfuscator.util.ConsoleUtil; import net.imprex.orebfuscator.util.MinecraftVersion; -import net.imprex.orebfuscator.util.OFCLogger; -import net.imprex.orebfuscator.util.Version; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.ComponentBuilder; @@ -62,7 +62,7 @@ public UpdateSystem(Orebfuscator orebfuscator) { private CompletableFuture> requestLatestVersion() { String installedVersion = this.orebfuscator.getDescription().getVersion(); if (!this.generalConfig.checkForUpdates() || isDevVersion(installedVersion)) { - OFCLogger.debug("UpdateSystem - Update check disabled or dev version detected; skipping"); + OfcLogger.debug("UpdateSystem - Update check disabled or dev version detected; skipping"); return CompletableFuture.completedFuture(Optional.empty()); } @@ -77,13 +77,13 @@ private CompletableFuture> requestLatestVersion() { .findFirst(); latestVersion.ifPresentOrElse( - v -> OFCLogger.debug("UpdateSystem - Fetched latest version " + v.version), - () -> OFCLogger.debug("UpdateSystem - Couldn't fetch latest version")); + v -> OfcLogger.debug("UpdateSystem - Fetched latest version " + v.version), + () -> OfcLogger.debug("UpdateSystem - Couldn't fetch latest version")); return latestVersion.map(v -> version.isBelow(v.version) ? v : null); }) ).exceptionally(throwable -> { - OFCLogger.log(Level.WARNING, "UpdateSystem - Unable to fetch latest version", throwable); + OfcLogger.log(LogLevel.WARN, "UpdateSystem - Unable to fetch latest version", throwable); return Optional.empty(); }); } @@ -91,7 +91,7 @@ private CompletableFuture> requestLatestVersion() { private CompletableFuture> getLatestVersion() { Instant validUntil = this.validUntil.get(); if (validUntil != null && validUntil.compareTo(Instant.now()) < 0 && this.validUntil.compareAndSet(validUntil, null)) { - OFCLogger.debug("UpdateSystem - Cleared latest cached version"); + OfcLogger.debug("UpdateSystem - Cleared latest cached version"); this.latestVersion.set(null); } @@ -102,7 +102,7 @@ private CompletableFuture> getLatestVersion() { CompletableFuture> newFuture = new CompletableFuture<>(); if (this.latestVersion.compareAndSet(null, newFuture)) { - OFCLogger.debug("UpdateSystem - Starting to check for updates"); + OfcLogger.debug("UpdateSystem - Starting to check for updates"); this.requestLatestVersion().thenAccept(version -> { this.validUntil.set(Instant.now().plus(CACHE_DURATION)); newFuture.complete(version); @@ -120,7 +120,7 @@ private void ifNewerVersionAvailable(Consumer consumer) { private void checkForUpdates() { this.ifNewerVersionAvailable(version -> { String downloadUri = String.format(DOWNLOAD_URI, version.version); - ConsoleUtil.printBox(Level.WARNING, "UPDATE AVAILABLE", "", downloadUri); + ConsoleUtil.printBox(LogLevel.WARN, "UPDATE AVAILABLE", "", downloadUri); }); } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java index ff1fe9a05..d51bf4d6f 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/AsyncChunkSerializer.java @@ -10,8 +10,12 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import org.jetbrains.annotations.NotNull; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.util.ChunkPosition; /** * This class works similar to a bounded buffer for cache read and write @@ -30,34 +34,38 @@ public class AsyncChunkSerializer implements Runnable { private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); - private final Map tasks = new HashMap<>(); - private final Queue positions = new LinkedList<>(); + private final Map tasks = new HashMap<>(); + private final Queue positions = new LinkedList<>(); + private final int maxTaskQueueSize; + private final ChunkSerializer serializer; private final Thread thread; private volatile boolean running = true; - public AsyncChunkSerializer(Orebfuscator orebfuscator) { + public AsyncChunkSerializer(Orebfuscator orebfuscator, AbstractRegionFileCache regionFileCache) { this.maxTaskQueueSize = orebfuscator.getOrebfuscatorConfig().cache().maximumTaskQueueSize(); + this.serializer = new ChunkSerializer(regionFileCache); this.thread = new Thread(Orebfuscator.THREAD_GROUP, this, "ofc-chunk-serializer"); this.thread.setDaemon(true); this.thread.start(); - orebfuscator.getStatistics().setDiskCacheQueueLengthSupplier(() -> this.tasks.size()); + orebfuscator.getStatistics().setDiskCacheQueueLengthSupplier(this.tasks::size); } - public CompletableFuture read(ChunkPosition position) { + @NotNull + public CompletableFuture read(@NotNull ChunkCacheKey key) { this.lock.lock(); try { - Runnable task = this.tasks.get(position); + Runnable task = this.tasks.get(key); if (task instanceof WriteTask) { return CompletableFuture.completedFuture(((WriteTask) task).chunk); } else if (task instanceof ReadTask) { return ((ReadTask) task).future; } else { - CompletableFuture future = new CompletableFuture<>(); - this.queueTask(position, new ReadTask(position, future)); + CompletableFuture future = new CompletableFuture<>(); + this.queueTask(key, new ReadTask(key, future)); return future; } } finally { @@ -65,10 +73,10 @@ public CompletableFuture read(ChunkPosition positio } } - public void write(ChunkPosition position, CompressedObfuscationResult chunk) { + public void write(@NotNull ChunkCacheKey key, @NotNull CacheChunkEntry chunk) { this.lock.lock(); try { - Runnable prevTask = this.queueTask(position, new WriteTask(position, chunk)); + Runnable prevTask = this.queueTask(key, new WriteTask(key, chunk)); if (prevTask instanceof ReadTask) { ((ReadTask) prevTask).future.complete(chunk); } @@ -77,7 +85,8 @@ public void write(ChunkPosition position, CompressedObfuscationResult chunk) { } } - private Runnable queueTask(ChunkPosition position, Runnable nextTask) { + @NotNull + private Runnable queueTask(@NotNull ChunkCacheKey key, @NotNull Runnable nextTask) { while (this.positions.size() >= this.maxTaskQueueSize) { this.notFull.awaitUninterruptibly(); } @@ -86,9 +95,9 @@ private Runnable queueTask(ChunkPosition position, Runnable nextTask) { throw new IllegalStateException("AsyncChunkSerializer already closed"); } - Runnable prevTask = this.tasks.put(position, nextTask); + Runnable prevTask = this.tasks.put(key, nextTask); if (prevTask == null) { - this.positions.offer(position); + this.positions.offer(key); } this.notEmpty.signal(); @@ -133,37 +142,39 @@ public void close() { } private class WriteTask implements Runnable { - private final ChunkPosition position; - private final CompressedObfuscationResult chunk; - public WriteTask(ChunkPosition position, CompressedObfuscationResult chunk) { - this.position = position; + private final ChunkCacheKey key; + private final CacheChunkEntry chunk; + + public WriteTask(ChunkCacheKey key, CacheChunkEntry chunk) { + this.key = key; this.chunk = chunk; } @Override public void run() { try { - ChunkSerializer.write(position, chunk); + serializer.write(key, chunk); } catch (IOException e) { - e.printStackTrace(); + OfcLogger.error(e); } } } private class ReadTask implements Runnable { - private final ChunkPosition position; - private final CompletableFuture future; - public ReadTask(ChunkPosition position, CompletableFuture future) { - this.position = position; + private final ChunkCacheKey key; + private final CompletableFuture future; + + public ReadTask(ChunkCacheKey key, CompletableFuture future) { + this.key = key; this.future = future; } @Override public void run() { try { - future.complete(ChunkSerializer.read(position)); + future.complete(serializer.read(key)); } catch (IOException e) { future.completeExceptionally(e); } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CompressedObfuscationResult.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheChunkEntry.java similarity index 79% rename from orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CompressedObfuscationResult.java rename to orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheChunkEntry.java index 3505c34b2..d6b746a52 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CompressedObfuscationResult.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheChunkEntry.java @@ -9,16 +9,16 @@ import java.util.Collection; import java.util.Optional; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.obfuscation.ObfuscationRequest; import net.imprex.orebfuscator.obfuscation.ObfuscationResult; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.ChunkPosition; import net.jpountz.lz4.LZ4BlockInputStream; import net.jpountz.lz4.LZ4BlockOutputStream; -public class CompressedObfuscationResult { +public class CacheChunkEntry { - public static CompressedObfuscationResult create(ObfuscationResult result) { + public static CacheChunkEntry create(ObfuscationResult result) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try ( @@ -43,18 +43,18 @@ public static CompressedObfuscationResult create(ObfuscationResult result) { dataOutputStream.writeInt(blockPosition.toSectionPos()); } } catch (Exception e) { - new IOException("Unable to compress chunk: " + result.getPosition(), e).printStackTrace(); + new IOException("Unable to compress chunk: " + result.getCacheKey(), e).printStackTrace(); return null; } - return new CompressedObfuscationResult(result.getPosition(), byteArrayOutputStream.toByteArray()); + return new CacheChunkEntry(result.getCacheKey(), byteArrayOutputStream.toByteArray()); } - private final ChunkPosition position; + private final ChunkCacheKey key; private final byte[] compressedData; - public CompressedObfuscationResult(ChunkPosition position, byte[] data) { - this.position = position; + public CacheChunkEntry(ChunkCacheKey key, byte[] data) { + this.key = key; this.compressedData = data; } @@ -86,10 +86,10 @@ public Optional toResult() { byte[] data = new byte[dataInputStream.readInt()]; dataInputStream.readFully(data); - ObfuscationResult result = new ObfuscationResult(this.position, hash, data); + ObfuscationResult result = new ObfuscationResult(this.key, hash, data); - int x = this.position.x << 4; - int z = this.position.z << 4; + int x = this.key.x() << 4; + int z = this.key.z() << 4; Collection proximityBlocks = result.getProximityBlocks(); for (int i = dataInputStream.readInt(); i > 0; i--) { @@ -103,7 +103,7 @@ public Optional toResult() { return Optional.of(result); } catch (Exception e) { - new IOException("Unable to decompress chunk: " + this.position, e).printStackTrace(); + new IOException("Unable to decompress chunk: " + this.key, e).printStackTrace(); return Optional.empty(); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheFileCleanupTask.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheFileCleanupTask.java index 3312b6270..84c0c0ffc 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheFileCleanupTask.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/CacheFileCleanupTask.java @@ -7,31 +7,31 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.logging.OfcLogger; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.CacheConfig; -import net.imprex.orebfuscator.nms.AbstractRegionFileCache; -import net.imprex.orebfuscator.util.OFCLogger; public class CacheFileCleanupTask implements Runnable { private final CacheConfig cacheConfig; + private final AbstractRegionFileCache regionFileCache; private int deleteCount = 0; - public CacheFileCleanupTask(Orebfuscator orebfuscator) { + public CacheFileCleanupTask(Orebfuscator orebfuscator, AbstractRegionFileCache regionFileCache) { this.cacheConfig = orebfuscator.getOrebfuscatorConfig().cache(); + this.regionFileCache = regionFileCache; } @Override public void run() { if (Files.notExists(this.cacheConfig.baseDirectory())) { - OFCLogger.debug("Skipping CacheFileCleanupTask as the cache directory doesn't exist."); + OfcLogger.debug("Skipping CacheFileCleanupTask as the cache directory doesn't exist."); return; } long deleteAfterMillis = this.cacheConfig.deleteRegionFilesAfterAccess(); - AbstractRegionFileCache regionFileCache = OrebfuscatorNms.getRegionFileCache(); this.deleteCount = 0; @@ -45,7 +45,7 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) thro Files.delete(path); CacheFileCleanupTask.this.deleteCount++; - OFCLogger.debug("deleted cache file: " + path); + OfcLogger.debug("deleted cache file: " + path); } return FileVisitResult.CONTINUE; } @@ -55,7 +55,7 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes attributes) thro } if (this.deleteCount > 0) { - OFCLogger.info(String.format("CacheFileCleanupTask successfully deleted %d cache file(s)", this.deleteCount)); + OfcLogger.info(String.format("CacheFileCleanupTask successfully deleted %d cache file(s)", this.deleteCount)); } } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java index e6ed4bb89..67c1ef87f 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ChunkSerializer.java @@ -4,23 +4,25 @@ import java.io.DataOutputStream; import java.io.IOException; -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.util.ChunkPosition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.util.ChunkCacheKey; public class ChunkSerializer { private static final int CACHE_VERSION = 2; - private static DataInputStream createInputStream(ChunkPosition key) throws IOException { - return OrebfuscatorNms.getRegionFileCache().createInputStream(key); - } + private final AbstractRegionFileCache regionFileCache; - private static DataOutputStream createOutputStream(ChunkPosition key) throws IOException { - return OrebfuscatorNms.getRegionFileCache().createOutputStream(key); + public ChunkSerializer(AbstractRegionFileCache regionFileCache) { + this.regionFileCache = regionFileCache; } - public static CompressedObfuscationResult read(ChunkPosition key) throws IOException { - try (DataInputStream dataInputStream = createInputStream(key)) { + @Nullable + public CacheChunkEntry read(@NotNull ChunkCacheKey key) throws IOException { + try (DataInputStream dataInputStream = this.regionFileCache.createInputStream(key)) { if (dataInputStream != null) { // check if cache entry has right version and if chunk is present if (dataInputStream.readInt() != CACHE_VERSION || !dataInputStream.readBoolean()) { @@ -30,7 +32,7 @@ public static CompressedObfuscationResult read(ChunkPosition key) throws IOExcep byte[] compressedData = new byte[dataInputStream.readInt()]; dataInputStream.readFully(compressedData); - return new CompressedObfuscationResult(key, compressedData); + return new CacheChunkEntry(key, compressedData); } } catch (IOException e) { throw new IOException("Unable to read chunk: " + key, e); @@ -39,8 +41,8 @@ public static CompressedObfuscationResult read(ChunkPosition key) throws IOExcep return null; } - public static void write(ChunkPosition key, CompressedObfuscationResult value) throws IOException { - try (DataOutputStream dataOutputStream = createOutputStream(key)) { + public void write(@NotNull ChunkCacheKey key, @Nullable CacheChunkEntry value) throws IOException { + try (DataOutputStream dataOutputStream = this.regionFileCache.createOutputStream(key)) { dataOutputStream.writeInt(CACHE_VERSION); if (value != null) { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ObfuscationCache.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ObfuscationCache.java index 2e8649160..913ac52d6 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ObfuscationCache.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/cache/ObfuscationCache.java @@ -3,24 +3,29 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import org.jetbrains.annotations.NotNull; + import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.cache.RemovalNotification; +import dev.imprex.orebfuscator.cache.AbstractRegionFileCache; +import dev.imprex.orebfuscator.config.api.CacheConfig; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.OrebfuscatorCompatibility; +import net.imprex.orebfuscator.OrebfuscatorNms; import net.imprex.orebfuscator.OrebfuscatorStatistics; -import net.imprex.orebfuscator.config.CacheConfig; import net.imprex.orebfuscator.obfuscation.ObfuscationRequest; import net.imprex.orebfuscator.obfuscation.ObfuscationResult; -import net.imprex.orebfuscator.util.ChunkPosition; public class ObfuscationCache { private final CacheConfig cacheConfig; private final OrebfuscatorStatistics statistics; - private final Cache cache; + private final AbstractRegionFileCache regionFileCache; + private final Cache cache; private final AsyncChunkSerializer serializer; public ObfuscationCache(Orebfuscator orebfuscator) { @@ -34,18 +39,20 @@ public ObfuscationCache(Orebfuscator orebfuscator) { .build(); this.statistics.setMemoryCacheSizeSupplier(() -> this.cache.size()); + this.regionFileCache = OrebfuscatorNms.createRegionFileCache(orebfuscator.getOrebfuscatorConfig()); + if (this.cacheConfig.enableDiskCache()) { - this.serializer = new AsyncChunkSerializer(orebfuscator); + this.serializer = new AsyncChunkSerializer(orebfuscator, regionFileCache); } else { this.serializer = null; } if (this.cacheConfig.enabled() && this.cacheConfig.deleteRegionFilesAfterAccess() > 0) { - OrebfuscatorCompatibility.runAsyncAtFixedRate(new CacheFileCleanupTask(orebfuscator), 0, 72000L); + OrebfuscatorCompatibility.runAsyncAtFixedRate(new CacheFileCleanupTask(orebfuscator, regionFileCache), 0, 72000L); } } - private void onRemoval(RemovalNotification notification) { + private void onRemoval(@NotNull RemovalNotification notification) { this.statistics.onCacheSizeChange(-notification.getValue().estimatedSize()); // don't serialize invalidated chunks since this would require locking the main @@ -55,20 +62,21 @@ private void onRemoval(RemovalNotification { - var compressedChunk = CompressedObfuscationResult.create(chunk); + var compressedChunk = CacheChunkEntry.create(chunk); if (compressedChunk != null) { - this.cache.put(request.getPosition(), compressedChunk); + this.cache.put(request.getCacheKey(), compressedChunk); this.statistics.onCacheSizeChange(compressedChunk.estimatedSize()); } }); } - public CompletableFuture get(ObfuscationRequest request) { - ChunkPosition key = request.getPosition(); + @NotNull + public CompletableFuture get(@NotNull ObfuscationRequest request) { + ChunkCacheKey key = request.getCacheKey(); - CompressedObfuscationResult cacheChunk = this.cache.getIfPresent(key); + CacheChunkEntry cacheChunk = this.cache.getIfPresent(key); if (cacheChunk != null && cacheChunk.isValid(request)) { this.statistics.onCacheHitMemory(); @@ -114,12 +122,12 @@ else if (cacheChunk == null && this.cacheConfig.enableDiskCache()) { return request.getFuture(); } - public void invalidate(ChunkPosition key) { + public void invalidate(ChunkCacheKey key) { this.cache.invalidate(key); } public void close() { - if (this.cacheConfig.enableDiskCache()) { + if (this.serializer != null) { // flush memory cache to disk on shutdown this.cache.asMap().entrySet().removeIf(entry -> { this.serializer.write(entry.getKey(), entry.getValue()); @@ -128,5 +136,7 @@ public void close() { this.serializer.close(); } + + this.regionFileCache.clear(); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ByteBufUtil.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ByteBufUtil.java deleted file mode 100644 index 8dbd842f1..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ByteBufUtil.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import io.netty.buffer.ByteBuf; - -public class ByteBufUtil { - - public static int getVarIntSize(int value) { - for (int bytes = 1; bytes < 5; bytes++) { - if ((value & -1 << bytes * 7) == 0) { - return bytes; - } - } - return 5; - } - - public static void skipVarInt(ByteBuf buffer) { - int bytes = 0; - byte in; - do { - in = buffer.readByte(); - if (++bytes > 5) { - throw new IndexOutOfBoundsException("varint32 too long"); - } - } while ((in & 0x80) != 0); - } - - public static int readVarInt(ByteBuf buffer) { - int out = 0; - int bytes = 0; - byte in; - do { - in = buffer.readByte(); - out |= (in & 0x7F) << bytes++ * 7; - if (bytes > 5) { - throw new IndexOutOfBoundsException("varint32 too long"); - } - } while ((in & 0x80) != 0); - return out; - } - - public static void writeVarInt(ByteBuf buffer, int value) { - while ((value & -0x80) != 0) { - buffer.writeByte(value & 0x7F | 0x80); - value >>>= 7; - } - buffer.writeByte(value); - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/Chunk.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/Chunk.java deleted file mode 100644 index f712a3025..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/Chunk.java +++ /dev/null @@ -1,138 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import java.util.Arrays; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; -import io.netty.buffer.Unpooled; -import net.imprex.orebfuscator.util.HeightAccessor; - -public class Chunk implements AutoCloseable { - - public static Chunk fromChunkStruct(ChunkStruct chunkStruct) { - return new Chunk(chunkStruct); - } - - private final int chunkX; - private final int chunkZ; - - private final HeightAccessor heightAccessor; - private final ChunkSectionHolder[] sections; - - private final ByteBuf inputBuffer; - private final ByteBuf outputBuffer; - - private Chunk(ChunkStruct chunkStruct) { - this.chunkX = chunkStruct.chunkX; - this.chunkZ = chunkStruct.chunkZ; - - this.heightAccessor = HeightAccessor.get(chunkStruct.world); - this.sections = new ChunkSectionHolder[this.heightAccessor.getSectionCount()]; - - this.inputBuffer = Unpooled.wrappedBuffer(chunkStruct.data); - this.outputBuffer = PooledByteBufAllocator.DEFAULT.heapBuffer(chunkStruct.data.length); - - for (int sectionIndex = 0; sectionIndex < this.sections.length; sectionIndex++) { - if (chunkStruct.sectionMask.get(sectionIndex)) { - this.sections[sectionIndex] = new ChunkSectionHolder(); - } - } - } - - public int getSectionCount() { - return this.sections.length; - } - - public HeightAccessor getHeightAccessor() { - return heightAccessor; - } - - public ChunkSection getSection(int index) { - ChunkSectionHolder chunkSection = this.sections[index]; - if (chunkSection != null) { - return chunkSection.chunkSection; - } - return null; - } - - public int getBlockState(int x, int y, int z) { - if (x >> 4 == this.chunkX && z >> 4 == this.chunkZ) { - ChunkSectionHolder chunkSection = this.sections[this.heightAccessor.getSectionIndex(y)]; - if (chunkSection != null) { - return chunkSection.data[ChunkSection.positionToIndex(x & 0xF, y & 0xF, z & 0xF)]; - } - return 0; - } - - return -1; - } - - public byte[] finalizeOutput() { - for (ChunkSectionHolder chunkSection : this.sections) { - if (chunkSection != null) { - chunkSection.write(); - } - } - this.outputBuffer.writeBytes(this.inputBuffer); - return Arrays.copyOfRange(this.outputBuffer.array(), this.outputBuffer.arrayOffset(), - this.outputBuffer.arrayOffset() + this.outputBuffer.readableBytes()); - } - - @Override - public void close() throws Exception { - this.inputBuffer.release(); - this.outputBuffer.release(); - } - - private void skipBiomePalettedContainer() { - int bitsPerValue = this.inputBuffer.readUnsignedByte(); - - if (bitsPerValue == 0) { - ByteBufUtil.readVarInt(this.inputBuffer); - } else if (bitsPerValue <= 3) { - for (int i = ByteBufUtil.readVarInt(this.inputBuffer); i > 0; i--) { - ByteBufUtil.readVarInt(this.inputBuffer); - } - } - - int expectedDataLength = SimpleVarBitBuffer.calculateArraySize(bitsPerValue, 64); - - if (ChunkCapabilities.hasLongArrayLengthField()) { - int dataLength = ByteBufUtil.readVarInt(this.inputBuffer); - if (expectedDataLength != dataLength) { - throw new IndexOutOfBoundsException("data.length != VarBitBuffer::size " + dataLength + " " + expectedDataLength); - } - } - - this.inputBuffer.skipBytes(Long.BYTES * expectedDataLength); - } - - private class ChunkSectionHolder { - - public ChunkSection chunkSection; - - public final int[] data; - public final int extraOffset; - - private int extraBytes; - - public ChunkSectionHolder() { - this.chunkSection = new ChunkSection(); - - this.data = this.chunkSection.read(inputBuffer); - this.extraOffset = inputBuffer.readerIndex(); - - if (ChunkCapabilities.hasBiomePalettedContainer()) { - skipBiomePalettedContainer(); - this.extraBytes = inputBuffer.readerIndex() - this.extraOffset; - } - } - - public void write() { - this.chunkSection.write(outputBuffer); - if (this.extraBytes > 0) { - outputBuffer.writeBytes(inputBuffer, this.extraOffset, extraBytes); - } - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkCapabilities.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkCapabilities.java deleted file mode 100644 index 7f16ecb20..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkCapabilities.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import net.imprex.orebfuscator.util.MinecraftVersion; - -public final class ChunkCapabilities { - - // hasLongArrayLengthField < 1.21.5 - // hasChunkPosFieldUnloadPacket >= 1.20.2 - // hasClientboundLevelChunkPacketData >= 1.18; - // hasBiomePalettedContainer >= 1.18 - // hasSingleValuePalette >= 1.18 - // hasHeightBitMask < 1.18 - // hasDynamicHeight >= 1.17 - - private static final boolean hasLongArrayLengthField = MinecraftVersion.isBelow("1.21.5"); - private static final boolean hasChunkPosFieldUnloadPacket = MinecraftVersion.isAtOrAbove("1.20.2"); - private static final boolean hasClientboundLevelChunkPacketData = MinecraftVersion.isAtOrAbove("1.18"); - private static final boolean hasBiomePalettedContainer = MinecraftVersion.isAtOrAbove("1.18"); - private static final boolean hasSingleValuePalette = MinecraftVersion.isAtOrAbove("1.18"); - private static final boolean hasHeightBitMask = MinecraftVersion.isBelow("1.18"); - private static final boolean hasDynamicHeight = MinecraftVersion.isAtOrAbove("1.17"); - - private ChunkCapabilities() { - } - - public static boolean hasLongArrayLengthField() { - return hasLongArrayLengthField; - } - - public static boolean hasChunkPosFieldUnloadPacket() { - return hasChunkPosFieldUnloadPacket; - } - - public static boolean hasClientboundLevelChunkPacketData() { - return hasClientboundLevelChunkPacketData; - } - - public static boolean hasBiomePalettedContainer() { - return hasBiomePalettedContainer; - } - - public static boolean hasSingleValuePalette() { - return hasSingleValuePalette; - } - - public static boolean hasHeightBitMask() { - return hasHeightBitMask; - } - - public static boolean hasDynamicHeight() { - return hasDynamicHeight; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkSection.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkSection.java deleted file mode 100644 index 8010c5227..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkSection.java +++ /dev/null @@ -1,136 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import io.netty.buffer.ByteBuf; -import net.imprex.orebfuscator.OrebfuscatorNms; - -public class ChunkSection { - - private int blockCount; - private int bitsPerBlock = -1; - - private Palette palette; - private VarBitBuffer data; - - public ChunkSection() { - this.setBitsPerBlock(0, true); - } - - private void setBitsPerBlock(int bitsPerBlock, boolean grow) { - if (this.bitsPerBlock != bitsPerBlock) { - if (ChunkCapabilities.hasSingleValuePalette() && bitsPerBlock == 0) { - this.bitsPerBlock = 0; - this.palette = new SingleValuePalette(this, 0); - } else if (!grow && bitsPerBlock == 1) { - // fix: fawe chunk format incompatibility with bitsPerBlock == 1 - // https://github.com/Imprex-Development/Orebfuscator/issues/36 - this.bitsPerBlock = bitsPerBlock; - this.palette = new IndirectPalette(this.bitsPerBlock, this); - } else if (bitsPerBlock <= 8) { - this.bitsPerBlock = Math.max(4, bitsPerBlock); - this.palette = new IndirectPalette(this.bitsPerBlock, this); - } else { - this.bitsPerBlock = OrebfuscatorNms.getMaxBitsPerBlockState(); - this.palette = new DirectPalette(); - } - - if (this.bitsPerBlock == 0) { - this.data = new ZeroVarBitBuffer(4096); - } else { - this.data = new SimpleVarBitBuffer(this.bitsPerBlock, 4096); - } - } - } - - int grow(int bitsPerBlock, int blockId) { - Palette palette = this.palette; - VarBitBuffer data = this.data; - - this.setBitsPerBlock(bitsPerBlock, true); - - for (int i = 0; i < data.size(); i++) { - int preBlockId = palette.valueFor(data.get(i)); - this.data.set(i, this.palette.idFor(preBlockId)); - } - - return this.palette.idFor(blockId); - } - - static int positionToIndex(int x, int y, int z) { - return y << 8 | z << 4 | x; - } - - public void setBlockState(int x, int y, int z, int blockId) { - this.setBlockState(positionToIndex(x, y, z), blockId); - } - - public void setBlockState(int index, int blockId) { - int prevBlockId = this.getBlockState(index); - - if (!OrebfuscatorNms.isAir(prevBlockId)) { - --this.blockCount; - } - - if (!OrebfuscatorNms.isAir(blockId)) { - ++this.blockCount; - } - - int paletteIndex = this.palette.idFor(blockId); - this.data.set(index, paletteIndex); - } - - public int getBlock(int x, int y, int z) { - return this.getBlockState(positionToIndex(x, y, z)); - } - - public int getBlockState(int index) { - return this.palette.valueFor(this.data.get(index)); - } - - public boolean isEmpty() { - return this.blockCount == 0; - } - - public void write(ByteBuf buffer) { - buffer.writeShort(this.blockCount); - - buffer.writeByte(this.bitsPerBlock); - this.palette.write(buffer); - - long[] data = this.data.toArray(); - - if (ChunkCapabilities.hasLongArrayLengthField()) { - ByteBufUtil.writeVarInt(buffer, data.length); - } - - for (long entry : data) { - buffer.writeLong(entry); - } - } - - public int[] read(ByteBuf buffer) { - this.blockCount = buffer.readShort(); - - this.setBitsPerBlock(buffer.readUnsignedByte(), false); - - this.palette.read(buffer); - - long[] data = this.data.toArray(); - - if (ChunkCapabilities.hasLongArrayLengthField()) { - int length = ByteBufUtil.readVarInt(buffer); - if (data.length != length) { - throw new IndexOutOfBoundsException("data.length != VarBitBuffer::size " + length + " " + this.data); - } - } - - for (int i = 0; i < data.length; i++) { - data[i] = buffer.readLong(); - } - - int[] directData = new int[4096]; - for (int i = 0; i < directData.length; i++) { - directData[i] = this.getBlockState(i); - } - return directData; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/DirectPalette.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/DirectPalette.java deleted file mode 100644 index 75bc0bd09..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/DirectPalette.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import io.netty.buffer.ByteBuf; - -public class DirectPalette implements Palette { - - @Override - public int idFor(int value) { - return value; - } - - @Override - public int valueFor(int id) { - return id; - } - - @Override - public void read(ByteBuf buffer) { - } - - @Override - public void write(ByteBuf buffer) { - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/IndirectPalette.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/IndirectPalette.java deleted file mode 100644 index be579c719..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/IndirectPalette.java +++ /dev/null @@ -1,71 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import java.util.Arrays; - -import io.netty.buffer.ByteBuf; -import net.imprex.orebfuscator.OrebfuscatorNms; - -public class IndirectPalette implements Palette { - - private final int bitsPerValue; - private final ChunkSection chunkSection; - - private final byte[] byValue; - private final int[] byId; - - private int size = 0; - - public IndirectPalette(int bitsPerValue, ChunkSection chunkSection) { - this.bitsPerValue = bitsPerValue; - this.chunkSection = chunkSection; - - // TODO improve block to index - this.byValue = new byte[OrebfuscatorNms.getUniqueBlockStateCount()]; - Arrays.fill(this.byValue, (byte) 0xFF); - this.byId = new int[1 << bitsPerValue]; - } - - @Override - public int idFor(int value) { - int id = this.byValue[value] & 0xFF; - if (id == 0xFF) { - id = this.size++; - - if (id != 0xFF && id < this.byId.length) { - this.byValue[value] = (byte) id; - this.byId[id] = value; - } else { - id = this.chunkSection.grow(this.bitsPerValue + 1, value); - } - } - return id; - } - - @Override - public int valueFor(int id) { - if (id < 0 || id >= this.size) { - throw new IndexOutOfBoundsException(); - } else { - return this.byId[id]; - } - } - - @Override - public void read(ByteBuf buffer) { - this.size = ByteBufUtil.readVarInt(buffer); - for (int id = 0; id < size; id++) { - int value = ByteBufUtil.readVarInt(buffer); - this.byId[id] = value; - this.byValue[value] = (byte) id; - } - } - - @Override - public void write(ByteBuf buffer) { - ByteBufUtil.writeVarInt(buffer, this.size); - - for (int id = 0; id < this.size; id++) { - ByteBufUtil.writeVarInt(buffer, this.valueFor(id)); - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/Palette.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/Palette.java deleted file mode 100644 index 651702fa8..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/Palette.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import io.netty.buffer.ByteBuf; - -public interface Palette { - - int idFor(int value); - - int valueFor(int id); - - void read(ByteBuf buffer); - - void write(ByteBuf buffer); - -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/SimpleVarBitBuffer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/SimpleVarBitBuffer.java deleted file mode 100644 index 2ee77bd67..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/SimpleVarBitBuffer.java +++ /dev/null @@ -1,50 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -public class SimpleVarBitBuffer implements VarBitBuffer { - - public static int calculateArraySize(int bitsPerEntry, int size) { - return bitsPerEntry == 0 ? 0 : (int) Math.ceil((float) size / (64 / bitsPerEntry)); - } - - private final int bitsPerEntry; - private final int entriesPerLong; - private final long adjustmentMask; - - private final int size; - private final long[] buffer; - - public SimpleVarBitBuffer(int bitsPerEntry, int size) { - this.bitsPerEntry = bitsPerEntry; - this.entriesPerLong = 64 / bitsPerEntry; - this.adjustmentMask = (1L << bitsPerEntry) - 1L; - - this.size = size; - this.buffer = new long[(int) Math.ceil((float) size / this.entriesPerLong)]; - } - - public int get(int index) { - int position = index / this.entriesPerLong; - int offset = (index - position * this.entriesPerLong) * this.bitsPerEntry; - return (int) (this.buffer[position] >> offset & this.adjustmentMask); - } - - public void set(int index, int value) { - int position = index / this.entriesPerLong; - int offset = (index - position * this.entriesPerLong) * this.bitsPerEntry; - this.buffer[position] = this.buffer[position] & ~(this.adjustmentMask << offset) - | (value & this.adjustmentMask) << offset; - } - - public long[] toArray() { - return this.buffer; - } - - public int size() { - return this.size; - } - - @Override - public String toString() { - return String.format("[size=%d, length=%d, bitsPerEntry=%d, entriesPerLong=%d]", size, buffer.length, bitsPerEntry, entriesPerLong); - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/SingleValuePalette.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/SingleValuePalette.java deleted file mode 100644 index 1e5036af2..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/SingleValuePalette.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -import io.netty.buffer.ByteBuf; - -public class SingleValuePalette implements Palette { - - private final ChunkSection chunkSection; - - private int value = -1; - - public SingleValuePalette(ChunkSection chunkSection, int value) { - this.chunkSection = chunkSection; - this.value = value; - } - - @Override - public int idFor(int value) { - if (this.value != -1 && value != this.value) { - return this.chunkSection.grow(1, value); - } else { - this.value = value; - return 0; - } - } - - @Override - public int valueFor(int id) { - if (this.value != -1 && id == 0) { - return this.value; - } else { - throw new IllegalStateException("value isn't initialized"); - } - } - - @Override - public void read(ByteBuf buffer) { - this.value = ByteBufUtil.readVarInt(buffer); - } - - @Override - public void write(ByteBuf buffer) { - if (this.value == -1) { - throw new IllegalStateException("value isn't initialized"); - } else { - ByteBufUtil.writeVarInt(buffer, this.value); - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/VarBitBuffer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/VarBitBuffer.java deleted file mode 100644 index 651618552..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/VarBitBuffer.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -public interface VarBitBuffer { - - int get(int index); - - void set(int index, int value); - - long[] toArray(); - - int size(); -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ZeroVarBitBuffer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ZeroVarBitBuffer.java deleted file mode 100644 index c5ddf752c..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ZeroVarBitBuffer.java +++ /dev/null @@ -1,34 +0,0 @@ -package net.imprex.orebfuscator.chunk; - -public class ZeroVarBitBuffer implements VarBitBuffer { - - public static final long[] EMPTY = new long[0]; - - private final int size; - - public ZeroVarBitBuffer(int size) { - this.size = size; - } - - @Override - public int get(int index) { - return 0; - } - - @Override - public void set(int index, int value) { - if (value != 0) { - throw new IllegalArgumentException("ZeroVarBitBuffer can't hold any value"); - } - } - - @Override - public long[] toArray() { - return EMPTY; - } - - @Override - public int size() { - return this.size; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/AbstractWorldConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/AbstractWorldConfig.java deleted file mode 100644 index 65eac3d10..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/AbstractWorldConfig.java +++ /dev/null @@ -1,136 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.config.components.WeightedBlockList; -import net.imprex.orebfuscator.config.components.WorldMatcher; -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.HeightAccessor; -import net.imprex.orebfuscator.util.MathUtil; -import net.imprex.orebfuscator.util.OFCLogger; -import net.imprex.orebfuscator.util.WeightedIntRandom; - -public abstract class AbstractWorldConfig implements WorldConfig { - - private final String name; - - protected boolean enabledValue = false; - protected boolean enabled = false; - - protected int minY = BlockPos.MIN_Y; - protected int maxY = BlockPos.MAX_Y; - - protected final List worldMatchers = new ArrayList<>(); - protected final List weightedBlockLists = new ArrayList<>(); - - public AbstractWorldConfig(String name) { - this.name = name; - } - - protected void deserializeBase(ConfigurationSection section) { - this.enabledValue = section.getBoolean("enabled", true); - - int minY = MathUtil.clamp(section.getInt("minY", BlockPos.MIN_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); - int maxY = MathUtil.clamp(section.getInt("maxY", BlockPos.MAX_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); - - this.minY = Math.min(minY, maxY); - this.maxY = Math.max(minY, maxY); - } - - protected void serializeBase(ConfigurationSection section) { - section.set("enabled", this.enabledValue); - section.set("minY", this.minY); - section.set("maxY", this.maxY); - } - - protected void deserializeWorlds(ConfigurationSection section, ConfigParsingContext context, String path) { - context = context.section(path); - - section.getStringList(path).stream().map(WorldMatcher::parseMatcher).forEach(worldMatchers::add); - - if (this.worldMatchers.isEmpty()) { - context.errorMissingOrEmpty(); - } - } - - protected void serializeWorlds(ConfigurationSection section, String path) { - section.set(path, worldMatchers.stream().map(WorldMatcher::serialize).collect(Collectors.toList())); - } - - protected void deserializeRandomBlocks(ConfigurationSection section, ConfigParsingContext context, String path) { - context = context.section(path); - - ConfigurationSection subSectionContainer = section.getConfigurationSection(path); - if (subSectionContainer == null) { - context.errorMissingOrEmpty(); - return; - } - - for (String subSectionName : subSectionContainer.getKeys(false)) { - ConfigParsingContext subContext = context.section(subSectionName); - ConfigurationSection subSection = subSectionContainer.getConfigurationSection(subSectionName); - this.weightedBlockLists.add(new WeightedBlockList(subSection, subContext)); - } - - if (this.weightedBlockLists.isEmpty()) { - context.errorMissingOrEmpty(); - } - } - - protected void serializeRandomBlocks(ConfigurationSection section, String path) { - ConfigurationSection subSectionContainer = section.createSection(path); - - for (WeightedBlockList weightedBlockList : this.weightedBlockLists) { - weightedBlockList.serialize(subSectionContainer); - } - } - - protected void disableOnError(ConfigParsingContext context) { - this.enabled = context.disableIfError(this.enabledValue); - } - - public String getName() { - return name; - } - - @Override - public boolean isEnabled() { - return this.enabled; - } - - @Override - public int getMinY() { - return this.minY; - } - - @Override - public int getMaxY() { - return this.maxY; - } - - @Override - public boolean matchesWorldName(String worldName) { - for (WorldMatcher matcher : this.worldMatchers) { - if (matcher.test(worldName)) { - return true; - } - } - return false; - } - - @Override - public boolean shouldObfuscate(int y) { - return y >= this.minY && y <= this.maxY; - } - - - WeightedIntRandom[] createWeightedRandoms(HeightAccessor heightAccessor) { - OFCLogger.debug(String.format("Creating weighted randoms for %s for world %s:", name, heightAccessor)); - return WeightedBlockList.create(heightAccessor, this.weightedBlockLists); - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorAdvancedConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorAdvancedConfig.java deleted file mode 100644 index 3c4d013ab..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorAdvancedConfig.java +++ /dev/null @@ -1,132 +0,0 @@ -package net.imprex.orebfuscator.config; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.util.OFCLogger; - -public class OrebfuscatorAdvancedConfig implements AdvancedConfig { - - private boolean verbose = false; - - private int obfuscationThreads = -1; - private long obfuscationTimeout = 10_000; - private int maxMillisecondsPerTick = 10; - - private int proximityThreads = -1; - private int proximityDefaultBucketSize = 50; - private int proximityThreadCheckInterval = 50; - private int proximityPlayerCheckInterval = 5000; - - private boolean obfuscationWorkerThreadsSet = false; - private boolean hasObfuscationTimeout = false; - private boolean proximityHiderThreadsSet = false; - private boolean hasProximityPlayerCheckInterval = true; - - public void deserialize(ConfigurationSection section, ConfigParsingContext context) { - this.verbose = section.getBoolean("verbose", false); - - // parse obfuscation section - ConfigParsingContext obfuscationContext = context.section("obfuscation"); - ConfigurationSection obfuscationSection = section.getConfigurationSection("obfuscation"); - if (obfuscationSection != null) { - this.obfuscationThreads = obfuscationSection.getInt("threads", -1); - this.obfuscationWorkerThreadsSet = (this.obfuscationThreads > 0); - - this.obfuscationTimeout = obfuscationSection.getLong("timeout", -1); - this.hasObfuscationTimeout = (this.obfuscationTimeout > 0); - - this.maxMillisecondsPerTick = obfuscationSection.getInt("maxMillisecondsPerTick", 10); - obfuscationContext.errorMinMaxValue("maxMillisecondsPerTick", 1, 50, this.maxMillisecondsPerTick); - } else { - obfuscationContext.warnMissingSection(); - } - - // parse proximity section - ConfigParsingContext proximityContext = context.section("proximity"); - ConfigurationSection proximitySection = section.getConfigurationSection("proximity"); - if (proximitySection != null) { - this.proximityThreads = proximitySection.getInt("threads", -1); - this.proximityHiderThreadsSet = (this.proximityThreads > 0); - - this.proximityDefaultBucketSize = proximitySection.getInt("defaultBucketSize", 50); - proximityContext.errorMinValue("defaultBucketSize", 1, this.proximityDefaultBucketSize); - - this.proximityThreadCheckInterval = proximitySection.getInt("threadCheckInterval", 50); - proximityContext.errorMinValue("threadCheckInterval", 1, this.proximityThreadCheckInterval); - - this.proximityPlayerCheckInterval = proximitySection.getInt("playerCheckInterval", 5000); - this.hasProximityPlayerCheckInterval = (this.proximityPlayerCheckInterval > 0); - } else { - proximityContext.warnMissingSection(); - } - } - - public void initialize() { - int availableThreads = Runtime.getRuntime().availableProcessors(); - this.obfuscationThreads = (int) (obfuscationWorkerThreadsSet ? obfuscationThreads : availableThreads); - this.proximityThreads = (int) (proximityHiderThreadsSet ? proximityThreads : Math.ceil(availableThreads / 2f)); - - OFCLogger.setVerboseLogging(this.verbose); - OFCLogger.debug("advanced.obfuscationWorkerThreads = " + this.obfuscationThreads); - OFCLogger.debug("advanced.proximityHiderThreads = " + this.proximityThreads); - } - - public void serialize(ConfigurationSection section) { - section.set("verbose", this.verbose); - - section.set("obfuscation.threads", this.obfuscationWorkerThreadsSet ? this.obfuscationThreads : -1); - section.set("obfuscation.timeout", this.hasObfuscationTimeout ? this.obfuscationTimeout : -1); - section.set("obfuscation.maxMillisecondsPerTick", this.maxMillisecondsPerTick); - - section.set("proximity.threads", this.proximityHiderThreadsSet ? this.proximityThreads : -1); - section.set("proximity.defaultBucketSize", this.proximityDefaultBucketSize); - section.set("proximity.threadCheckInterval", this.proximityThreadCheckInterval); - section.set("proximity.playerCheckInterval", this.hasProximityPlayerCheckInterval ? this.proximityPlayerCheckInterval : -1); - } - - @Override - public int obfuscationThreads() { - return this.obfuscationThreads; - } - - @Override - public boolean hasObfuscationTimeout() { - return this.hasObfuscationTimeout; - } - - @Override - public long obfuscationTimeout() { - return this.obfuscationTimeout; - } - - @Override - public int maxMillisecondsPerTick() { - return this.maxMillisecondsPerTick; - } - - @Override - public int proximityThreads() { - return this.proximityThreads; - } - - @Override - public int proximityDefaultBucketSize() { - return this.proximityDefaultBucketSize; - } - - @Override - public int proximityThreadCheckInterval() { - return this.proximityThreadCheckInterval; - } - - @Override - public boolean hasProximityPlayerCheckInterval() { - return this.hasProximityPlayerCheckInterval; - } - - @Override - public int proximityPlayerCheckInterval() { - return this.proximityPlayerCheckInterval; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorBlockFlags.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorBlockFlags.java deleted file mode 100644 index 5c38d1997..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorBlockFlags.java +++ /dev/null @@ -1,64 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.util.Map.Entry; - -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.BlockStateProperties; - -public class OrebfuscatorBlockFlags implements BlockFlags { - - private static final OrebfuscatorBlockFlags EMPTY_FLAGS = new OrebfuscatorBlockFlags(null, null); - - static OrebfuscatorBlockFlags create(OrebfuscatorObfuscationConfig worldConfig, OrebfuscatorProximityConfig proximityConfig) { - if ((worldConfig != null && worldConfig.isEnabled()) || (proximityConfig != null && proximityConfig.isEnabled())) { - return new OrebfuscatorBlockFlags(worldConfig, proximityConfig); - } - return EMPTY_FLAGS; - } - - private final int[] blockFlags = new int[OrebfuscatorNms.getUniqueBlockStateCount()]; - - private OrebfuscatorBlockFlags(OrebfuscatorObfuscationConfig worldConfig, OrebfuscatorProximityConfig proximityConfig) { - if (worldConfig != null && worldConfig.isEnabled()) { - for (BlockProperties block : worldConfig.hiddenBlocks()) { - this.setBlockBits(block, FLAG_OBFUSCATE); - } - } - - if (proximityConfig != null && proximityConfig.isEnabled()) { - for (Entry entry : proximityConfig.hiddenBlocks()) { - this.setBlockBits(entry.getKey(), entry.getValue()); - } - for (BlockProperties block : proximityConfig.allowForUseBlockBelow()) { - this.setBlockBits(block, FLAG_ALLOW_FOR_USE_BLOCK_BELOW); - } - } - } - - private void setBlockBits(BlockProperties block, int bits) { - for (BlockStateProperties blockState : block.getBlockStates()) { - int blockMask = this.blockFlags[blockState.getId()] | bits; - - if (blockState.isBlockEntity()) { - blockMask |= FLAG_BLOCK_ENTITY; - } - - this.blockFlags[blockState.getId()] = blockMask; - } - } - - @Override - public int flags(int blockState) { - return this.blockFlags[blockState]; - } - - @Override - public int flags(int blockState, int y) { - int flags = this.blockFlags[blockState]; - if (ProximityHeightCondition.match(flags, y)) { - flags |= FLAG_PROXIMITY; - } - return flags; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java deleted file mode 100644 index f1fcbb013..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorCacheConfig.java +++ /dev/null @@ -1,158 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.concurrent.TimeUnit; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.util.ChunkPosition; -import net.imprex.orebfuscator.util.OFCLogger; - -public class OrebfuscatorCacheConfig implements CacheConfig { - - private boolean enabledValue = true; - - private int maximumSize = 8192; - private long expireAfterAccess = TimeUnit.SECONDS.toMillis(30); - - private boolean enableDiskCacheValue = true; - private Path baseDirectory = Bukkit.getWorldContainer().toPath().resolve("orebfuscator_cache/"); - private int maximumOpenRegionFiles = 256; - private long deleteRegionFilesAfterAccess = TimeUnit.DAYS.toMillis(2); - private int maximumTaskQueueSize = 32768; - - // feature enabled states after context evaluation - private boolean enabled = false; - private boolean enableDiskCache = false; - - public void deserialize(ConfigurationSection section, ConfigParsingContext context) { - this.enabledValue = section.getBoolean("enabled", true); - - // parse memoryCache section - ConfigParsingContext memoryContext = context.section("memoryCache"); - ConfigurationSection memorySection = section.getConfigurationSection("memoryCache"); - if (memorySection != null) { - this.maximumSize = memorySection.getInt("maximumSize", 8192); - memoryContext.errorMinValue("maximumSize", 1, this.maximumSize); - - this.expireAfterAccess = memorySection.getLong("expireAfterAccess", TimeUnit.SECONDS.toMillis(30)); - memoryContext.errorMinValue("expireAfterAccess", 1, this.expireAfterAccess); - } else { - memoryContext.warnMissingSection(); - } - - // parse diskCache section, isolate errors to disable only diskCache on section error - ConfigParsingContext diskContext = context.section("diskCache", true); - ConfigurationSection diskSection = section.getConfigurationSection("diskCache"); - if (diskSection != null) { - this.enableDiskCacheValue = diskSection.getBoolean("enabled", true); - this.deserializeBaseDirectory(diskSection, diskContext, "orebfuscator_cache/"); - - this.maximumOpenRegionFiles = diskSection.getInt("maximumOpenFiles", 256); - diskContext.errorMinValue("maximumOpenFiles", 1, this.maximumOpenRegionFiles); - - this.deleteRegionFilesAfterAccess = diskSection.getLong("deleteFilesAfterAccess", TimeUnit.DAYS.toMillis(2)); - diskContext.errorMinValue("deleteFilesAfterAccess", 1, this.deleteRegionFilesAfterAccess); - - this.maximumTaskQueueSize = diskSection.getInt("maximumTaskQueueSize", 32768); - diskContext.errorMinValue("maximumTaskQueueSize", 1, this.maximumTaskQueueSize); - } else { - diskContext.warnMissingSection(); - } - - // try create diskCache.directory - if (this.enabledValue && this.enableDiskCacheValue) { - OFCLogger.debug("Using '" + this.baseDirectory.toAbsolutePath() + "' as chunk cache path"); - try { - if (Files.notExists(this.baseDirectory)) { - Files.createDirectories(this.baseDirectory); - } - } catch (IOException e) { - diskContext.error(String.format("can't create cache directory {%s}", e)); - e.printStackTrace(); - } - } - - // disable features if their config sections contain errors - this.enabled = context.disableIfError(this.enabledValue); - this.enableDiskCache = diskContext.disableIfError(this.enableDiskCacheValue); - } - - public void serialize(ConfigurationSection section) { - section.set("enabled", this.enabledValue); - - section.set("memoryCache.maximumSize", this.maximumSize); - section.set("memoryCache.expireAfterAccess", this.expireAfterAccess); - - section.set("diskCache.enabled", this.enableDiskCacheValue); - section.set("diskCache.directory", this.baseDirectory.toString()); - section.set("diskCache.maximumOpenFiles", this.maximumOpenRegionFiles); - section.set("diskCache.deleteFilesAfterAccess", this.deleteRegionFilesAfterAccess); - section.set("diskCache.maximumTaskQueueSize", this.maximumTaskQueueSize); - } - - private void deserializeBaseDirectory(ConfigurationSection section, ConfigParsingContext context, String defaultPath) { - Path worldPath = Bukkit.getWorldContainer().toPath().normalize(); - String baseDirectory = section.getString("directory", defaultPath); - - try { - this.baseDirectory = Paths.get(baseDirectory).normalize(); - } catch (InvalidPathException e) { - context.warn("directory", String.format("contains malformed path {%s}, using default path {%s}", - baseDirectory, defaultPath)); - this.baseDirectory = worldPath.resolve(defaultPath).normalize(); - } - } - - @Override - public boolean enabled() { - return this.enabled; - } - - @Override - public int maximumSize() { - return this.maximumSize; - } - - @Override - public long expireAfterAccess() { - return this.expireAfterAccess; - } - - @Override - public boolean enableDiskCache() { - return this.enableDiskCache; - } - - @Override - public Path baseDirectory() { - return this.baseDirectory; - } - - @Override - public Path regionFile(ChunkPosition key) { - return this.baseDirectory.resolve(key.world) - .resolve("r." + (key.x >> 5) + "." + (key.z >> 5) + ".mca"); - } - - @Override - public int maximumOpenRegionFiles() { - return this.maximumOpenRegionFiles; - } - - @Override - public long deleteRegionFilesAfterAccess() { - return this.deleteRegionFilesAfterAccess; - } - - @Override - public int maximumTaskQueueSize() { - return this.maximumTaskQueueSize; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java deleted file mode 100644 index 9652fa09e..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorConfig.java +++ /dev/null @@ -1,412 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.WeakHashMap; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.plugin.Plugin; - -import com.google.common.hash.Hashing; - -import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.OrebfuscatorCompatibility; -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.config.context.DefaultConfigParsingContext; -import net.imprex.orebfuscator.config.migrations.ConfigMigrator; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.HeightAccessor; -import net.imprex.orebfuscator.util.MinecraftVersion; -import net.imprex.orebfuscator.util.OFCLogger; -import net.imprex.orebfuscator.util.WeightedIntRandom; - -public class OrebfuscatorConfig implements Config { - - private static final int CONFIG_VERSION = 4; - - private final OrebfuscatorGeneralConfig generalConfig = new OrebfuscatorGeneralConfig(); - private final OrebfuscatorAdvancedConfig advancedConfig = new OrebfuscatorAdvancedConfig(); - private final OrebfuscatorCacheConfig cacheConfig = new OrebfuscatorCacheConfig(); - - private final List obfuscationConfigs = new ArrayList<>(); - private final List proximityConfigs = new ArrayList<>(); - - private final Map worldConfigBundles = new WeakHashMap<>(); - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - - private final Plugin plugin; - - private byte[] systemHash; - private String configReport; - - public OrebfuscatorConfig(Plugin plugin) { - this.plugin = plugin; - - this.load(); - } - - public void load() { - this.createConfigIfNotExist(); - this.plugin.reloadConfig(); - - DefaultConfigParsingContext context = new DefaultConfigParsingContext(); - this.deserialize(this.plugin.getConfig(), context); - this.configReport = context.report(); - - if (context.hasErrors()) { - throw new IllegalArgumentException("Can't parse config due to errors, Orebfuscator will now disable itself!"); - } - } - - public void store() { - ConfigurationSection section = this.plugin.getConfig(); - for (String path : section.getKeys(false)) { - section.set(path, null); - } - - this.serialize(section); - this.plugin.saveConfig(); - } - - private void createConfigIfNotExist() { - try { - Path dataFolder = this.plugin.getDataFolder().toPath(); - Path path = dataFolder.resolve("config.yml"); - - if (Files.notExists(path)) { - String configVersion = MinecraftVersion.majorVersion() + "." + MinecraftVersion.minorVersion(); - - if (Files.notExists(dataFolder)) { - Files.createDirectories(dataFolder); - } - - Files.copy(Orebfuscator.class.getResourceAsStream("/config/config-" + configVersion + ".yml"), path); - } - - this.systemHash = this.calculateSystemHash(path); - } catch (IOException e) { - throw new RuntimeException("unable to create config", e); - } - } - - private byte[] calculateSystemHash(Path path) throws IOException { - return Hashing.murmur3_128().newHasher() - .putBytes(this.plugin.getDescription().getVersion().getBytes(StandardCharsets.UTF_8)) - .putBytes(MinecraftVersion.nmsVersion().getBytes(StandardCharsets.UTF_8)) - .putBytes(Files.readAllBytes(path)).hash().asBytes(); - } - - private void deserialize(ConfigurationSection section, ConfigParsingContext context) { - ConfigMigrator.migrateToLatestVersion(section); - // instantly fail on invalid config version - if (section.getInt("version", -1) != CONFIG_VERSION) { - throw new RuntimeException("config is not up to date, please delete your config"); - } - - this.obfuscationConfigs.clear(); - this.proximityConfigs.clear(); - this.worldConfigBundles.clear(); - - // parse general section - ConfigParsingContext generalContext = context.section("general"); - ConfigurationSection generalSection = section.getConfigurationSection("general"); - if (generalSection != null) { - this.generalConfig.deserialize(generalSection, generalContext); - } else { - generalContext.warnMissingSection(); - } - - // parse advanced section - ConfigParsingContext advancedContext = context.section("advanced"); - ConfigurationSection advancedSection = section.getConfigurationSection("advanced"); - if (advancedSection != null) { - this.advancedConfig.deserialize(advancedSection, advancedContext); - } else { - advancedContext.warnMissingSection(); - } - - // post init advanced config - this.advancedConfig.initialize(); - - // parse cache section - ConfigParsingContext cacheContext = context.section("cache", true); - ConfigurationSection cacheSection = section.getConfigurationSection("cache"); - if (cacheSection != null) { - this.cacheConfig.deserialize(cacheSection, cacheContext); - } else { - cacheContext.warnMissingSection(); - } - - OrebfuscatorCompatibility.initialize(this.plugin, this); - - OrebfuscatorNms.close(); - OrebfuscatorNms.initialize(this); - - // parse obfuscation sections - ConfigParsingContext obfuscationContext = context.section("obfuscation"); - ConfigurationSection obfuscationSection = section.getConfigurationSection("obfuscation"); - for (String key : obfuscationSection.getKeys(false)) { - this.obfuscationConfigs.add(new OrebfuscatorObfuscationConfig( - obfuscationSection.getConfigurationSection(key), - obfuscationContext.section(key, true))); - } - if (this.obfuscationConfigs.isEmpty()) { - obfuscationContext.warnMissingOrEmpty(); - } - - // parse proximity sections - ConfigParsingContext proximityContext = context.section("proximity"); - ConfigurationSection proximitySection = section.getConfigurationSection("proximity"); - for (String key : proximitySection.getKeys(false)) { - this.proximityConfigs.add(new OrebfuscatorProximityConfig( - proximitySection.getConfigurationSection(key), - proximityContext.section(key, true))); - } - if (this.proximityConfigs.isEmpty()) { - proximityContext.warnMissingOrEmpty(); - } - - for (World world : Bukkit.getWorlds()) { - this.worldConfigBundles.put(world, new OrebfuscatorWorldConfigBundle(world)); - } - } - - private void serialize(ConfigurationSection section) { - section.set("version", CONFIG_VERSION); - - this.generalConfig.serialize(section.createSection("general")); - this.advancedConfig.serialize(section.createSection("advanced")); - this.cacheConfig.serialize(section.createSection("cache")); - - ConfigurationSection obfuscation = section.createSection("obfuscation"); - for (OrebfuscatorObfuscationConfig obfuscationConfig : this.obfuscationConfigs) { - obfuscationConfig.serialize(obfuscation.createSection(obfuscationConfig.getName())); - } - - ConfigurationSection proximity = section.createSection("proximity"); - for (OrebfuscatorProximityConfig proximityConfig : this.proximityConfigs) { - proximityConfig.serialize(proximity.createSection(proximityConfig.getName())); - } - } - - @Override - public byte[] systemHash() { - return systemHash; - } - - @Override - public String report() { - return configReport; - } - - @Override - public GeneralConfig general() { - return this.generalConfig; - } - - @Override - public AdvancedConfig advanced() { - return this.advancedConfig; - } - - @Override - public CacheConfig cache() { - return this.cacheConfig; - } - - @Override - public WorldConfigBundle world(World world) { - return this.getWorldConfigBundle(world); - } - - @Override - public boolean proximityEnabled() { - for (ProximityConfig proximityConfig : this.proximityConfigs) { - if (proximityConfig.isEnabled()) { - return true; - } - } - return false; - } - - public boolean usesBlockSpecificConfigs() { - for (OrebfuscatorProximityConfig config : this.proximityConfigs) { - if (config.usesBlockSpecificConfigs()) { - return true; - } - } - return false; - } - - public boolean usesFrustumCulling() { - for (ProximityConfig config : this.proximityConfigs) { - if (config.frustumCullingEnabled()) { - return true; - } - } - return false; - } - - public String usesRayCastCheck() { - for (ProximityConfig config : this.proximityConfigs) { - if (config.rayCastCheckEnabled()) { - return config.rayCastCheckOnlyCheckCenter() ? "center" : "true"; - } - } - return "false"; - } - - private OrebfuscatorWorldConfigBundle getWorldConfigBundle(World world) { - this.lock.readLock().lock(); - try { - OrebfuscatorWorldConfigBundle worldConfigs = this.worldConfigBundles.get(Objects.requireNonNull(world)); - if (worldConfigs != null) { - return worldConfigs; - } - } finally { - this.lock.readLock().unlock(); - } - - OrebfuscatorWorldConfigBundle worldConfigs = new OrebfuscatorWorldConfigBundle(world); - this.lock.writeLock().lock(); - try { - this.worldConfigBundles.putIfAbsent(world, worldConfigs); - return this.worldConfigBundles.get(world); - } finally { - this.lock.writeLock().unlock(); - } - } - - private class OrebfuscatorWorldConfigBundle implements WorldConfigBundle { - - private final OrebfuscatorObfuscationConfig obfuscationConfig; - private final OrebfuscatorProximityConfig proximityConfig; - - private final OrebfuscatorBlockFlags blockFlags; - private final boolean needsObfuscation; - - private final int minY; - private final int maxY; - - private final int minSectionIndex; - private final int maxSectionIndex; - - private final HeightAccessor heightAccessor; - private final WeightedIntRandom[] obfuscationRandoms; - private final WeightedIntRandom[] proximityRandoms; - - public OrebfuscatorWorldConfigBundle(World world) { - String worldName = world.getName(); - - this.obfuscationConfig = findConfig(obfuscationConfigs.stream(), worldName, "obfuscation"); - this.proximityConfig = findConfig(proximityConfigs.stream(), worldName, "proximity"); - - this.blockFlags = OrebfuscatorBlockFlags.create(obfuscationConfig, proximityConfig); - this.needsObfuscation = obfuscationConfig != null && obfuscationConfig.isEnabled() || - proximityConfig != null && proximityConfig.isEnabled(); - - this.minY = Math.min( - this.obfuscationConfig != null ? this.obfuscationConfig.getMinY() : BlockPos.MAX_Y, - this.proximityConfig != null ? this.proximityConfig.getMinY() : BlockPos.MAX_Y); - this.maxY = Math.max( - this.obfuscationConfig != null ? this.obfuscationConfig.getMaxY() : BlockPos.MIN_Y, - this.proximityConfig != null ? this.proximityConfig.getMaxY() : BlockPos.MIN_Y); - - this.heightAccessor = HeightAccessor.get(world); - this.minSectionIndex = heightAccessor.getSectionIndex(this.minY); - this.maxSectionIndex = heightAccessor.getSectionIndex(this.maxY - 1) + 1; - - this.obfuscationRandoms = this.obfuscationConfig != null - ? this.obfuscationConfig.createWeightedRandoms(heightAccessor) : null; - this.proximityRandoms = this.proximityConfig != null - ? this.proximityConfig.createWeightedRandoms(heightAccessor) : null; - } - - private T findConfig(Stream configs, String worldName, String configType) { - List matchingConfigs = configs - .filter(config -> config.matchesWorldName(worldName)) - .collect(Collectors.toList()); - - if (matchingConfigs.size() > 1) { - OFCLogger.warn(String.format("world '%s' has more than one %s config choosing first one", worldName, configType)); - } - - T config = matchingConfigs.size() > 0 ? matchingConfigs.get(0) : null; - String configName = config == null ? "null" : config.getName(); - - OFCLogger.debug(String.format("using '%s' %s config for world '%s'", configName, configType, worldName)); - - return config; - } - - @Override - public BlockFlags blockFlags() { - return this.blockFlags; - } - - @Override - public ObfuscationConfig obfuscation() { - return this.obfuscationConfig; - } - - @Override - public ProximityConfig proximity() { - return this.proximityConfig; - } - - @Override - public boolean needsObfuscation() { - return this.needsObfuscation; - } - - @Override - public boolean skipReadSectionIndex(int index) { - return index < this.minSectionIndex || index > this.maxSectionIndex; - } - - @Override - public boolean skipProcessingSectionIndex(int index) { - return index < this.minSectionIndex || index > this.maxSectionIndex; - } - - @Override - public int minSectionIndex() { - return this.minSectionIndex; - } - - @Override - public int maxSectionIndex() { - return this.maxSectionIndex; - } - - @Override - public boolean shouldObfuscate(int y) { - return y >= this.minY && y <= this.maxY; - } - - @Override - public int nextRandomObfuscationBlock(int y) { - return this.obfuscationRandoms != null - ? this.obfuscationRandoms[y - this.heightAccessor.getMinBuildHeight()].next() : 0; - } - - @Override - public int nextRandomProximityBlock(int y) { - return this.proximityRandoms != null - ? this.proximityRandoms[y - this.heightAccessor.getMinBuildHeight()].next() : 0; - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorGeneralConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorGeneralConfig.java deleted file mode 100644 index 24bca191e..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorGeneralConfig.java +++ /dev/null @@ -1,57 +0,0 @@ -package net.imprex.orebfuscator.config; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.config.context.ConfigParsingContext; - -public class OrebfuscatorGeneralConfig implements GeneralConfig { - - private boolean checkForUpdates = true; - private boolean updateOnBlockDamage = true; - private boolean bypassNotification = true; - private boolean ignoreSpectator = false; - private int updateRadius = 2; - - public void deserialize(ConfigurationSection section, ConfigParsingContext context) { - this.checkForUpdates = section.getBoolean("checkForUpdates", true); - this.updateOnBlockDamage = section.getBoolean("updateOnBlockDamage", true); - this.bypassNotification = section.getBoolean("bypassNotification", true); - this.ignoreSpectator = section.getBoolean("ignoreSpectator", false); - - this.updateRadius = section.getInt("updateRadius", 2); - context.errorMinValue("updateRadius", 1, this.updateRadius); - } - - public void serialize(ConfigurationSection section) { - section.set("checkForUpdates", this.checkForUpdates); - section.set("updateOnBlockDamage", this.updateOnBlockDamage); - section.set("bypassNotification", this.bypassNotification); - section.set("ignoreSpectator", this.ignoreSpectator); - section.set("updateRadius", this.updateRadius); - } - - @Override - public boolean checkForUpdates() { - return this.checkForUpdates; - } - - @Override - public boolean updateOnBlockDamage() { - return this.updateOnBlockDamage; - } - - @Override - public boolean bypassNotification() { - return this.bypassNotification; - } - - @Override - public boolean ignoreSpectator() { - return this.ignoreSpectator; - } - - @Override - public int updateRadius() { - return this.updateRadius; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorObfuscationConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorObfuscationConfig.java deleted file mode 100644 index 63fca3efa..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorObfuscationConfig.java +++ /dev/null @@ -1,76 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.util.BlockProperties; - -public class OrebfuscatorObfuscationConfig extends AbstractWorldConfig implements ObfuscationConfig { - - private boolean layerObfuscation = false; - - private final Set hiddenBlocks = new LinkedHashSet<>(); - - OrebfuscatorObfuscationConfig(ConfigurationSection section, ConfigParsingContext context) { - super(section.getName()); - this.deserializeBase(section); - this.deserializeWorlds(section, context, "worlds"); - this.layerObfuscation = section.getBoolean("layerObfuscation", false); - this.deserializeHiddenBlocks(section, context, "hiddenBlocks"); - this.deserializeRandomBlocks(section, context, "randomBlocks"); - this.disableOnError(context); - } - - void serialize(ConfigurationSection section) { - this.serializeBase(section); - this.serializeWorlds(section, "worlds"); - section.set("layerObfuscation", this.layerObfuscation); - this.serializeHiddenBlocks(section, "hiddenBlocks"); - this.serializeRandomBlocks(section, "randomBlocks"); - } - - private void deserializeHiddenBlocks(ConfigurationSection section, ConfigParsingContext context, String path) { - context = context.section(path); - - for (String blockName : section.getStringList(path)) { - BlockProperties blockProperties = OrebfuscatorNms.getBlockByName(blockName); - if (blockProperties == null) { - context.warnUnknownBlock(blockName); - } else if (blockProperties.getDefaultBlockState().isAir()) { - context.warnAirBlock(blockName); - } else { - this.hiddenBlocks.add(blockProperties); - } - } - - if (this.hiddenBlocks.isEmpty()) { - context.errorMissingOrEmpty(); - } - } - - private void serializeHiddenBlocks(ConfigurationSection section, String path) { - List blockNames = new ArrayList<>(); - - for (BlockProperties block : this.hiddenBlocks) { - blockNames.add(block.getKey().toString()); - } - - section.set(path, blockNames); - } - - @Override - public boolean layerObfuscation() { - return this.layerObfuscation; - } - - @Override - public Iterable hiddenBlocks() { - return this.hiddenBlocks; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java deleted file mode 100644 index bfffcd5fc..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/OrebfuscatorProximityConfig.java +++ /dev/null @@ -1,199 +0,0 @@ -package net.imprex.orebfuscator.config; - -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; - -import org.bukkit.configuration.ConfigurationSection; -import org.joml.Matrix4f; - -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.components.WeightedBlockList; -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.util.BlockProperties; - -public class OrebfuscatorProximityConfig extends AbstractWorldConfig implements ProximityConfig { - - private int distance = 24; - - private boolean frustumCullingEnabled = true; - private float frustumCullingMinDistance = 3; - private float frustumCullingFov = 80f; - - private float frustumCullingMinDistanceSquared = 9; - private Matrix4f frustumCullingProjectionMatrix; - - private boolean rayCastCheckEnabled = false; - private boolean rayCastCheckOnlyCheckCenter = false; - private int defaultBlockFlags = (ProximityHeightCondition.MATCH_ALL | BlockFlags.FLAG_USE_BLOCK_BELOW); - - private boolean usesBlockSpecificConfigs = false; - private Map hiddenBlocks = new LinkedHashMap<>(); - private Set allowForUseBlockBelow = new HashSet<>(); - - OrebfuscatorProximityConfig(ConfigurationSection section, ConfigParsingContext context) { - super(section.getName()); - this.deserializeBase(section); - this.deserializeWorlds(section, context, "worlds"); - - this.distance = section.getInt("distance", 24); - context.errorMinValue("distance", 1, this.distance); - - this.frustumCullingEnabled = section.getBoolean("frustumCulling.enabled", false); - this.frustumCullingMinDistance = (float) section.getDouble("frustumCulling.minDistance", 3); - this.frustumCullingFov = (float) section.getDouble("frustumCulling.fov", 80d); - - if (this.frustumCullingEnabled && (this.frustumCullingFov < 10 || this.frustumCullingFov > 170)) { - context.errorMinMaxValue("frustumCulling.fov", 10, 170, (int) this.frustumCullingFov); - } - - this.frustumCullingMinDistanceSquared = frustumCullingMinDistance * frustumCullingMinDistance; - this.frustumCullingProjectionMatrix = new Matrix4f() // create projection matrix with aspect 16:9 - .perspective(frustumCullingFov, 16f / 9f, 0.01f, 2 * distance); - - this.rayCastCheckEnabled = section.getBoolean("rayCastCheck.enabled", false); - this.rayCastCheckOnlyCheckCenter = section.getBoolean("rayCastCheck.onlyCheckCenter", false); - - this.defaultBlockFlags = ProximityHeightCondition.create(minY, maxY); - if (section.getBoolean("useBlockBelow", true)) { - this.defaultBlockFlags |= BlockFlags.FLAG_USE_BLOCK_BELOW; - } - - this.deserializeHiddenBlocks(section, context, "hiddenBlocks"); - this.deserializeRandomBlocks(section, context, "randomBlocks"); - - for (WeightedBlockList blockList : this.weightedBlockLists) { - this.allowForUseBlockBelow.addAll(blockList.getBlocks()); - } - - this.disableOnError(context); - } - - protected void serialize(ConfigurationSection section) { - this.serializeBase(section); - this.serializeWorlds(section, "worlds"); - - section.set("distance", this.distance); - - section.set("frustumCulling.enabled", frustumCullingEnabled); - section.set("frustumCulling.minDistance", frustumCullingMinDistance); - section.set("frustumCulling.fov", frustumCullingFov); - - section.set("rayCastCheck.enabled", this.rayCastCheckEnabled); - section.set("rayCastCheck.onlyCheckCenter", this.rayCastCheckOnlyCheckCenter); - section.set("useBlockBelow", BlockFlags.isUseBlockBelowBitSet(this.defaultBlockFlags)); - - this.serializeHiddenBlocks(section, "hiddenBlocks"); - this.serializeRandomBlocks(section, "randomBlocks"); - } - - private void deserializeHiddenBlocks(ConfigurationSection section, ConfigParsingContext context, String path) { - context = context.section(path); - - ConfigurationSection blockSection = section.getConfigurationSection(path); - if (blockSection == null) { - return; - } - - for (String blockName : blockSection.getKeys(false)) { - BlockProperties blockProperties = OrebfuscatorNms.getBlockByName(blockName); - if (blockProperties == null) { - context.warnUnknownBlock(blockName); - } else if (blockProperties.getDefaultBlockState().isAir()) { - context.warnAirBlock(blockName); - } else { - int blockFlags = this.defaultBlockFlags; - - // parse block specific height condition - if (blockSection.isInt(blockName + ".minY") && blockSection.isInt(blockName + ".maxY")) { - int minY = blockSection.getInt(blockName + ".minY"); - int maxY = blockSection.getInt(blockName + ".maxY"); - - blockFlags = ProximityHeightCondition.remove(blockFlags); - blockFlags |= ProximityHeightCondition.create( - Math.min(minY, maxY), - Math.max(minY, maxY)); - usesBlockSpecificConfigs = true; - } - - // parse block specific flags - if (blockSection.isBoolean(blockName + ".useBlockBelow")) { - if (blockSection.getBoolean(blockName + ".useBlockBelow")) { - blockFlags |= BlockFlags.FLAG_USE_BLOCK_BELOW; - } else { - blockFlags &= ~BlockFlags.FLAG_USE_BLOCK_BELOW; - } - usesBlockSpecificConfigs = true; - } - - this.hiddenBlocks.put(blockProperties, blockFlags); - } - } - - if (this.hiddenBlocks.isEmpty()) { - context.errorMissingOrEmpty(); - } - } - - private void serializeHiddenBlocks(ConfigurationSection section, String path) { - ConfigurationSection parentSection = section.createSection(path); - - for (Map.Entry entry : this.hiddenBlocks.entrySet()) { - ConfigurationSection childSection = parentSection.createSection(entry.getKey().getKey().toString()); - - int blockFlags = entry.getValue(); - if (!ProximityHeightCondition.equals(blockFlags, this.defaultBlockFlags)) { - childSection.set("minY", ProximityHeightCondition.getMinY(blockFlags)); - childSection.set("maxY", ProximityHeightCondition.getMaxY(blockFlags)); - } - - if (BlockFlags.isUseBlockBelowBitSet(blockFlags) != BlockFlags.isUseBlockBelowBitSet(this.defaultBlockFlags)) { - childSection.set("useBlockBelow", BlockFlags.isUseBlockBelowBitSet(blockFlags)); - } - } - } - - @Override - public int distance() { - return this.distance; - } - - @Override - public boolean frustumCullingEnabled() { - return this.frustumCullingEnabled; - } - - @Override - public float frustumCullingMinDistanceSquared() { - return this.frustumCullingMinDistanceSquared; - } - - @Override - public Matrix4f frustumCullingProjectionMatrix() { - return new Matrix4f(frustumCullingProjectionMatrix); - } - - @Override - public boolean rayCastCheckEnabled() { - return this.rayCastCheckEnabled; - } - - @Override - public boolean rayCastCheckOnlyCheckCenter() { - return this.rayCastCheckOnlyCheckCenter; - } - - @Override - public Iterable> hiddenBlocks() { - return this.hiddenBlocks.entrySet(); - } - - public Iterable allowForUseBlockBelow() { - return this.allowForUseBlockBelow; - } - - boolean usesBlockSpecificConfigs() { - return usesBlockSpecificConfigs; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/ProximityHeightCondition.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/ProximityHeightCondition.java deleted file mode 100644 index 5c0bd13b9..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/ProximityHeightCondition.java +++ /dev/null @@ -1,56 +0,0 @@ -package net.imprex.orebfuscator.config; - -import net.imprex.orebfuscator.util.BlockPos; - -/** - * Only use 25 MSBs of blockFlags for ProximityHeightCondition - * 12 bit min y | 12 bit max y | 1 bit present - */ -public class ProximityHeightCondition { - - public static final int MATCH_ALL = ProximityHeightCondition.create(BlockPos.MIN_Y, BlockPos.MAX_Y); - - public static int clampY(int y) { - return Math.min(BlockPos.MAX_Y, Math.max(BlockPos.MIN_Y, y)); - } - - private static int unsigned(int y) { - return (clampY(y) - BlockPos.MIN_Y) & 0xFFF; - } - - private static int signed(int y) { - return y + BlockPos.MIN_Y; - } - - public static int create(int minY, int maxY) { - return unsigned(minY) << 20 | unsigned(maxY) << 8 | 0x80; - } - - public static int remove(int hideCondition) { - return hideCondition & 0x7F; - } - - private static int extractHideCondition(int hideCondition) { - return hideCondition & 0xFFFFFF80; - } - - public static boolean equals(int a, int b) { - return extractHideCondition(a) == extractHideCondition(b); - } - - public static boolean match(int hideCondition, int y) { - return isPresent(hideCondition) && getMinY(hideCondition) <= y && getMaxY(hideCondition) >= y; - } - - public static boolean isPresent(int hideCondition) { - return (hideCondition & 0x80) != 0; - } - - public static int getMinY(int hideCondition) { - return signed(hideCondition >>> 20); - } - - public static int getMaxY(int hideCondition) { - return signed(hideCondition << 12 >>> 20); - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/components/WeightedBlockList.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/components/WeightedBlockList.java deleted file mode 100644 index 5aed49d91..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/components/WeightedBlockList.java +++ /dev/null @@ -1,123 +0,0 @@ -package net.imprex.orebfuscator.config.components; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.context.ConfigParsingContext; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.BlockProperties; -import net.imprex.orebfuscator.util.HeightAccessor; -import net.imprex.orebfuscator.util.MathUtil; -import net.imprex.orebfuscator.util.OFCLogger; -import net.imprex.orebfuscator.util.WeightedIntRandom; - -public class WeightedBlockList { - - public static WeightedIntRandom[] create(HeightAccessor heightAccessor, List lists) { - WeightedIntRandom[] heightMap = new WeightedIntRandom[heightAccessor.getHeight()]; - - List last = new ArrayList<>(); - List next = new ArrayList<>(); - - int count = 0; - - for (int y = heightAccessor.getMinBuildHeight(); y < heightAccessor.getMaxBuildHeight(); y++) { - for (WeightedBlockList list : lists) { - if (list.minY <= y && list.maxY >= y) { - next.add(list); - } - } - - int index = y - heightAccessor.getMinBuildHeight(); - if (index > 0 && last.equals(next)) { - // copy last random - heightMap[index] = heightMap[index - 1]; - } else { - WeightedIntRandom.Builder builder = WeightedIntRandom.builder(); - - for (WeightedBlockList list : next) { - for (Map.Entry entry : list.blocks.entrySet()) { - if (!builder.add(entry.getKey().getDefaultBlockState().getId(), entry.getValue())) { - OFCLogger.warn(String.format("duplicate randomBlock entry for %s in %s", - entry.getKey().getKey(), list.name)); - } - } - } - - heightMap[index] = builder.build(); - count++; - - // update last only on change - last.clear(); - last.addAll(next); - } - - next.clear(); - } - - OFCLogger.debug(String.format("Successfully created %s weigthed randoms", count)); - - return heightMap; - } - - private final String name; - - private final int minY; - private final int maxY; - - private final Map blocks = new LinkedHashMap<>(); - - public WeightedBlockList(ConfigurationSection section, ConfigParsingContext context) { - this.name = section.getName(); - - int minY = MathUtil.clamp(section.getInt("minY", BlockPos.MIN_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); - int maxY = MathUtil.clamp(section.getInt("maxY", BlockPos.MAX_Y), BlockPos.MIN_Y, BlockPos.MAX_Y); - - this.minY = Math.min(minY, maxY); - this.maxY = Math.max(minY, maxY); - - ConfigParsingContext blocksContext = context.section("blocks"); - if (!section.isConfigurationSection("blocks")) { - blocksContext.errorMissingOrEmpty(); - return; - } - - ConfigurationSection blockSection = section.getConfigurationSection("blocks"); - for (String blockName : blockSection.getKeys(false)) { - BlockProperties blockProperties = OrebfuscatorNms.getBlockByName(blockName); - if (blockProperties != null) { - int weight = blockSection.getInt(blockName, 1); - this.blocks.put(blockProperties, weight); - } else { - blocksContext.warnUnknownBlock(blockName); - } - } - - if (this.blocks.isEmpty()) { - blocksContext.errorMissingOrEmpty(); - } - } - - public void serialize(ConfigurationSection section) { - section = section.createSection(this.name); - - section.set("minY", this.minY); - section.set("maxY", this.maxY); - - section = section.createSection("blocks"); - for (Map.Entry entry : this.blocks.entrySet()) { - section.set(entry.getKey().getKey().toString(), entry.getValue()); - } - } - - public Set getBlocks() { - return Collections.unmodifiableSet(this.blocks.keySet()); - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/components/WorldMatcher.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/components/WorldMatcher.java deleted file mode 100644 index 52f179ace..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/components/WorldMatcher.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.imprex.orebfuscator.config.components; - -import java.util.function.Predicate; -import java.util.regex.Pattern; - -public class WorldMatcher implements Predicate { - - public static WorldMatcher parseMatcher(String value) { - if (value.startsWith("regex:")) { - return new WorldMatcher(parseRegexMatcher(value.substring(6)), Type.REGEX); - } else { - return new WorldMatcher(parseWildcardMatcher(value), Type.WILDCARD); - } - } - - private static Pattern parseRegexMatcher(String pattern) { - return Pattern.compile(pattern); - } - - private static Pattern parseWildcardMatcher(String value) { - String pattern = ("\\Q" + value + "\\E").replace("*", "\\E.*\\Q"); - return Pattern.compile(pattern); - } - - private final Pattern pattern; - private final Type type; - - private WorldMatcher(Pattern pattern, Type type) { - this.pattern = pattern; - this.type = type; - } - - @Override - public boolean test(String value) { - return this.pattern.matcher(value).matches(); - } - - public String serialize() { - if (this.type == Type.REGEX) { - return "regex:" + this.pattern.pattern(); - } else { - return this.pattern.pattern() - .replace("\\E.*\\Q", "*") - .replaceAll("\\\\Q|\\\\E", ""); - } - } - - private enum Type { - REGEX, WILDCARD; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/context/ConfigParsingContext.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/context/ConfigParsingContext.java deleted file mode 100644 index 0b4d8b92c..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/context/ConfigParsingContext.java +++ /dev/null @@ -1,67 +0,0 @@ -package net.imprex.orebfuscator.config.context; - -public interface ConfigParsingContext { - - ConfigParsingContext section(String path, boolean isolateErrors); - - default ConfigParsingContext section(String path) { - return section(path, false); - } - - ConfigParsingContext warn(String message); - - ConfigParsingContext warn(String path, String message); - - default ConfigParsingContext warnMissingSection() { - warn("section is missing, adding default one"); - return this; - } - - default ConfigParsingContext warnMissingOrEmpty() { - warn("is missing or empty"); - return this; - } - - default ConfigParsingContext warnUnknownBlock(String name) { - warn(String.format("contains unknown block '%s', skipping", name)); - return this; - } - - default ConfigParsingContext warnAirBlock(String name) { - warn(String.format("contains air block '%s', skipping", name)); - return this; - } - - default boolean disableIfError(boolean enabled) { - if (enabled && hasErrors()) { - warn("section got disabled due to errors"); - return false; - } - return enabled; - } - - ConfigParsingContext error(String message); - - ConfigParsingContext error(String path, String message); - - default ConfigParsingContext errorMinValue(String path, long min, long value) { - if (value < min) { - error(path, String.format("value too low {value(%d) < min(%d)}", value, min)); - } - return this; - } - - default ConfigParsingContext errorMinMaxValue(String path, long min, long max, long value) { - if (value < min || value > max) { - error(path, String.format("value out of range {value(%d) not in range[%d, %d]}", value, min, max)); - } - return this; - } - - default ConfigParsingContext errorMissingOrEmpty() { - error("is missing or empty"); - return this; - } - - boolean hasErrors(); -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/context/DefaultConfigParsingContext.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/context/DefaultConfigParsingContext.java deleted file mode 100644 index af5e908cb..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/context/DefaultConfigParsingContext.java +++ /dev/null @@ -1,159 +0,0 @@ -package net.imprex.orebfuscator.config.context; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; - -import net.imprex.orebfuscator.util.OFCLogger; - -public class DefaultConfigParsingContext implements ConfigParsingContext { - - private static final String ANSI_RESET = "\u001B[m"; - private static final String ANSI_ERROR = "\u001B[31;1m"; // Bold Red - private static final String ANSI_WARN = "\u001B[33;1m"; // Bold Yellow - - private final int depth; - private boolean isolateErrors = false; - - private final Map section = new LinkedHashMap<>(); - private final List messages = new ArrayList<>(); - - public DefaultConfigParsingContext() { - this(0); - } - - private DefaultConfigParsingContext(int depth) { - this.depth = depth; - } - - @Override - public DefaultConfigParsingContext section(String path, boolean isolateErrors) { - DefaultConfigParsingContext context = getContext(path); - context.isolateErrors = isolateErrors; - return context; - } - - @Override - public ConfigParsingContext warn(String message) { - this.messages.add(new Message(false, message)); - return this; - } - - @Override - public ConfigParsingContext warn(String path, String message) { - getContext(path).warn(message); - return this; - } - - @Override - public ConfigParsingContext error(String message) { - this.messages.add(new Message(true, message)); - return this; - } - - @Override - public ConfigParsingContext error(String path, String message) { - getContext(path).error(message); - return this; - } - - @Override - public boolean hasErrors() { - for (Message message : this.messages) { - if (message.isError()) { - return true; - } - } - - for (var section : this.section.values()) { - if (!section.isolateErrors && section.hasErrors()) { - return true; - } - } - - return false; - } - - private DefaultConfigParsingContext getContext(String path) { - DefaultConfigParsingContext context = this; - - for (String segment : path.split("\\.")) { - DefaultConfigParsingContext nextContext = context.section.get(segment); - if (nextContext == null) { - nextContext = new DefaultConfigParsingContext(context.depth + 1); - context.section.put(segment, nextContext); - } - context = nextContext; - } - - return context; - } - - private int getMessageCount() { - int messageCount = this.messages.size(); - - for (DefaultConfigParsingContext section : section.values()) { - messageCount += section.getMessageCount(); - } - - return messageCount; - } - - private String buildReport() { - int messageCount = this.getMessageCount(); - if (messageCount == 0) { - return ""; - } - - final StringBuilder builder = new StringBuilder(); - final String indent = " ".repeat(this.depth); - - // sort -> errors should come before warnings - Collections.sort(this.messages); - - for (Message message : this.messages) { - String color = message.isError() ? ANSI_ERROR : ANSI_WARN; - builder.append(indent).append(color).append("- ").append(message.content()).append('\n'); - } - - for (var entry : this.section.entrySet()) { - if (entry.getValue().getMessageCount() == 0) { - continue; - } - builder.append(indent).append(ANSI_WARN).append(entry.getKey()).append(":\n"); - builder.append(entry.getValue().buildReport()); - } - - return builder.toString(); - } - - public String report() { - int messageCount = this.getMessageCount(); - if (messageCount == 0) { - return null; - } - - StringBuilder builder = new StringBuilder(); - builder.append("Encountered ").append(messageCount).append(" issue(s) while parsing the config:\n") - .append(ANSI_RESET) - .append(buildReport()) - .append(ANSI_RESET); - - String message = builder.toString(); - OFCLogger.log(hasErrors() ? Level.SEVERE : Level.WARNING, message); - return message; - } - - private record Message(boolean isError, String content) implements Comparable { - - @Override - public int compareTo(Message o) { - int a = this.isError ? 1 : 0; - int b = o.isError ? 1 : 0; - return b - a; - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigration.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigration.java deleted file mode 100644 index 10c97a40a..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigration.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.imprex.orebfuscator.config.migrations; - -import java.util.List; -import java.util.Map; - -import org.bukkit.configuration.ConfigurationSection; - -interface ConfigMigration { - - int sourceVersion(); - - ConfigurationSection migrate(ConfigurationSection section); - - static void migrateNames(ConfigurationSection section, List> mapping) { - if (section == null) { - return; - } - - for (Map.Entry entry : mapping) { - Object value = section.get(entry.getKey()); - if (value != null) { - section.set(entry.getValue(), value); - } - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV1.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV1.java deleted file mode 100644 index 141f12443..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV1.java +++ /dev/null @@ -1,64 +0,0 @@ -package net.imprex.orebfuscator.config.migrations; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.bukkit.configuration.ConfigurationSection; - -class ConfigMigrationV1 implements ConfigMigration { - - @Override - public int sourceVersion() { - return 1; - } - - @Override - public ConfigurationSection migrate(ConfigurationSection section) { - // check if config is still using old path - String obfuscationConfigPath = section.contains("world") ? "world" : "obfuscation"; - this.convertSectionListToSection(section, obfuscationConfigPath); - this.convertSectionListToSection(section, "proximity"); - - return section; - } - - private void convertSectionListToSection(ConfigurationSection parentSection, String path) { - List sections = this.deserializeSectionList(parentSection, path); - ConfigurationSection section = parentSection.createSection(path); - for (ConfigurationSection childSection : sections) { - section.set(childSection.getName(), childSection); - } - } - - private List deserializeSectionList(ConfigurationSection parentSection, String path) { - List sections = new ArrayList<>(); - - List sectionList = parentSection.getList(path); - if (sectionList != null) { - for (int i = 0; i < sectionList.size(); i++) { - Object section = sectionList.get(i); - if (section instanceof Map) { - sections.add(this.convertMapsToSections((Map) section, - parentSection.createSection(path + "-" + i))); - } - } - } - - return sections; - } - - private ConfigurationSection convertMapsToSections(Map input, ConfigurationSection section) { - for (Map.Entry entry : input.entrySet()) { - String key = entry.getKey().toString(); - Object value = entry.getValue(); - - if (value instanceof Map) { - this.convertMapsToSections((Map) value, section.createSection(key)); - } else { - section.set(key, value); - } - } - return section; - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV2.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV2.java deleted file mode 100644 index 4b9e47647..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV2.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.imprex.orebfuscator.config.migrations; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.util.BlockPos; - -class ConfigMigrationV2 implements ConfigMigration { - - @Override - public int sourceVersion() { - return 2; - } - - @Override - public ConfigurationSection migrate(ConfigurationSection section) { - this.convertRandomBlocksToSections(section.getConfigurationSection("obfuscation")); - this.convertRandomBlocksToSections(section.getConfigurationSection("proximity")); - return section; - } - - - private void convertRandomBlocksToSections(ConfigurationSection parentSection) { - for (String key : parentSection.getKeys(false)) { - ConfigurationSection config = parentSection.getConfigurationSection(key); - ConfigurationSection blockSection = config.getConfigurationSection("randomBlocks"); - if (blockSection == null) { - continue; - } - - ConfigurationSection newBlockSection = config.createSection("randomBlocks"); - newBlockSection = newBlockSection.createSection("section-global"); - newBlockSection.set("minY", BlockPos.MIN_Y); - newBlockSection.set("maxY", BlockPos.MAX_Y); - newBlockSection = newBlockSection.createSection("blocks"); - - for (String blockName : blockSection.getKeys(false)) { - newBlockSection.set(blockName, blockSection.getInt(blockName, 1)); - } - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV3.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV3.java deleted file mode 100644 index 90c623b8c..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrationV3.java +++ /dev/null @@ -1,95 +0,0 @@ -package net.imprex.orebfuscator.config.migrations; - -import java.util.List; -import java.util.Map; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.util.BlockPos; - -class ConfigMigrationV3 implements ConfigMigration { - - @Override - public int sourceVersion() { - return 3; - } - - @Override - public ConfigurationSection migrate(ConfigurationSection section) { - migrateAdvancedConfig(section.getConfigurationSection("advanced")); - migrateCacheConfig(section.getConfigurationSection("cache")); - migrateProximityConfigs(section.getConfigurationSection("proximity")); - return section; - } - - private static void migrateAdvancedConfig(ConfigurationSection section) { - ConfigMigration.migrateNames(section, List.of( - // obfuscation mapping - Map.entry("obfuscationWorkerThreads", "obfuscation.threads"), - Map.entry("obfuscationTimeout", "obfuscation.timeout"), - Map.entry("maxMillisecondsPerTick", "obfuscation.maxMillisecondsPerTick"), - // proximity mapping - Map.entry("proximityHiderThreads", "proximity.threads"), - Map.entry("proximityDefaultBucketSize", "proximity.defaultBucketSize"), - Map.entry("proximityThreadCheckInterval", "proximity.threadCheckInterval"), - Map.entry("proximityPlayerCheckInterval", "proximity.playerCheckInterval") - )); - } - - private static void migrateCacheConfig(ConfigurationSection section) { - ConfigMigration.migrateNames(section, List.of( - // memory cache mapping - Map.entry("maximumSize", "memoryCache.maximumSize"), - Map.entry("expireAfterAccess", "memoryCache.expireAfterAccess"), - // disk cache mapping - Map.entry("enableDiskCache", "diskCache.enabled"), - Map.entry("baseDirectory", "diskCache.directory"), - Map.entry("maximumOpenRegionFiles", "diskCache.maximumOpenFiles"), - Map.entry("deleteRegionFilesAfterAccess", "diskCache.deleteFilesAfterAccess"), - Map.entry("maximumTaskQueueSize", "diskCache.maximumTaskQueueSize") - )); - } - - private static void migrateProximityConfigs(ConfigurationSection parentSection) { - for (String key : parentSection.getKeys(false)) { - ConfigurationSection section = parentSection.getConfigurationSection(key); - - // LEGACY: transform to post 5.2.2 - if (section.isConfigurationSection("defaults")) { - int y = section.getInt("defaults.y"); - if (section.getBoolean("defaults.above")) { - section.set("minY", y); - section.set("maxY", BlockPos.MAX_Y); - } else { - section.set("minY", BlockPos.MIN_Y); - section.set("maxY", y); - } - - section.set("useBlockBelow", section.getBoolean("defaults.useBlockBelow")); - } - - // rename all ray cast name variations - if (section.isBoolean("useRayCastCheck") || section.isBoolean("useFastGazeCheck")) { - section.set("rayCastCheck.enabled", - section.getBoolean("useRayCastCheck", - section.getBoolean("useFastGazeCheck"))); - } - - // LEGACY: transform pre 5.2.2 height condition - ConfigurationSection blockSection = section.getConfigurationSection("hiddenBlocks"); - for (String blockName : blockSection.getKeys(false)) { - - if (blockSection.isInt(blockName + ".y") && blockSection.isBoolean(blockName + ".above")) { - int y = blockSection.getInt(blockName + ".y"); - if (blockSection.getBoolean(blockName + ".above")) { - blockSection.set(blockName + ".minY", y); - blockSection.set(blockName + ".maxY", BlockPos.MAX_Y); - } else { - blockSection.set(blockName + ".minY", BlockPos.MIN_Y); - blockSection.set(blockName + ".maxY", y); - } - } - } - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrator.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrator.java deleted file mode 100644 index 16755dbda..000000000 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/config/migrations/ConfigMigrator.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.imprex.orebfuscator.config.migrations; - -import java.util.HashMap; -import java.util.Map; - -import org.bukkit.configuration.ConfigurationSection; - -import net.imprex.orebfuscator.util.OFCLogger; - -public class ConfigMigrator { - - private static final Map MIGRATIONS = new HashMap<>(); - - static { - register(new ConfigMigrationV1()); - register(new ConfigMigrationV2()); - register(new ConfigMigrationV3()); - } - - private static void register(ConfigMigration migration) { - MIGRATIONS.put(migration.sourceVersion(), migration); - } - - public static void migrateToLatestVersion(ConfigurationSection section) { - while (true) { - int sourceVersion = section.getInt("version", -1); - int targetVersion = sourceVersion + 1; - - ConfigMigration migration = MIGRATIONS.get(sourceVersion); - if (migration == null) { - break; - } - - OFCLogger.info("Starting to migrate config to version " + targetVersion); - - section = migration.migrate(section); - section.set("version", targetVersion); - - OFCLogger.info("Successfully migrated config to version " + targetVersion); - } - } -} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitChunkPacketAccessor.java similarity index 59% rename from orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java rename to orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitChunkPacketAccessor.java index 3d8debb64..c3f9af328 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/ChunkStruct.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitChunkPacketAccessor.java @@ -1,43 +1,47 @@ -package net.imprex.orebfuscator.chunk; +package net.imprex.orebfuscator.iterop; import java.util.BitSet; import java.util.Iterator; import java.util.List; import java.util.function.Predicate; -import org.bukkit.World; - import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.wrappers.nbt.NbtBase; import com.comphenix.protocol.wrappers.nbt.NbtCompound; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.HeightAccessor; +import dev.imprex.orebfuscator.interop.ChunkPacketAccessor; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.util.BlockPos; +import net.imprex.orebfuscator.util.MinecraftVersion; +import net.imprex.orebfuscator.util.WrappedClientboundLevelChunkPacketData; + +public class BukkitChunkPacketAccessor implements ChunkPacketAccessor { -public class ChunkStruct { + private static final boolean HAS_CLIENTBOUND_LEVEL_CHUNK_PACKET_DATA = MinecraftVersion.isAtOrAbove("1.18"); + private static final boolean HAS_HEIGHT_BITMASK = MinecraftVersion.isBelow("1.18"); + private static final boolean HAS_VARINT_BITMASK = MinecraftVersion.isBelow("1.17"); - public final World world; + public final BukkitWorldAccessor worldAccessor; - public final int chunkX; - public final int chunkZ; + private final int chunkX; + private final int chunkZ; - public final BitSet sectionMask; - public final byte[] data; + private final BitSet sectionMask; + private final byte[] data; private final PacketContainer packet; private final WrappedClientboundLevelChunkPacketData packetData; - public ChunkStruct(PacketContainer packet, World world) { + public BukkitChunkPacketAccessor(PacketContainer packet, BukkitWorldAccessor worldAccessor) { this.packet = packet; - StructureModifier packetInteger = packet.getIntegers(); - - this.world = world; + this.worldAccessor = worldAccessor; + StructureModifier packetInteger = packet.getIntegers(); this.chunkX = packetInteger.read(0); this.chunkZ = packetInteger.read(1); - if (ChunkCapabilities.hasClientboundLevelChunkPacketData()) { + if (HAS_CLIENTBOUND_LEVEL_CHUNK_PACKET_DATA) { this.packetData = new WrappedClientboundLevelChunkPacketData(packet); this.data = this.packetData.getBuffer(); } else { @@ -45,19 +49,45 @@ public ChunkStruct(PacketContainer packet, World world) { this.data = packet.getByteArrays().read(0); } - if (ChunkCapabilities.hasHeightBitMask()) { - if (ChunkCapabilities.hasDynamicHeight()) { - this.sectionMask = packet.getSpecificModifier(BitSet.class).read(0); - } else { + if (HAS_HEIGHT_BITMASK) { + if (HAS_VARINT_BITMASK) { this.sectionMask = convertIntToBitSet(packetInteger.read(2)); + } else { + this.sectionMask = packet.getSpecificModifier(BitSet.class).read(0); } } else { this.sectionMask = new BitSet(); - this.sectionMask.set(0, HeightAccessor.get(world).getSectionCount()); + this.sectionMask.set(0, worldAccessor.getSectionCount()); } } - public void setDataBuffer(byte[] data) { + @Override + public WorldAccessor world() { + return this.worldAccessor; + } + + @Override + public int chunkX() { + return this.chunkX; + } + + @Override + public int chunkZ() { + return this.chunkZ; + } + + @Override + public boolean isSectionPresent(int index) { + return this.sectionMask.get(index); + } + + @Override + public byte[] data() { + return this.data; + } + + @Override + public void setData(byte[] data) { if (this.packetData != null) { this.packetData.setBuffer(data); } else { @@ -65,7 +95,8 @@ public void setDataBuffer(byte[] data) { } } - public void removeBlockEntityIf(Predicate predicate) { + @Override + public void filterBlockEntities(Predicate predicate) { if (this.packetData != null) { this.packetData.removeBlockEntityIf(relativePostion -> predicate.test(relativePostion.add(chunkX << 4, 0, chunkZ << 4))); @@ -101,7 +132,7 @@ public boolean isEmpty() { return this.sectionMask.isEmpty(); } - private BitSet convertIntToBitSet(int value) { + private static BitSet convertIntToBitSet(int value) { BitSet bitSet = new BitSet(); for (int index = 0; value != 0; index++) { if ((value & 1) == 1) { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitLoggerAccessor.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitLoggerAccessor.java new file mode 100644 index 000000000..614ab66ba --- /dev/null +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitLoggerAccessor.java @@ -0,0 +1,34 @@ +package net.imprex.orebfuscator.iterop; + +import java.util.Objects; +import java.util.logging.Logger; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import dev.imprex.orebfuscator.logging.LogLevel; +import dev.imprex.orebfuscator.logging.LoggerAccessor; + +public class BukkitLoggerAccessor implements LoggerAccessor { + + private final Logger logger; + + public BukkitLoggerAccessor(@NotNull Logger logger) { + this.logger = Objects.requireNonNull(logger, "Plugin logger can't be null"); + } + + @Override + public void log(@NotNull LogLevel level, @NotNull String message, @Nullable Throwable throwable) { + var mappedLevel = switch (level) { + case DEBUG, INFO -> java.util.logging.Level.INFO; + case WARN -> java.util.logging.Level.WARNING; + case ERROR -> java.util.logging.Level.SEVERE; + }; + + if (level == LogLevel.DEBUG) { + message = "[Debug] " + message; + } + + this.logger.log(mappedLevel, message, throwable); + } +} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/HeightAccessor.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitWorldAccessor.java similarity index 57% rename from orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/HeightAccessor.java rename to orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitWorldAccessor.java index b97331e3e..fb360abe3 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/HeightAccessor.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/iterop/BukkitWorldAccessor.java @@ -1,38 +1,48 @@ -package net.imprex.orebfuscator.util; +package net.imprex.orebfuscator.iterop; +import java.util.Collection; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.plugin.Plugin; import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.MethodAccessor; -import net.imprex.orebfuscator.chunk.ChunkCapabilities; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.logging.OfcLogger; +import net.imprex.orebfuscator.util.MinecraftVersion; -public class HeightAccessor { +public class BukkitWorldAccessor implements WorldAccessor { - private static final Map ACCESSOR_LOOKUP = new ConcurrentHashMap<>(); + private static final boolean HAS_DYNAMIC_HEIGHT = MinecraftVersion.isAtOrAbove("1.17"); - public static HeightAccessor get(World world) { - return ACCESSOR_LOOKUP.computeIfAbsent(world, HeightAccessor::new); + private static final Map ACCESSOR_LOOKUP = new ConcurrentHashMap<>(); + + public static BukkitWorldAccessor get(World world) { + return ACCESSOR_LOOKUP.computeIfAbsent(world, key -> { + OfcLogger.warn("Created world accessor outside of event!"); + return new BukkitWorldAccessor(key); + }); } private static final MethodAccessor WORLD_GET_MAX_HEIGHT = getWorldMethod("getMaxHeight"); private static final MethodAccessor WORLD_GET_MIN_HEIGHT = getWorldMethod("getMinHeight"); private static MethodAccessor getWorldMethod(String methodName) { - if (ChunkCapabilities.hasDynamicHeight()) { + if (HAS_DYNAMIC_HEIGHT) { MethodAccessor methodAccessor = getWorldMethod0(World.class, methodName); if (methodAccessor == null) { throw new RuntimeException("unable to find method: World::" + methodName + "()"); } - OFCLogger.debug("HeightAccessor found method: World::" + methodName + "()"); + OfcLogger.debug("HeightAccessor found method: World::" + methodName + "()"); return methodAccessor; } return null; @@ -56,8 +66,22 @@ private static int blockToSectionCoord(int block) { return block >> 4; } + public static Collection getWorlds() { + return ACCESSOR_LOOKUP.values(); + } + public static void registerListener(Plugin plugin) { + for (World world : Bukkit.getWorlds()) { + ACCESSOR_LOOKUP.put(world, new BukkitWorldAccessor(world)); + } + Bukkit.getPluginManager().registerEvents(new Listener() { + @EventHandler + public void onWorldUnload(WorldLoadEvent event) { + World world = event.getWorld(); + ACCESSOR_LOOKUP.put(world, new BukkitWorldAccessor(world)); + } + @EventHandler public void onWorldUnload(WorldUnloadEvent event) { ACCESSOR_LOOKUP.remove(event.getWorld()); @@ -65,15 +89,15 @@ public void onWorldUnload(WorldUnloadEvent event) { }, plugin); } - private final String worldName; + public final World world; private final int maxHeight; private final int minHeight; - private HeightAccessor(World world) { - this.worldName = world.getName(); + private BukkitWorldAccessor(World world) { + this.world = Objects.requireNonNull(world); - if (ChunkCapabilities.hasDynamicHeight()) { + if (HAS_DYNAMIC_HEIGHT) { this.maxHeight = (int) WORLD_GET_MAX_HEIGHT.invoke(world); this.minHeight = (int) WORLD_GET_MIN_HEIGHT.invoke(world); } else { @@ -82,36 +106,61 @@ private HeightAccessor(World world) { } } + @Override + public String getName() { + return this.world.getName(); + } + + @Override public int getHeight() { return this.maxHeight - this.minHeight; } + @Override public int getMinBuildHeight() { return this.minHeight; } + @Override public int getMaxBuildHeight() { return this.maxHeight; } + @Override public int getSectionCount() { return this.getMaxSection() - this.getMinSection(); } + @Override public int getMinSection() { return blockToSectionCoord(this.getMinBuildHeight()); } + @Override public int getMaxSection() { return blockToSectionCoord(this.getMaxBuildHeight() - 1) + 1; } + @Override public int getSectionIndex(int y) { return blockToSectionCoord(y) - getMinSection(); } + @Override + public int hashCode() { + return this.world.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + return obj instanceof BukkitWorldAccessor other && this.world.equals(other.world); + } + @Override public String toString() { - return String.format("[%s, minY=%s, maxY=%s]", worldName, minHeight, maxHeight); + return String.format("[%s, minY=%s, maxY=%s]", world.getName(), minHeight, maxHeight); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationListener.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationListener.java index f6018f246..7324b8fae 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationListener.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationListener.java @@ -19,9 +19,9 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.UpdateSystem; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; import net.imprex.orebfuscator.util.ConsoleUtil; import net.imprex.orebfuscator.util.PermissionUtil; diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationWorker.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationWorker.java index f562583e1..ae43085a3 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationWorker.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/DeobfuscationWorker.java @@ -10,15 +10,16 @@ import com.google.common.collect.Iterables; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.config.api.BlockFlags; +import dev.imprex.orebfuscator.config.api.ObfuscationConfig; +import dev.imprex.orebfuscator.config.api.WorldConfigBundle; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.ChunkCacheKey; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.OrebfuscatorNms; import net.imprex.orebfuscator.cache.ObfuscationCache; -import net.imprex.orebfuscator.config.BlockFlags; -import net.imprex.orebfuscator.config.ObfuscationConfig; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; -import net.imprex.orebfuscator.config.WorldConfigBundle; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.ChunkPosition; +import net.imprex.orebfuscator.iterop.BukkitWorldAccessor; public class DeobfuscationWorker { @@ -44,7 +45,8 @@ public void deobfuscate(Collection blocks, boolean occluding) { } World world = Iterables.get(blocks, 0).getWorld(); - WorldConfigBundle bundle = this.config.world(world); + BukkitWorldAccessor worldAccessor = BukkitWorldAccessor.get(world); + WorldConfigBundle bundle = this.config.world(worldAccessor); ObfuscationConfig obfuscationConfig = bundle.obfuscation(); if (obfuscationConfig == null || !obfuscationConfig.isEnabled()) { @@ -54,7 +56,7 @@ public void deobfuscate(Collection blocks, boolean occluding) { int updateRadius = this.config.general().updateRadius(); BlockFlags blockFlags = bundle.blockFlags(); - try (Processor processor = new Processor(world, blockFlags)) { + try (Processor processor = new Processor(worldAccessor, blockFlags)) { for (Block block : blocks) { if (!occluding || block.getType().isOccluding()) { BlockPos position = new BlockPos(block.getX(), block.getY(), block.getZ()); @@ -67,23 +69,23 @@ public void deobfuscate(Collection blocks, boolean occluding) { public class Processor implements AutoCloseable { private final Set updatedBlocks = new HashSet<>(); - private final Set invalidChunks = new HashSet<>(); + private final Set invalidChunks = new HashSet<>(); - private final World world; + private final BukkitWorldAccessor worldAccessor; private final BlockFlags blockFlags; - public Processor(World world, BlockFlags blockFlags) { - this.world = world; + public Processor(BukkitWorldAccessor worldAccessor, BlockFlags blockFlags) { + this.worldAccessor = worldAccessor; this.blockFlags = blockFlags; } public void processPosition(BlockPos position, int depth) { - int blockId = OrebfuscatorNms.getBlockState(this.world, position); + int blockId = OrebfuscatorNms.getBlockState(worldAccessor.world, position); if (BlockFlags.isObfuscateBitSet(blockFlags.flags(blockId)) && updatedBlocks.add(position)) { // invalidate cache if enabled if (config.cache().enabled()) { - ChunkPosition chunkPosition = position.toChunkPosition(world); + ChunkCacheKey chunkPosition = new ChunkCacheKey(worldAccessor, position); if (this.invalidChunks.add(chunkPosition)) { cache.invalidate(chunkPosition); } @@ -102,7 +104,7 @@ public void processPosition(BlockPos position, int depth) { @Override public void close() { - OrebfuscatorNms.sendBlockUpdates(this.world, this.updatedBlocks); + OrebfuscatorNms.sendBlockUpdates(worldAccessor.world, this.updatedBlocks); } } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java index 10e4df55b..bd4af52d0 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationListener.java @@ -19,16 +19,18 @@ import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketEvent; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.config.api.AdvancedConfig; +import dev.imprex.orebfuscator.logging.OfcLogger; +import dev.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.OrebfuscatorCompatibility; -import net.imprex.orebfuscator.chunk.ChunkStruct; -import net.imprex.orebfuscator.config.AdvancedConfig; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; +import net.imprex.orebfuscator.iterop.BukkitChunkPacketAccessor; +import net.imprex.orebfuscator.iterop.BukkitWorldAccessor; import net.imprex.orebfuscator.player.OrebfuscatorPlayer; import net.imprex.orebfuscator.player.OrebfuscatorPlayerMap; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.OFCLogger; import net.imprex.orebfuscator.util.PermissionUtil; +import net.imprex.orebfuscator.util.RollingAverage; import net.imprex.orebfuscator.util.ServerVersion; public class ObfuscationListener extends PacketAdapter { @@ -55,6 +57,9 @@ private static PacketType tryGetPacketType(PacketTypeEnum packetTypeEnum, String private final AsynchronousManager asynchronousManager; private final AsyncListenerHandler asyncListenerHandler; + private final RollingAverage originalSize = new RollingAverage(2048); + private final RollingAverage obfuscatedSize = new RollingAverage(2048); + public ObfuscationListener(Orebfuscator orebfuscator) { super(orebfuscator, PACKET_TYPES.stream() .filter(Objects::nonNull) @@ -73,6 +78,10 @@ public ObfuscationListener(Orebfuscator orebfuscator) { } else { this.asyncListenerHandler.start(); } + + var statistics = orebfuscator.getStatistics(); + statistics.setOriginalChunkSize(() -> (long) originalSize.average()); + statistics.setObfuscatedChunkSize(() -> (long) obfuscatedSize.average()); } public void unregister() { @@ -91,19 +100,20 @@ public void onPacketSending(PacketEvent event) { } Player player = event.getPlayer(); - if (this.shouldNotObfuscate(player)) { + BukkitWorldAccessor worldAccessor = BukkitWorldAccessor.get(player.getWorld()); + if (this.shouldNotObfuscate(player, worldAccessor)) { return; } - ChunkStruct struct = new ChunkStruct(event.getPacket(), player.getWorld()); - if (struct.isEmpty()) { + var packet = new BukkitChunkPacketAccessor(event.getPacket(), worldAccessor); + if (packet.isEmpty()) { return; } // delay packet event.getAsyncMarker().incrementProcessingDelay(); - CompletableFuture future = this.obfuscationSystem.obfuscate(struct); + CompletableFuture future = this.obfuscationSystem.obfuscate(packet); AdvancedConfig advancedConfig = this.config.advanced(); if (advancedConfig.hasObfuscationTimeout()) { @@ -112,44 +122,47 @@ public void onPacketSending(PacketEvent event) { future.whenComplete((chunk, throwable) -> { if (throwable != null) { - this.completeExceptionally(event, struct, throwable); + this.completeExceptionally(event, packet, throwable); } else if (chunk != null) { - this.complete(event, struct, chunk); + this.complete(event, packet, chunk); } else { - OFCLogger.warn(String.format("skipping chunk[world=%s, x=%d, z=%d] because obfuscation result is missing", - struct.world.getName(), struct.chunkX, struct.chunkZ)); + OfcLogger.warn(String.format("skipping chunk[world=%s, x=%d, z=%d] because obfuscation result is missing", + packet.worldAccessor.getName(), packet.chunkX(), packet.chunkZ())); this.asynchronousManager.signalPacketTransmission(event); } }); } - private boolean shouldNotObfuscate(Player player) { - return PermissionUtil.canBypassObfuscate(player) || !config.world(player.getWorld()).needsObfuscation(); + private boolean shouldNotObfuscate(Player player, BukkitWorldAccessor worldAccessor) { + return PermissionUtil.canBypassObfuscate(player) || !config.world(worldAccessor).needsObfuscation(); } - private void completeExceptionally(PacketEvent event, ChunkStruct struct, Throwable throwable) { + private void completeExceptionally(PacketEvent event, BukkitChunkPacketAccessor packet, Throwable throwable) { if (throwable instanceof TimeoutException) { - OFCLogger.warn(String.format("Obfuscation for chunk[world=%s, x=%d, z=%d] timed out", - struct.world.getName(), struct.chunkX, struct.chunkZ)); + OfcLogger.warn(String.format("Obfuscation for chunk[world=%s, x=%d, z=%d] timed out", + packet.worldAccessor.getName(), packet.chunkX(), packet.chunkZ())); } else { - OFCLogger.error(String.format("An error occurred while obfuscating chunk[world=%s, x=%d, z=%d]", - struct.world.getName(), struct.chunkX, struct.chunkZ), throwable); + OfcLogger.error(String.format("An error occurred while obfuscating chunk[world=%s, x=%d, z=%d]", + packet.worldAccessor.getName(), packet.chunkX(), packet.chunkZ()), throwable); } this.asynchronousManager.signalPacketTransmission(event); } - private void complete(PacketEvent event, ChunkStruct struct, ObfuscationResult chunk) { - struct.setDataBuffer(chunk.getData()); + private void complete(PacketEvent event, BukkitChunkPacketAccessor packet, ObfuscationResult chunk) { + originalSize.add(packet.data().length); + obfuscatedSize.add(chunk.getData().length); + + packet.setData(chunk.getData()); Set blockEntities = chunk.getBlockEntities(); if (!blockEntities.isEmpty()) { - struct.removeBlockEntityIf(blockEntities::contains); + packet.filterBlockEntities(blockEntities::contains); } final OrebfuscatorPlayer player = this.playerMap.get(event.getPlayer()); if (player != null) { - player.addChunk(struct.chunkX, struct.chunkZ, chunk.getProximityBlocks()); + player.addChunk(packet.chunkX(), packet.chunkZ(), chunk.getProximityBlocks()); } this.asynchronousManager.signalPacketTransmission(event); diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationProcessor.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationProcessor.java index 2b761fd72..1df38e0d3 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationProcessor.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationProcessor.java @@ -5,37 +5,37 @@ import java.util.List; import java.util.Set; -import org.bukkit.World; - +import dev.imprex.orebfuscator.chunk.Chunk; +import dev.imprex.orebfuscator.chunk.ChunkFactory; +import dev.imprex.orebfuscator.chunk.ChunkSection; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.config.ProximityHeightCondition; +import dev.imprex.orebfuscator.config.api.BlockFlags; +import dev.imprex.orebfuscator.config.api.ObfuscationConfig; +import dev.imprex.orebfuscator.config.api.ProximityConfig; +import dev.imprex.orebfuscator.config.api.WorldConfigBundle; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.chunk.Chunk; -import net.imprex.orebfuscator.chunk.ChunkSection; -import net.imprex.orebfuscator.chunk.ChunkStruct; -import net.imprex.orebfuscator.config.BlockFlags; -import net.imprex.orebfuscator.config.ObfuscationConfig; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; -import net.imprex.orebfuscator.config.ProximityConfig; -import net.imprex.orebfuscator.config.ProximityHeightCondition; -import net.imprex.orebfuscator.config.WorldConfigBundle; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.HeightAccessor; +import net.imprex.orebfuscator.iterop.BukkitChunkPacketAccessor; public class ObfuscationProcessor { private final OrebfuscatorConfig config; + private final ChunkFactory chunkFactory; public ObfuscationProcessor(Orebfuscator orebfuscator) { this.config = orebfuscator.getOrebfuscatorConfig(); + this.chunkFactory = orebfuscator.getChunkFactory(); } public void process(ObfuscationTask task) { - ChunkStruct chunkStruct = task.getChunkStruct(); + BukkitChunkPacketAccessor packet = task.getPacket(); - World world = chunkStruct.world; - HeightAccessor heightAccessor = HeightAccessor.get(world); + WorldAccessor worldAccessor = packet.worldAccessor; - WorldConfigBundle bundle = this.config.world(world); + WorldConfigBundle bundle = this.config.world(worldAccessor); BlockFlags blockFlags = bundle.blockFlags(); ObfuscationConfig obfuscationConfig = bundle.obfuscation(); ProximityConfig proximityConfig = bundle.proximity(); @@ -43,13 +43,13 @@ public void process(ObfuscationTask task) { Set blockEntities = new HashSet<>(); List proximityBlocks = new ArrayList<>(); - int baseX = chunkStruct.chunkX << 4; - int baseZ = chunkStruct.chunkZ << 4; + int baseX = packet.chunkX() << 4; + int baseZ = packet.chunkZ() << 4; int layerY = Integer.MIN_VALUE; int layerYBlockState = -1; - try (Chunk chunk = Chunk.fromChunkStruct(chunkStruct)) { + try (Chunk chunk = this.chunkFactory.fromPacket(packet)) { for (int sectionIndex = Math.max(0, bundle.minSectionIndex()); sectionIndex <= Math .min(chunk.getSectionCount() - 1, bundle.maxSectionIndex()); sectionIndex++) { ChunkSection chunkSection = chunk.getSection(sectionIndex); @@ -57,7 +57,7 @@ public void process(ObfuscationTask task) { continue; } - final int baseY = heightAccessor.getMinBuildHeight() + (sectionIndex << 4); + final int baseY = worldAccessor.getMinBuildHeight() + (sectionIndex << 4); for (int index = 0; index < 4096; index++) { int y = baseY + (index >> 8 & 15); if (!bundle.shouldObfuscate(y)) { @@ -124,7 +124,7 @@ public void process(ObfuscationTask task) { private int getBlockStateBelow(WorldConfigBundle bundle, Chunk chunk, int x, int y, int z, boolean allowNonOcclude) { BlockFlags blockFlags = bundle.blockFlags(); - for (int targetY = y - 1; targetY > chunk.getHeightAccessor().getMinBuildHeight(); targetY--) { + for (int targetY = y - 1; targetY > chunk.world().getMinBuildHeight(); targetY--) { int blockData = chunk.getBlockState(x, targetY, z); if (blockData != -1 && (allowNonOcclude || OrebfuscatorNms.isOccluding(blockData))) { int mask = blockFlags.flags(blockData, y); @@ -147,7 +147,7 @@ && isAdjacentBlockOccluding(task, chunk, x, y, z + 1) } private boolean isAdjacentBlockOccluding(ObfuscationTask task, Chunk chunk, int x, int y, int z) { - if (y >= chunk.getHeightAccessor().getMaxBuildHeight() || y < chunk.getHeightAccessor().getMinBuildHeight()) { + if (y >= chunk.world().getMaxBuildHeight() || y < chunk.world().getMinBuildHeight()) { return false; } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationRequest.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationRequest.java index 394508a8c..b784ed6f6 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationRequest.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationRequest.java @@ -7,10 +7,10 @@ import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; -import net.imprex.orebfuscator.chunk.ChunkStruct; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.ChunkCacheKey; +import net.imprex.orebfuscator.iterop.BukkitChunkPacketAccessor; public class ObfuscationRequest { @@ -23,42 +23,42 @@ private static final byte[] hash(byte[] systemHash, byte[] data) { return HASH_FUNCTION.newHasher().putBytes(systemHash).putBytes(data).hash().asBytes(); } - public static ObfuscationRequest fromChunk(ChunkStruct struct, OrebfuscatorConfig config, + public static ObfuscationRequest fromChunk(BukkitChunkPacketAccessor packet, OrebfuscatorConfig config, ObfuscationTaskDispatcher dispatcher) { - ChunkPosition position = new ChunkPosition(struct.world, struct.chunkX, struct.chunkZ); - byte[] hash = config.cache().enabled() ? hash(config.systemHash(), struct.data) : EMPTY_HASH; - return new ObfuscationRequest(dispatcher, position, hash, struct); + byte[] hash = config.cache().enabled() ? hash(config.systemHash(), packet.data()) : EMPTY_HASH; + return new ObfuscationRequest(dispatcher, packet, hash); } private final CompletableFuture future = new CompletableFuture<>(); private final ObfuscationTaskDispatcher dispatcher; - private final ChunkPosition position; + private final BukkitChunkPacketAccessor packet; + + private final ChunkCacheKey chunkCacheKey; private final byte[] chunkHash; - private final ChunkStruct chunkStruct; - private ObfuscationRequest(ObfuscationTaskDispatcher dispatcher, ChunkPosition position, byte[] chunkHash, - ChunkStruct chunkStruct) { + private ObfuscationRequest(ObfuscationTaskDispatcher dispatcher, BukkitChunkPacketAccessor packet, byte[] chunkHash) { this.dispatcher = dispatcher; - this.position = position; + this.packet = packet; + + this.chunkCacheKey = new ChunkCacheKey(packet.world().getName(), packet.chunkX(), packet.chunkZ()); this.chunkHash = chunkHash; - this.chunkStruct = chunkStruct; } public CompletableFuture getFuture() { return future; } - public ChunkPosition getPosition() { - return position; + public ChunkCacheKey getCacheKey() { + return chunkCacheKey; } public byte[] getChunkHash() { return chunkHash; } - public ChunkStruct getChunkStruct() { - return chunkStruct; + public BukkitChunkPacketAccessor getPacket() { + return packet; } public CompletableFuture submitForObfuscation() { @@ -67,7 +67,7 @@ public CompletableFuture submitForObfuscation() { } public ObfuscationResult createResult(byte[] data, Set blockEntities, List proximityBlocks) { - return new ObfuscationResult(this.position, this.chunkHash, data, blockEntities, proximityBlocks); + return new ObfuscationResult(this.chunkCacheKey, this.chunkHash, data, blockEntities, proximityBlocks); } public CompletableFuture complete(ObfuscationResult result) { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationResult.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationResult.java index 5c852b981..dcd63cb43 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationResult.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationResult.java @@ -5,12 +5,12 @@ import java.util.List; import java.util.Set; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.ChunkPosition; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.ChunkCacheKey; public class ObfuscationResult { - private final ChunkPosition position; + private final ChunkCacheKey key; private final byte[] hash; private final byte[] data; @@ -18,21 +18,21 @@ public class ObfuscationResult { private final Set blockEntities; private final List proximityBlocks; - public ObfuscationResult(ChunkPosition position, byte[] hash, byte[] data) { - this(position, hash, data, new HashSet<>(), new ArrayList<>()); + public ObfuscationResult(ChunkCacheKey key, byte[] hash, byte[] data) { + this(key, hash, data, new HashSet<>(), new ArrayList<>()); } - public ObfuscationResult(ChunkPosition position, byte[] hash, byte[] data, + public ObfuscationResult(ChunkCacheKey key, byte[] hash, byte[] data, Set blockEntities, List proximityBlocks) { - this.position = position; + this.key = key; this.hash = hash; this.data = data; this.blockEntities = blockEntities; this.proximityBlocks = proximityBlocks; } - public ChunkPosition getPosition() { - return position; + public ChunkCacheKey getCacheKey() { + return key; } public byte[] getHash() { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationSystem.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationSystem.java index 7238a468f..9ad42919d 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationSystem.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationSystem.java @@ -5,10 +5,10 @@ import org.bukkit.block.Block; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.cache.ObfuscationCache; -import net.imprex.orebfuscator.chunk.ChunkStruct; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; +import net.imprex.orebfuscator.iterop.BukkitChunkPacketAccessor; public class ObfuscationSystem { @@ -38,8 +38,8 @@ public void registerChunkListener() { this.listener = new ObfuscationListener(orebfuscator); } - public CompletableFuture obfuscate(ChunkStruct chunkStruct) { - ObfuscationRequest request = ObfuscationRequest.fromChunk(chunkStruct, this.config, this.dispatcher); + public CompletableFuture obfuscate(BukkitChunkPacketAccessor packet) { + ObfuscationRequest request = ObfuscationRequest.fromChunk(packet, this.config, this.dispatcher); if (this.config.cache().enabled()) { return this.cache.get(request); } else { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTask.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTask.java index 153d6444e..a9332a6ef 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTask.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTask.java @@ -6,20 +6,20 @@ import org.bukkit.World; +import dev.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.ChunkCacheKey; +import dev.imprex.orebfuscator.util.ChunkDirection; import net.imprex.orebfuscator.OrebfuscatorCompatibility; -import net.imprex.orebfuscator.chunk.ChunkStruct; +import net.imprex.orebfuscator.iterop.BukkitChunkPacketAccessor; import net.imprex.orebfuscator.nms.ReadOnlyChunk; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.ChunkDirection; -import net.imprex.orebfuscator.util.ChunkPosition; public class ObfuscationTask { public static CompletableFuture fromRequest(ObfuscationRequest request) { - World world = request.getChunkStruct().world; - ChunkPosition position = request.getPosition(); + World world = request.getPacket().worldAccessor.world; + ChunkCacheKey key = request.getCacheKey(); - return OrebfuscatorCompatibility.getNeighboringChunks(world, position) + return OrebfuscatorCompatibility.getNeighboringChunks(world, key) .thenApply(chunks -> new ObfuscationTask(request, chunks)); } @@ -35,8 +35,8 @@ private ObfuscationTask(ObfuscationRequest request, ReadOnlyChunk[] neighboringC this.neighboringChunks = neighboringChunks; } - public ChunkStruct getChunkStruct() { - return this.request.getChunkStruct(); + public BukkitChunkPacketAccessor getPacket() { + return this.request.getPacket(); } public void complete(byte[] data, Set blockEntities, List proximityBlocks) { @@ -48,7 +48,7 @@ public void completeExceptionally(Throwable throwable) { } public int getBlockState(int x, int y, int z) { - ChunkDirection direction = ChunkDirection.fromPosition(request.getPosition(), x, z); + ChunkDirection direction = ChunkDirection.fromPosition(request.getCacheKey(), x, z); return this.neighboringChunks[direction.ordinal()].getBlockState(x, y, z); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTaskDispatcher.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTaskDispatcher.java index bff70473f..d290ad5d5 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTaskDispatcher.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/obfuscation/ObfuscationTaskDispatcher.java @@ -6,8 +6,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; +import dev.imprex.orebfuscator.config.api.AdvancedConfig; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.config.AdvancedConfig; class ObfuscationTaskDispatcher { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayer.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayer.java index cba1047a6..36f898c18 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayer.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayer.java @@ -11,13 +11,16 @@ import com.google.common.base.Objects; +import dev.imprex.orebfuscator.config.api.AdvancedConfig; +import dev.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.config.AdvancedConfig; -import net.imprex.orebfuscator.util.BlockPos; -import net.imprex.orebfuscator.util.ChunkPosition; public class OrebfuscatorPlayer { + private static long chunkCoordsToLong(int chunkX, int chunkZ) { + return (chunkZ & 0xffffffffL) << 32 | chunkX & 0xffffffffL; + } + private final Player player; private final AdvancedConfig config; @@ -112,15 +115,15 @@ void updateWorld() { } public void addChunk(int chunkX, int chunkZ, List blocks) { - this.chunks.put(ChunkPosition.toLong(chunkX, chunkZ), + this.chunks.put(chunkCoordsToLong(chunkX, chunkZ), new OrebfuscatorPlayerChunk(chunkX, chunkZ, blocks)); } public OrebfuscatorPlayerChunk getChunk(int chunkX, int chunkZ) { - return this.chunks.get(ChunkPosition.toLong(chunkX, chunkZ)); + return this.chunks.get(chunkCoordsToLong(chunkX, chunkZ)); } public void removeChunk(int chunkX, int chunkZ) { - this.chunks.remove(ChunkPosition.toLong(chunkX, chunkZ)); + this.chunks.remove(chunkCoordsToLong(chunkX, chunkZ)); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayerChunk.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayerChunk.java index 4fca83dbf..6320e0967 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayerChunk.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/player/OrebfuscatorPlayerChunk.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.NoSuchElementException; -import net.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.BlockPos; public class OrebfuscatorPlayerChunk { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityDirectorThread.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityDirectorThread.java index e62912ec9..a4225d255 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityDirectorThread.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityDirectorThread.java @@ -16,9 +16,9 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import dev.imprex.orebfuscator.config.api.AdvancedConfig; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.config.AdvancedConfig; -import net.imprex.orebfuscator.util.OFCLogger; +import dev.imprex.orebfuscator.logging.OfcLogger; import net.imprex.orebfuscator.util.RingTimer; public class ProximityDirectorThread extends Thread implements Listener { @@ -180,7 +180,7 @@ public void run() { } if (this.phaser.isTerminated() && this.running) { - OFCLogger.error("Looks like we encountered an invalid state, please report this:", + OfcLogger.error("Looks like we encountered an invalid state, please report this:", new IllegalStateException("Proximity Phaser terminated!")); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityPacketListener.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityPacketListener.java index d19e18b46..5d79c99b6 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityPacketListener.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityPacketListener.java @@ -1,6 +1,5 @@ package net.imprex.orebfuscator.proximity; -import org.bukkit.World; import org.bukkit.entity.Player; import com.comphenix.protocol.PacketType; @@ -12,16 +11,20 @@ import com.comphenix.protocol.reflect.StructureModifier; import com.comphenix.protocol.wrappers.ChunkCoordIntPair; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.config.api.ProximityConfig; +import dev.imprex.orebfuscator.interop.WorldAccessor; import net.imprex.orebfuscator.Orebfuscator; -import net.imprex.orebfuscator.chunk.ChunkCapabilities; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; -import net.imprex.orebfuscator.config.ProximityConfig; +import net.imprex.orebfuscator.iterop.BukkitWorldAccessor; import net.imprex.orebfuscator.player.OrebfuscatorPlayer; import net.imprex.orebfuscator.player.OrebfuscatorPlayerMap; +import net.imprex.orebfuscator.util.MinecraftVersion; import net.imprex.orebfuscator.util.PermissionUtil; public class ProximityPacketListener extends PacketAdapter { + private static final boolean HAS_CHUNK_POS_FIELD = MinecraftVersion.isAtOrAbove("1.20.2"); + private final ProtocolManager protocolManager; private final OrebfuscatorConfig config; @@ -48,8 +51,8 @@ public void onPacketSending(PacketEvent event) { return; } - World world = player.getWorld(); - ProximityConfig proximityConfig = config.world(world).proximity(); + WorldAccessor worldAccessor = BukkitWorldAccessor.get(player.getWorld()); + ProximityConfig proximityConfig = config.world(worldAccessor).proximity(); if (proximityConfig == null || !proximityConfig.isEnabled()) { return; } @@ -57,7 +60,7 @@ public void onPacketSending(PacketEvent event) { OrebfuscatorPlayer orebfuscatorPlayer = this.playerMap.get(player); if (orebfuscatorPlayer != null) { PacketContainer packet = event.getPacket(); - if (ChunkCapabilities.hasChunkPosFieldUnloadPacket()) { + if (HAS_CHUNK_POS_FIELD) { ChunkCoordIntPair chunkPos = packet.getChunkCoordIntPairs().read(0); orebfuscatorPlayer.removeChunk(chunkPos.getChunkX(), chunkPos.getChunkZ()); } else { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityWorker.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityWorker.java index 8df120430..0e22f59cb 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityWorker.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/proximity/ProximityWorker.java @@ -11,15 +11,17 @@ import org.joml.FrustumIntersection; import org.joml.Quaternionf; +import dev.imprex.orebfuscator.config.OrebfuscatorConfig; +import dev.imprex.orebfuscator.config.api.ProximityConfig; +import dev.imprex.orebfuscator.interop.WorldAccessor; +import dev.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.Orebfuscator; import net.imprex.orebfuscator.OrebfuscatorCompatibility; import net.imprex.orebfuscator.OrebfuscatorNms; -import net.imprex.orebfuscator.config.OrebfuscatorConfig; -import net.imprex.orebfuscator.config.ProximityConfig; +import net.imprex.orebfuscator.iterop.BukkitWorldAccessor; import net.imprex.orebfuscator.player.OrebfuscatorPlayer; import net.imprex.orebfuscator.player.OrebfuscatorPlayerChunk; import net.imprex.orebfuscator.player.OrebfuscatorPlayerMap; -import net.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.util.FastGazeUtil; import net.imprex.orebfuscator.util.PermissionUtil; @@ -57,9 +59,10 @@ private void process(Player player) { } World world = player.getWorld(); + WorldAccessor worldAccessor = BukkitWorldAccessor.get(world); // check if world has enabled proximity config - ProximityConfig proximityConfig = this.config.world(world).proximity(); + ProximityConfig proximityConfig = this.config.world(worldAccessor).proximity(); if (proximityConfig == null || !proximityConfig.isEnabled()) { return; } @@ -122,8 +125,8 @@ private void process(Player player) { // check if block AABB is inside frustum int result = frustum.intersectAab( - blockPos.x, blockPos.y, blockPos.z, - blockPos.x + 1, blockPos.y + 1, blockPos.z + 1); + blockPos.x(), blockPos.y(), blockPos.z(), + blockPos.x() + 1, blockPos.y() + 1, blockPos.z() + 1); // block is outside if (result != FrustumIntersection.INSIDE && result != FrustumIntersection.INTERSECT) { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/AbstractHttpService.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/AbstractHttpService.java index 60b999218..e64218540 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/AbstractHttpService.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/AbstractHttpService.java @@ -16,6 +16,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import dev.imprex.orebfuscator.util.Version; import net.imprex.orebfuscator.Orebfuscator; public abstract class AbstractHttpService { diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/ConsoleUtil.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/ConsoleUtil.java index d5bdcd310..a9f4845f7 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/ConsoleUtil.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/ConsoleUtil.java @@ -3,10 +3,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.logging.Level; import org.bukkit.ChatColor; +import dev.imprex.orebfuscator.logging.LogLevel; +import dev.imprex.orebfuscator.logging.OfcLogger; + public final class ConsoleUtil { private static final int BOX_PADDING = 3; @@ -22,9 +24,9 @@ public static String replaceAnsiColorWithChatColor(String value) { return value; } - public static void printBox(Level level, String...lines) { + public static void printBox(LogLevel level, String...lines) { for (String line : createBox(lines)) { - OFCLogger.log(level, line); + OfcLogger.log(level, line); } } diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/FastGazeUtil.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/FastGazeUtil.java index 7b1afc110..f928e01aa 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/FastGazeUtil.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/FastGazeUtil.java @@ -3,6 +3,7 @@ import org.bukkit.Location; import org.bukkit.World; +import dev.imprex.orebfuscator.util.BlockPos; import net.imprex.orebfuscator.OrebfuscatorNms; public class FastGazeUtil { @@ -21,9 +22,9 @@ public static boolean doFastCheck(BlockPos pos, Location eyes, World player, boo double ex = eyes.getX(); double ey = eyes.getY(); double ez = eyes.getZ(); - double x = pos.x; - double y = pos.y; - double z = pos.z; + double x = pos.x(); + double y = pos.y(); + double z = pos.z(); if (onlyCheckCenter) { return // center FastGazeUtil.fastAABBRayCheck(x, y, z, x + 0.5, y + 0.5, z + 0.5, ex, ey, ez, player); diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/RollingAverage.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/RollingAverage.java new file mode 100644 index 000000000..5d665e7f6 --- /dev/null +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/RollingAverage.java @@ -0,0 +1,42 @@ +package net.imprex.orebfuscator.util; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.concurrent.atomic.AtomicInteger; + +public class RollingAverage { + + private static final VarHandle BUFFER_HANDLE = MethodHandles.arrayElementVarHandle(long[].class); + + private final long[] buffer; + + private final AtomicInteger head = new AtomicInteger(0); + private final AtomicInteger size = new AtomicInteger(0); + + public RollingAverage(int capacity) { + this.buffer = new long[capacity]; + } + + public void add(long value) { + int index = head.getAndUpdate(h -> (h + 1) % buffer.length); + BUFFER_HANDLE.setRelease(buffer, index, value); + + if (size.get() < buffer.length) { + size.updateAndGet(s -> s < buffer.length ? s + 1 : s); + } + } + + public double average() { + int size = this.size.get(); + if (size == 0) { + return 0; + } + + double sum = 0; + for (int i = 0; i < size; i++) { + sum += (long) BUFFER_HANDLE.getAcquire(buffer, i); + } + + return sum / size; + } +} diff --git a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/WrappedClientboundLevelChunkPacketData.java b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/WrappedClientboundLevelChunkPacketData.java similarity index 96% rename from orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/WrappedClientboundLevelChunkPacketData.java rename to orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/WrappedClientboundLevelChunkPacketData.java index d634e58b9..6ee8530a4 100644 --- a/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/chunk/WrappedClientboundLevelChunkPacketData.java +++ b/orebfuscator-plugin/src/main/java/net/imprex/orebfuscator/util/WrappedClientboundLevelChunkPacketData.java @@ -1,4 +1,4 @@ -package net.imprex.orebfuscator.chunk; +package net.imprex.orebfuscator.util; import java.util.Iterator; import java.util.List; @@ -9,7 +9,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.utility.MinecraftReflection; -import net.imprex.orebfuscator.util.BlockPos; +import dev.imprex.orebfuscator.util.BlockPos; public class WrappedClientboundLevelChunkPacketData { diff --git a/orebfuscator-plugin/src/main/resources/config/config-1.17.yml b/orebfuscator-plugin/src/main/resources/config/config-1.17.yml deleted file mode 100644 index b2c5ccbdc..000000000 --- a/orebfuscator-plugin/src/main/resources/config/config-1.17.yml +++ /dev/null @@ -1,410 +0,0 @@ -version: 4 -general: - checkForUpdates: true - updateOnBlockDamage: true - bypassNotification: true - ignoreSpectator: false - updateRadius: 2 -advanced: - verbose: false - obfuscation: - threads: -1 - timeout: 10000 - maxMillisecondsPerTick: 10 - proximity: - threads: -1 - defaultBucketSize: 50 - threadCheckInterval: 50 - playerCheckInterval: 5000 -cache: - enabled: true - memoryCache: - maximumSize: 32768 - expireAfterAccess: 60000 - diskCache: - enabled: true - directory: orebfuscator_cache - maximumOpenFiles: 256 - deleteFilesAfterAccess: 172800000 - maximumTaskQueueSize: 32768 -obfuscation: - obfuscation-overworld: - enabled: true - minY: -2032 - maxY: 2031 - worlds: - - world - layerObfuscation: false - hiddenBlocks: - - minecraft:emerald_ore - - minecraft:deepslate_emerald_ore - - minecraft:diamond_ore - - minecraft:deepslate_diamond_ore - - minecraft:gold_ore - - minecraft:deepslate_gold_ore - - minecraft:iron_ore - - minecraft:deepslate_iron_ore - - minecraft:coal_ore - - minecraft:deepslate_coal_ore - - minecraft:lapis_ore - - minecraft:deepslate_lapis_ore - - minecraft:redstone_ore - - minecraft:deepslate_redstone_ore - - minecraft:copper_ore - - minecraft:deepslate_copper_ore - - minecraft:stone - - minecraft:deepslate - - minecraft:spawner - - minecraft:chest - - minecraft:ender_chest - - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:bedrock - randomBlocks: - section-global: - minY: -2032 - maxY: 2031 - blocks: - minecraft:cave_air: 5 - minecraft:cobblestone: 1 - minecraft:mossy_cobblestone: 1 - minecraft:obsidian: 1 - section-stone: - minY: -5 - maxY: 2031 - blocks: - minecraft:stone: 15 - minecraft:diamond_ore: 10 - minecraft:emerald_ore: 5 - minecraft:gold_ore: 5 - minecraft:iron_ore: 5 - minecraft:copper_ore: 5 - minecraft:coal_ore: 5 - minecraft:lapis_ore: 5 - minecraft:redstone_ore: 5 - section-deepslate: - minY: -2032 - maxY: 5 - blocks: - minecraft:deepslate: 15 - minecraft:deepslate_diamond_ore: 10 - minecraft:deepslate_emerald_ore: 5 - minecraft:deepslate_gold_ore: 5 - minecraft:deepslate_iron_ore: 5 - minecraft:deepslate_copper_ore: 5 - minecraft:deepslate_coal_ore: 5 - minecraft:deepslate_lapis_ore: 5 - minecraft:deepslate_redstone_ore: 5 - section-bedrock: - minY: 0 - maxY: 5 - blocks: - minecraft:bedrock: 20 - obfuscation-nether: - enabled: true - minY: -2032 - maxY: 2031 - worlds: - - world_nether - layerObfuscation: false - hiddenBlocks: - - minecraft:netherrack - - minecraft:nether_quartz_ore - - minecraft:spawner - - minecraft:chest - - minecraft:ender_chest - - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - - minecraft:ancient_debris - - minecraft:nether_gold_ore - - minecraft:basalt - - minecraft:soul_sand - - minecraft:soul_soil - - minecraft:bedrock - randomBlocks: - section-global: - minY: -2032 - maxY: 2032 - blocks: - minecraft:netherrack: 4 - minecraft:nether_quartz_ore: 1 - minecraft:nether_gold_ore: 1 - minecraft:ancient_debris: 1 - section-bedrock-top: - minY: 123 - maxY: 127 - blocks: - minecraft:bedrock: 20 - section-bedrock-bottom: - minY: 0 - maxY: 4 - blocks: - minecraft:bedrock: 20 - obfuscation-end: - enabled: true - minY: -2032 - maxY: 2031 - worlds: - - world_the_end - layerObfuscation: false - hiddenBlocks: - - minecraft:spawner - - minecraft:chest - - minecraft:ender_chest - - minecraft:trapped_chest - - minecraft:shulker_box - - minecraft:white_shulker_box - - minecraft:orange_shulker_box - - minecraft:magenta_shulker_box - - minecraft:light_blue_shulker_box - - minecraft:yellow_shulker_box - - minecraft:lime_shulker_box - - minecraft:pink_shulker_box - - minecraft:gray_shulker_box - - minecraft:light_gray_shulker_box - - minecraft:cyan_shulker_box - - minecraft:purple_shulker_box - - minecraft:blue_shulker_box - - minecraft:brown_shulker_box - - minecraft:green_shulker_box - - minecraft:red_shulker_box - - minecraft:black_shulker_box - randomBlocks: - section-global: - minY: -2032 - maxY: 2032 - blocks: - minecraft:end_stone: 1 - minecraft:end_stone_bricks: 1 - minecraft:bedrock: 1 - minecraft:obsidian: 1 - minecraft:purpur_block: 1 -proximity: - proximity-overworld: - enabled: true - minY: 0 - maxY: 2031 - worlds: - - world - distance: 24 - frustumCulling: - enabled: true - minDistance: 3 - fov: 80 - rayCastCheck: - enabled: false - onlyCheckCenter: false - useBlockBelow: true - hiddenBlocks: - minecraft:mossy_cobblestone: {} - minecraft:diamond_ore: {} - minecraft:deepslate_diamond_ore: {} - minecraft:emerald_ore: {} - minecraft:deepslate_emerald_ore: {} - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} - minecraft:blast_furnace: {} - minecraft:cartography_table: {} - minecraft:fletching_table: {} - minecraft:grindstone: {} - minecraft:composter: {} - minecraft:lectern: {} - minecraft:loom: {} - minecraft:smithing_table: {} - minecraft:smoker: {} - minecraft:stonecutter: {} - minecraft:hopper: {} - minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - randomBlocks: - section-stone: - minY: -5 - maxY: 2031 - blocks: - minecraft:stone: 1 - section-deepslate: - minY: -2032 - maxY: 5 - blocks: - minecraft:deepslate: 1 - proximity-nether: - enabled: true - minY: 0 - maxY: 2031 - worlds: - - world_nether - distance: 24 - frustumCulling: - enabled: true - minDistance: 3 - fov: 80 - rayCastCheck: - enabled: false - onlyCheckCenter: false - useBlockBelow: true - hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} - minecraft:blast_furnace: {} - minecraft:cartography_table: {} - minecraft:fletching_table: {} - minecraft:grindstone: {} - minecraft:composter: {} - minecraft:lectern: {} - minecraft:loom: {} - minecraft:smithing_table: {} - minecraft:smoker: {} - minecraft:stonecutter: {} - minecraft:hopper: {} - minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - minecraft:ancient_debris: {} - minecraft:nether_gold_ore: {} - minecraft:respawn_anchor: {} - randomBlocks: - section-global: - minY: -2032 - maxY: 2032 - blocks: - minecraft:netherrack: 1 - proximity-end: - enabled: true - minY: 0 - maxY: 2031 - worlds: - - world_the_end - distance: 24 - frustumCulling: - enabled: true - minDistance: 3 - fov: 80 - rayCastCheck: - enabled: false - onlyCheckCenter: false - useBlockBelow: true - hiddenBlocks: - minecraft:chest: {} - minecraft:ender_chest: {} - minecraft:trapped_chest: {} - minecraft:anvil: {} - minecraft:crafting_table: {} - minecraft:dispenser: {} - minecraft:enchanting_table: {} - minecraft:furnace: {} - minecraft:blast_furnace: {} - minecraft:cartography_table: {} - minecraft:fletching_table: {} - minecraft:grindstone: {} - minecraft:composter: {} - minecraft:lectern: {} - minecraft:loom: {} - minecraft:smithing_table: {} - minecraft:smoker: {} - minecraft:stonecutter: {} - minecraft:hopper: {} - minecraft:spawner: {} - minecraft:shulker_box: {} - minecraft:white_shulker_box: {} - minecraft:orange_shulker_box: {} - minecraft:magenta_shulker_box: {} - minecraft:light_blue_shulker_box: {} - minecraft:yellow_shulker_box: {} - minecraft:lime_shulker_box: {} - minecraft:pink_shulker_box: {} - minecraft:gray_shulker_box: {} - minecraft:light_gray_shulker_box: {} - minecraft:cyan_shulker_box: {} - minecraft:purple_shulker_box: {} - minecraft:blue_shulker_box: {} - minecraft:brown_shulker_box: {} - minecraft:green_shulker_box: {} - minecraft:red_shulker_box: {} - minecraft:black_shulker_box: {} - minecraft:bee_nest: {} - minecraft:beehive: {} - randomBlocks: - section-global: - minY: -2032 - maxY: 2032 - blocks: - minecraft:end_stone: 1 diff --git a/orebfuscator-plugin/src/test/java/net/imprex/orebfuscator/config/ProximityHeightConditionTest.java b/orebfuscator-plugin/src/test/java/net/imprex/orebfuscator/config/ProximityHeightConditionTest.java deleted file mode 100644 index 9a9b5f665..000000000 --- a/orebfuscator-plugin/src/test/java/net/imprex/orebfuscator/config/ProximityHeightConditionTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.imprex.orebfuscator.config; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -public class ProximityHeightConditionTest { - - private static final int TEST_MIN = ProximityHeightCondition.clampY(-0xFFF); - private static final int TEST_MAX = ProximityHeightCondition.clampY(0xFFF); - - @Test - public void testCreateRemove() { - final int minY = -10; - final int maxY = 10; - - int flag = 0b101; - assertFalse(ProximityHeightCondition.isPresent(flag)); - - flag |= ProximityHeightCondition.create(minY, maxY); - assertTrue(ProximityHeightCondition.isPresent(flag)); - assertEquals(minY, ProximityHeightCondition.getMinY(flag)); - assertEquals(maxY, ProximityHeightCondition.getMaxY(flag)); - - for (int y = TEST_MIN; y <= TEST_MAX; y++) { - boolean expected = minY <= y && maxY >= y; - assertEquals(expected, ProximityHeightCondition.match(flag, y), "failed for " + y); - } - - int other = ProximityHeightCondition.create(minY, maxY); - assertTrue(ProximityHeightCondition.equals(flag, other)); - - flag = ProximityHeightCondition.remove(flag); - assertEquals(0b101, flag); - } - - @Test - public void testReadWrite() { - for (int minY = TEST_MIN; minY <= TEST_MAX; minY++) { - for (int maxY = minY; maxY <= TEST_MAX; maxY++) { - int flag = ProximityHeightCondition.create(minY, maxY); - - assertTrue(ProximityHeightCondition.isPresent(flag)); - assertEquals(minY, ProximityHeightCondition.getMinY(flag)); - assertEquals(maxY, ProximityHeightCondition.getMaxY(flag)); - } - } - } -} diff --git a/pom.xml b/pom.xml index eb4cdfd28..c60dc1a2b 100644 --- a/pom.xml +++ b/pom.xml @@ -25,17 +25,26 @@ 1.20.1-R0.1-SNAPSHOT 1.20.1-R0.1-SNAPSHOT 3.1.0 - 5.11.2 - 4.1.90.Final 5.3.0 + + + 4.1.90.Final + 33.4.8-jre + 2.13.1 + 2.4 1.10.8 1.8.0 - 3.13.0 + + 5.13.4 + 5.19.0 + 26.0.2 + + 3.14.0 3.6.0 - 3.5.1 - 1.6.0 - 2.0.3 + 3.5.3 + 1.7.2 + 2.0.4 UTF-8 @@ -54,7 +63,7 @@ orebfuscator-api orebfuscator-api-example - orebfuscator-common + orebfuscator-core orebfuscator-compatibility orebfuscator-nms orebfuscator-plugin @@ -104,12 +113,28 @@ *:* + META-INF/*.SF META-INF/*.DSA META-INF/*.RSA + + + META-INF/MANIFEST.MF + META-INF/LICENSE* + META-INF/NOTICE* + META-INF/maven/** + + + module-info.class + META-INF/versions/**/module-info.class + + + + @@ -161,12 +186,25 @@ - + org.junit.jupiter junit-jupiter-engine ${dependency.junit.version} test + + org.mockito + mockito-core + ${dependency.mockito.version} + test + + + + org.jetbrains + annotations + ${dependency.jetannotations.version} + provided +