diff --git a/build.gradle b/build.gradle index 8ebc4e144..69be3e782 100644 --- a/build.gradle +++ b/build.gradle @@ -18,6 +18,12 @@ allprojects { repositories { mavenLocal() mavenCentral() + exclusiveContent { + forRepository { + maven { url "https://cursemaven.com" } + } + filter { includeGroup "curse.maven" } + } } version = "${minecraft_version}-${mod_version}" diff --git a/common/build.gradle b/common/build.gradle index a632fd4ee..0858c89d8 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -11,6 +11,8 @@ dependencies { implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jackson_version}" implementation "com.fasterxml.jackson.core:jackson-databind:${jackson_version}" implementation "org.yaml:snakeyaml:${snakeyaml_version}" + implementation 'it.unimi.dsi:fastutil:7.1.0' + implementation 'org.apache.commons:commons-lang3:3.5' } shadowJar { diff --git a/common/src/main/java/com/pg85/otg/OTG.java b/common/src/main/java/com/pg85/otg/OTG.java index 6af7c6c71..a935c151c 100644 --- a/common/src/main/java/com/pg85/otg/OTG.java +++ b/common/src/main/java/com/pg85/otg/OTG.java @@ -17,19 +17,22 @@ import com.pg85.otg.generator.resource.Resource; import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.CompressionUtils; -import java.io.ByteArrayOutputStream; +import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Random; +import org.apache.commons.lang3.StringUtils; + public class OTG { // Used to determine if a new world is being created or if a world save already exists @@ -366,49 +369,29 @@ public static boolean IsInAreaBeingPopulated(int blockX, int blockZ, ChunkCoordi public static boolean bo4DataExists(BO4Config config) { - String filePath = - config.getFile().getAbsolutePath().endsWith(".BO4") ? config.getFile().getAbsolutePath().replace(".BO4", ".BO4Data") : - config.getFile().getAbsolutePath().endsWith(".bo4") ? config.getFile().getAbsolutePath().replace(".bo4", ".BO4Data") : - config.getFile().getAbsolutePath().endsWith(".BO3") ? config.getFile().getAbsolutePath().replace(".BO3", ".BO4Data") : - config.getFile().getAbsolutePath().endsWith(".bo3") ? config.getFile().getAbsolutePath().replace(".bo3", ".BO4Data") : - config.getFile().getAbsolutePath(); - - File file = new File(filePath); - return file.exists(); + return Files.exists(getBO4DataFile(config)); } public static void generateBO4Data(BO4Config config) { - //write to disk - String filePath = - config.getFile().getAbsolutePath().endsWith(".BO4") ? config.getFile().getAbsolutePath().replace(".BO4", ".BO4Data") : - config.getFile().getAbsolutePath().endsWith(".bo4") ? config.getFile().getAbsolutePath().replace(".bo4", ".BO4Data") : - config.getFile().getAbsolutePath().endsWith(".BO3") ? config.getFile().getAbsolutePath().replace(".BO3", ".BO4Data") : - config.getFile().getAbsolutePath().endsWith(".bo3") ? config.getFile().getAbsolutePath().replace(".bo3", ".BO4Data") : - config.getFile().getAbsolutePath(); - - File file = new File(filePath); - if(!file.exists()) + try(DataOutputStream out = new DataOutputStream(new BufferedOutputStream(CompressionUtils.newDeflaterOutputStream(getBO4DataFile(config))))) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - config.writeToStream(dos); - byte[] compressedBytes = com.pg85.otg.util.CompressionUtils.compress(bos.toByteArray()); - dos.close(); - FileOutputStream fos = new FileOutputStream(file); - DataOutputStream dos2 = new DataOutputStream(fos); - dos2.write(compressedBytes, 0, compressedBytes.length); - dos2.close(); - } - catch (FileNotFoundException e) - { - e.printStackTrace(); - } - catch (IOException e) - { - e.printStackTrace(); - } + config.writeToStream(out); + } + catch(IOException e) + { + e.printStackTrace(); + } + } + + private static Path getBO4DataFile(BO4Config config) + { + Path file = config.getFile().toPath(); + String name = file.getFileName().toString(); + if(StringUtils.endsWithIgnoreCase(name, ".BO4") || StringUtils.endsWithIgnoreCase(name, ".BO3")) + { + return file.resolveSibling(name.substring(0, name.length() - 4) + ".BO4Data"); } + return file; } } diff --git a/common/src/main/java/com/pg85/otg/common/BlockContainer.java b/common/src/main/java/com/pg85/otg/common/BlockContainer.java new file mode 100644 index 000000000..a78b2d612 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/common/BlockContainer.java @@ -0,0 +1,156 @@ +package com.pg85.otg.common; + +import java.nio.file.Path; +import java.util.Objects; + +import com.pg85.otg.util.bo3.NamedBinaryTag; +import com.pg85.otg.util.bo3.Rotation; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; + +public abstract class BlockContainer +{ + protected final LocalMaterialData material; + + protected BlockContainer(LocalMaterialData material) + { + this.material = Objects.requireNonNull(material); + } + + public static BlockContainer of(LocalMaterialData material) + { + return WithoutTag.of(material); + } + + public static BlockContainer of(LocalMaterialData material, Path directory, String relativePath) + { + return WithTag.of(material, directory, relativePath); + } + + public LocalMaterialData material() + { + return material; + } + + public abstract boolean hasTag(); + + public abstract NamedBinaryTag tag(); + + public abstract String tagPath(); + + public BlockContainer withMaterial(BlockContainer other) + { + return withMaterial(other.material); + } + + public abstract BlockContainer withMaterial(LocalMaterialData material); + + public abstract BlockContainer rotate(Rotation rotation); + + public static class WithoutTag extends BlockContainer + { + private static final Reference2ReferenceMap INSTANCES = new Reference2ReferenceOpenHashMap<>(); + + private WithoutTag(LocalMaterialData material) + { + super(material); + } + + public static BlockContainer of(LocalMaterialData material) + { + return INSTANCES.computeIfAbsent(material.withBlockData(), WithoutTag::new); + } + + @Override + public boolean hasTag() + { + return false; + } + + @Override + public NamedBinaryTag tag() + { + return null; + } + + @Override + public String tagPath() + { + return null; + } + + @Override + public BlockContainer withMaterial(LocalMaterialData material) + { + if(material == this.material) + return this; + return of(material); + } + + @Override + public BlockContainer rotate(Rotation rotation) + { + LocalMaterialData rotatedMaterial; + if((rotatedMaterial = material.rotate(rotation)) == material) + { + return this; + } + return of(rotatedMaterial); + } + } + + public static class WithTag extends BlockContainer + { + private final TagContainer tagContainer; + + private WithTag(LocalMaterialData material, TagContainer tagContainer) + { + super(material); + this.tagContainer = Objects.requireNonNull(tagContainer); + } + + public static BlockContainer of(LocalMaterialData material, Path directory, String tagFileName) + { + // TODO implement cache? + return new WithTag(material, TagContainer.of(directory, tagFileName)); + } + + @Override + public boolean hasTag() + { + return true; + } + + @Override + public NamedBinaryTag tag() + { + return tagContainer.getTag(); + } + + @Override + public String tagPath() + { + return tagContainer.getRelativePath(); + } + + @Override + public BlockContainer withMaterial(LocalMaterialData material) + { + if(material == this.material) + return this; + return new WithTag(material, tagContainer); + } + + @Override + public BlockContainer rotate(Rotation rotation) + { + LocalMaterialData rotatedMaterial; + if((rotatedMaterial = material.rotate(rotation)) == material) + { + return this; + } + return new WithTag(rotatedMaterial, tagContainer); + } + } +} diff --git a/common/src/main/java/com/pg85/otg/common/LocalMaterialData.java b/common/src/main/java/com/pg85/otg/common/LocalMaterialData.java index 8590cb37f..6ec2c99d0 100644 --- a/common/src/main/java/com/pg85/otg/common/LocalMaterialData.java +++ b/common/src/main/java/com/pg85/otg/common/LocalMaterialData.java @@ -1,15 +1,12 @@ package com.pg85.otg.common; +import java.nio.file.Path; + import com.pg85.otg.OTGEngine; import com.pg85.otg.configuration.biome.BiomeConfig; -import com.pg85.otg.util.helpers.BlockHelper; +import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; -// TODO: Make this class unmodifiable (parseForWorld modifies atm), -// implement a world-specific materials cache and ensure only one -// instance of each unique material (id+metadata) exists in memory. -// TODO: Do creation of new material instances in one place only? - /** * Represents one of Minecraft's materials. Also includes its data value. * Immutable. @@ -19,20 +16,85 @@ */ public abstract class LocalMaterialData { - protected DefaultMaterial defaultMaterial; - protected String rawEntry; - protected boolean isBlank = false; - protected boolean checkedFallbacks = false; - protected boolean parsedDefaultMaterial = false; - + protected static final String BLANK_NAME = "BLANK"; + protected final DefaultMaterial defaultMaterial; + protected final int blockId; + private BlockContainer blockContainer; + + public LocalMaterialData(int blockId) + { + this.defaultMaterial = DefaultMaterial.getMaterial(blockId); + this.blockId = blockId; + } + + public abstract LocalMaterialData withoutBlockData(); + + public abstract LocalMaterialData withBlockData(); + + /** + * Gets an instance with the same material as this object, but the default + * block data of the material. This instance is not modified. + * + * @return An instance with the default block data. + */ + public abstract LocalMaterialData withDefaultBlockData(); + + /** + * Gets an instance with the same material as this object, but with the + * given block data. This instance is not modified. + * + * @param newData + * The new block data. + * @return An instance with the given block data. + */ + public abstract LocalMaterialData withBlockData(int newData); + + public BlockContainer blockContainer() + { + BlockContainer blockContainer; + if((blockContainer = this.blockContainer) == null) + { + this.blockContainer = blockContainer = BlockContainer.of(this); + } + return blockContainer; + } + + public BlockContainer blockContainer(Path directory, String relativePath) + { + return BlockContainer.of(this, directory, relativePath); + } + + /** + * Gets the default material belonging to this material. The block data will + * be lost. If the material is not one of the vanilla Minecraft materials, + * {@link DefaultMaterial#UNKNOWN_BLOCK} is returned. + * + * @return The default material. + */ + public DefaultMaterial toDefaultMaterial() + { + return this.defaultMaterial; + } + /** - * Gets a {@code LocalMaterialData} of the given material and data. - * @param material The material. - * @param data The block data. - * @return The {@code LocalMaterialData} instance. + * Gets whether the block is of the given material. Block data is ignored, + * as {@link DefaultMaterial} doesn't include block data. + * + * @param material + * The material to check. + * @return True if this block is of the given material, false otherwise. */ - protected abstract LocalMaterialData ofDefaultMaterialPrivate(DefaultMaterial material, int data); - + public boolean isMaterial(DefaultMaterial material) + { + return this.defaultMaterial == material; + } + + @Override + public String toString() + { + return getName(); + } + /** * Gets the name of this material. If a {@link #toDefaultMaterial() * DefaultMaterial is available,} that name is used, otherwise it's up to @@ -41,7 +103,7 @@ public abstract class LocalMaterialData * * @return The name of this material. */ - public abstract String getName(); + public abstract String getName(); /** * Gets the internal block id. At the moment, all of Minecraft's vanilla @@ -50,7 +112,10 @@ public abstract class LocalMaterialData * * @return The internal block id. */ - public abstract int getBlockId(); + public int getBlockId() + { + return this.blockId; + } /** * Gets the internal block data. Block data represents things like growth @@ -60,6 +125,13 @@ public abstract class LocalMaterialData */ public abstract byte getBlockData(); + public abstract int getBlockDataMask(); + + public boolean matches(LocalMaterialData other) + { + return this.blockId == other.getBlockId() && ((1 << other.getBlockData()) & this.getBlockDataMask()) != 0; + } + /** * Gets whether this material is a liquid, like water or lava. * @@ -77,98 +149,29 @@ public abstract class LocalMaterialData */ public abstract boolean isSolid(); + public boolean isEmpty() + { + return false; + } + /** * Gets whether this material is air. This is functionally equivalent to * {@code isMaterial(DefaultMaterial.AIR)}, but may yield better * performance. + * * @return True if this material is air, false otherwise. */ - public abstract boolean isEmptyOrAir(); - public abstract boolean isAir(); - public abstract boolean isEmpty(); - - /** - * Gets the default material belonging to this material. The block data will - * be lost. If the material is not one of the vanilla Minecraft materials, - * {@link DefaultMaterial#UNKNOWN_BLOCK} is returned. - * - * @return The default material. - */ - public abstract DefaultMaterial toDefaultMaterial(); - - /** - * Gets whether snow can fall on this block. - * - * @return True if snow can fall on this block, false otherwise. - */ - public abstract boolean canSnowFallOn(); - - /** - * Gets whether the block is of the given material. Block data is ignored, - * as {@link DefaultMaterial} doesn't include block data. - * - * @param material - * The material to check. - * @return True if this block is of the given material, false otherwise. - */ - public abstract boolean isMaterial(DefaultMaterial material); - - /** - * Gets an instance with the same material as this object, but with the - * given block data. This instance is not modified. - * - * @param newData - * The new block data. - * @return An instance with the given block data. - */ - public abstract LocalMaterialData withBlockData(int newData); - - /** - * Gets an instance with the same material as this object, but the default - * block data of the material. This instance is not modified. - * - * @return An instance with the default block data. - */ - public abstract LocalMaterialData withDefaultBlockData(); - - /** - * Gets whether this material equals another material. The block data is - * taken into account. - * - * @param other - * The other material. - * @return True if the materials are equal, false otherwise. - */ - public abstract boolean equals(Object other); - - /** - * Gets the hashCode of the material, based on the block id and block data. - * The hashCode must be unique, which is possible considering that there are - * only 4096 * 16 possible materials. - * - * @return The unique hashCode. - */ - public abstract int hashCode(); + public abstract boolean isLeaves(); /** - * Gets the hashCode of the material, based on only the block id. No - * hashCode returned by this method may be the same as any hashCode returned - * by {@link #hashCode()}. + * Gets whether this material falls down when no other block supports this + * block, like gravel and sand do. * - * @return The unique hashCode. + * @return True if this material can fall, false otherwise. */ - public int hashCodeWithoutBlockData() - { - // From 0 to 4095 when there are 4096 block ids - return getBlockId(); - } - - public String toString() - { - return getName(); - } + public abstract boolean canFall(); /** * Gets a new material that is rotated 90 degrees. North -> west -> south -> @@ -177,43 +180,14 @@ public String toString() * * @return The rotated material. */ - public LocalMaterialData rotate() - { - return rotate(1); - } - + public abstract LocalMaterialData rotate(Rotation rotation); + /** - * Gets a new material that is rotated 90 degrees. North -> west -> south -> - * east. If this material cannot be rotated, the material itself is - * returned. + * Gets whether this material can be used as an anchor point for a smooth area * - * @return The rotated material. + * @return True if this material is a solid block, false if it is a tile-entity, half-slab, stairs(?), water, wood or leaves */ - public LocalMaterialData rotate(int rotateTimes) - { - // TODO: Rotate modded blocks? - - // Try to rotate - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - // We only know how to rotate vanilla blocks - byte blockDataByte = 0; - int newData = 0; - for(int i = 0; i < rotateTimes; i++) - { - blockDataByte = getBlockData(); - newData = BlockHelper.rotateData(defaultMaterial, blockDataByte); - } - if (newData != blockDataByte) - { - return ofDefaultMaterialPrivate(defaultMaterial, newData); - } - } - - // No changes, return object itself - return this; - } + public abstract boolean isSmoothAreaAnchor(boolean allowWood, boolean ignoreWater); /** * Parses this material through the fallback system of the world if required. @@ -222,52 +196,14 @@ public LocalMaterialData rotate(int rotateTimes) * @return The parsed material */ public abstract LocalMaterialData parseForWorld(LocalWorld world); - - public LocalMaterialData parseWithBiomeAndHeight(LocalWorld world, BiomeConfig biomeConfig, int y) - { - if (!biomeConfig.worldConfig.biomeConfigsHaveReplacement) + + public LocalMaterialData parseWithBiomeAndHeight(LocalWorld world, BiomeConfig biomeConfig, int y) + { + if(!biomeConfig.worldConfig.biomeConfigsHaveReplacement) { // Don't waste time here, ReplacedBlocks is empty everywhere return this; } return biomeConfig.replacedBlocks.replaceBlock(y, this); - } - - /** - * Gets whether this material falls down when no other block supports this - * block, like gravel and sand do. - * @return True if this material can fall, false otherwise. - */ - public abstract boolean canFall(); - - /** - * Gets whether this material can be used as an anchor point for a smooth area - * - * @return True if this material is a solid block, false if it is a tile-entity, half-slab, stairs(?), water, wood or leaves - */ - public boolean isSmoothAreaAnchor(boolean allowWood, boolean ignoreWater) - { - return - ( - isSolid() || - ( - !ignoreWater && isLiquid() - ) - ) || ( - ( - isMaterial(DefaultMaterial.ICE) || - isMaterial(DefaultMaterial.PACKED_ICE) || - isMaterial(DefaultMaterial.FROSTED_ICE) - ) && ( - allowWood || - !( - isMaterial(DefaultMaterial.LOG) || - isMaterial(DefaultMaterial.LOG_2) - ) - ) && - !isMaterial(DefaultMaterial.WATER_LILY) - ); } - - public abstract boolean hasData(); } diff --git a/common/src/main/java/com/pg85/otg/common/LocalWorld.java b/common/src/main/java/com/pg85/otg/common/LocalWorld.java index 74a9d7771..ba4cd1dbc 100644 --- a/common/src/main/java/com/pg85/otg/common/LocalWorld.java +++ b/common/src/main/java/com/pg85/otg/common/LocalWorld.java @@ -221,4 +221,6 @@ public interface LocalWorld // Used when setting blocks during population that should // use the same chc settings as the base terrain. public double getBiomeBlocksNoiseValue(int xInWorld, int zInWorld); + + public boolean canPlaceSnowAt(int x, int y, int z, ChunkCoordinate chunkBeingPopulated); } diff --git a/common/src/main/java/com/pg85/otg/common/TagContainer.java b/common/src/main/java/com/pg85/otg/common/TagContainer.java new file mode 100644 index 000000000..5c867475f --- /dev/null +++ b/common/src/main/java/com/pg85/otg/common/TagContainer.java @@ -0,0 +1,38 @@ +package com.pg85.otg.common; + +import java.nio.file.Path; +import java.util.AbstractMap.SimpleEntry; +import java.util.Map; + +import com.pg85.otg.util.bo3.NamedBinaryTag; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; + +public class TagContainer +{ + private static final Object2ReferenceMap, TagContainer> INSTANCES = new Object2ReferenceOpenHashMap<>(); + private final String relativePath; + private final TagProvider tagProvider; + + private TagContainer(Map.Entry p) + { + this.relativePath = p.getValue(); + this.tagProvider = TagProvider.of(p.getKey().resolve(p.getValue())); + } + + public static TagContainer of(Path directory, String relativePath) + { + return INSTANCES.computeIfAbsent(new SimpleEntry<>(directory.toAbsolutePath(), relativePath), TagContainer::new); + } + + public NamedBinaryTag getTag() + { + return tagProvider.get(); + } + + public String getRelativePath() + { + return relativePath; + } +} diff --git a/common/src/main/java/com/pg85/otg/common/TagProvider.java b/common/src/main/java/com/pg85/otg/common/TagProvider.java new file mode 100644 index 000000000..085a175bb --- /dev/null +++ b/common/src/main/java/com/pg85/otg/common/TagProvider.java @@ -0,0 +1,73 @@ +package com.pg85.otg.common; + +import java.io.IOException; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; + +import com.pg85.otg.OTG; +import com.pg85.otg.exception.InvalidConfigException; +import com.pg85.otg.logging.LogMarker; +import com.pg85.otg.util.bo3.NamedBinaryTag; +import com.pg85.otg.util.bo3.NamedBinaryTag.Type; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; + +public class TagProvider +{ + private static final Object2ReferenceMap INSTANCES = new Object2ReferenceOpenHashMap<>(); + private final Path file; + private NamedBinaryTag tag; + + private TagProvider(Path file) + { + this.file = file; + } + + public static TagProvider of(Path file) + { + return INSTANCES.computeIfAbsent(file.toAbsolutePath(), TagProvider::new); + } + + public NamedBinaryTag get() + { + NamedBinaryTag tag; + if((tag = this.tag) == null) + { + try + { + tag = NamedBinaryTag.readFrom(file); + + if(tag.getTag("id") == null) + { + if(tag.getValue() instanceof NamedBinaryTag[] && ((NamedBinaryTag[]) tag.getValue()).length != 0) + { + tag = ((NamedBinaryTag[]) tag.getValue())[0]; + } + else + { + OTG.log(LogMarker.WARN, "Structure of NBT file is incorrect: " + file); + tag = new NamedBinaryTag(Type.TAG_Compound, null, new NamedBinaryTag[0]); + } + } + + this.tag = tag; + } + catch(NoSuchFileException e) + { + if(OTG.getPluginConfig().spawnLog) + { + OTG.log(LogMarker.WARN, "NBT file {} not found", file); + } + tag = new NamedBinaryTag(Type.TAG_Compound, null, new NamedBinaryTag[0]); + } + catch(IOException | InvalidConfigException e) + { + OTG.log(LogMarker.ERROR, "Failed to read NBT file {}", file); + OTG.printStackTrace(LogMarker.ERROR, e); + tag = new NamedBinaryTag(Type.TAG_Compound, null, new NamedBinaryTag[0]); + } + } + return tag; + } +} diff --git a/common/src/main/java/com/pg85/otg/common/WorldSession.java b/common/src/main/java/com/pg85/otg/common/WorldSession.java index fce4e557c..fd21a2ee2 100644 --- a/common/src/main/java/com/pg85/otg/common/WorldSession.java +++ b/common/src/main/java/com/pg85/otg/common/WorldSession.java @@ -68,7 +68,7 @@ public HashMap>> getModDataForChunk(ChunkCoo { for(ModDataFunction modData : worldInfoChunk.modDataManager.modData) { - if(ChunkCoordinate.fromBlockCoords(modData.x, modData.z).equals(chunkCoord)) // modData for all branches of the structure is stored, make sure the modData is in this chunk + if(ChunkCoordinate.fromBlockCoords(modData.x(), modData.z()).equals(chunkCoord)) // modData for all branches of the structure is stored, make sure the modData is in this chunk { if(!result.containsKey(modData.modId)) { @@ -90,7 +90,7 @@ public ArrayList> getSpawnersForChunk(ChunkCoordinate chunkCo { for(SpawnerFunction spawnerData : worldInfoChunk.spawnerManager.spawnerData) { - if(ChunkCoordinate.fromBlockCoords(spawnerData.x, spawnerData.z).equals(chunkCoord)) // spawnerData for all branches of the structure is stored, make sure the modData is in this chunk + if(ChunkCoordinate.fromBlockCoords(spawnerData.x(), spawnerData.z()).equals(chunkCoord)) // spawnerData for all branches of the structure is stored, make sure the modData is in this chunk { result.add(spawnerData); } @@ -108,7 +108,7 @@ public ArrayList> getParticlesForChunk(ChunkCoordinate chunk { for(ParticleFunction particleData : worldInfoChunk.particlesManager.particleData) { - if(ChunkCoordinate.fromBlockCoords(particleData.x, particleData.z).equals(chunkCoord)) // paticleData for all branches of the structure is stored, make sure the modData is in this chunk + if(ChunkCoordinate.fromBlockCoords(particleData.x(), particleData.z()).equals(chunkCoord)) // paticleData for all branches of the structure is stored, make sure the modData is in this chunk { result.add(particleData); } diff --git a/common/src/main/java/com/pg85/otg/configuration/ConfigFunction.java b/common/src/main/java/com/pg85/otg/configuration/ConfigFunction.java index c551d9d53..e52f63478 100644 --- a/common/src/main/java/com/pg85/otg/configuration/ConfigFunction.java +++ b/common/src/main/java/com/pg85/otg/configuration/ConfigFunction.java @@ -137,13 +137,7 @@ protected final LocalMaterialData readMaterial(String string) throws InvalidConf */ protected final MaterialSet readMaterials(List strings, int start) throws InvalidConfigException { - MaterialSet materials = new MaterialSet(); - for (int i = start; i < strings.size(); i++) - { - materials.parseAndAdd(strings.get(i)); - } - - return materials; + return MaterialSet.create(strings.subList(start, strings.size())); } /** diff --git a/common/src/main/java/com/pg85/otg/configuration/biome/BiomeConfig.java b/common/src/main/java/com/pg85/otg/configuration/biome/BiomeConfig.java index d821698a4..55fde598c 100644 --- a/common/src/main/java/com/pg85/otg/configuration/biome/BiomeConfig.java +++ b/common/src/main/java/com/pg85/otg/configuration/biome/BiomeConfig.java @@ -31,6 +31,7 @@ import java.io.DataOutput; import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; public class BiomeConfig extends ConfigFile { @@ -274,27 +275,24 @@ public List getCustomStructures() */ public int getSnowHeight(float temp) { - // OTG biome temperature is between 0.0 and 2.0. - // Judging by WorldStandardValues.SNOW_AND_ICE_MAX_TEMP, snow should appear below 0.15. - // According to the configs, snow and ice should appear between 0.2 (at y > 90) and 0.1 (entire biome covered in ice). - // Let's make sure that at 0.2, snow layers start with thickness 0 at y 90 and thickness 7 around y 255. - // In a 0.2 temp biome, y90 temp is 0.156, y255 temp is -0.12 - - float snowTemp = WorldStandardValues.SNOW_AND_ICE_TEMP; - if(temp <= snowTemp) - { - float maxColdTemp = WorldStandardValues.SNOW_AND_ICE_MAX_TEMP; - float maxThickness = 7.0f; - if(temp < maxColdTemp) - { - return (int)maxThickness; - } - float range = Math.abs(maxColdTemp - snowTemp); - float fraction = Math.abs(maxColdTemp - temp); - return (int)Math.floor((1.0f - (fraction / range)) * maxThickness); - } - - return 0; + // OTG biome temperature is between 0.0 and 2.0. + // Judging by WorldStandardValues.SNOW_AND_ICE_MAX_TEMP, snow should appear below 0.15. + // According to the configs, snow and ice should appear between 0.2 (at y > 90) and 0.1 (entire biome covered in ice). + // Let's make sure that at 0.2, snow layers start with thickness 0 at y 90 and thickness 7 around y 255. + // In a 0.2 temp biome, y90 temp is 0.156, y255 temp is -0.12 + + float startTemp = WorldStandardValues.SNOW_AND_ICE_TEMP; + float maxTemp = WorldStandardValues.SNOW_AND_ICE_MAX_TEMP; + int maxThickness = 7; + if(temp > startTemp) + { + return 0; + } + if(temp < maxTemp) + { + return maxThickness; + } + return (int) Math.floor((temp - startTemp) / (maxTemp - startTemp) * maxThickness); } public SaplingGen getSaplingGen(SaplingType type) @@ -837,7 +835,7 @@ protected void writeConfigSettings(SettingsMap writer) "TreeTypeChance: similar to Rarity. Example:", " Tree(10,Taiga1,35,Taiga2,100) - plugin tries 10 times, for each attempt it tries to place Taiga1 (35% chance),", " if that fails, it attempts to place Taiga2 (100% chance).", - "PlantType: one of the plant types: " + StringHelper.join(PlantType.values(), ", "), + "PlantType: one of the plant types: " + StringHelper.join(PlantType.values().stream().sorted(Comparator.comparing(PlantType::getName, String.CASE_INSENSITIVE_ORDER)).collect(Collectors.toList()), ", "), " or simply a BlockName", "IceSpikeType: one of the ice spike types: " + StringHelper.join(IceSpikeGen.SpikeType.values(), ","), "Object: can be a any kind of custom object (bo2 or bo3) but without the file extension. You can", diff --git a/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroup.java b/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroup.java index 7a1542e56..a500016f2 100644 --- a/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroup.java +++ b/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroup.java @@ -8,6 +8,7 @@ import com.pg85.otg.configuration.world.WorldConfig; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.logging.LogMarker; +import com.pg85.otg.util.WeightedList; import com.pg85.otg.util.helpers.StringHelper; import com.pg85.otg.util.minecraft.defaults.DefaultBiome; @@ -29,9 +30,8 @@ public final class BiomeGroup extends ConfigFunction private final int generationDepth; private float avgTemp = 0; private final Map biomes = new LinkedHashMap(32); - private final List> cachedDepthMapOrHigher = new ArrayList<>(); - private final List> cachedDepthMaps = new ArrayList<>(); - private TreeMap defaultDepthMap = null; + private final WeightedList[] cachedDepthMapOrHigher; + private WeightedList defaultDepthMap; /** * Variable used by the the ungrouped biome generator. This generator @@ -49,6 +49,7 @@ public final class BiomeGroup extends ConfigFunction * @see #BiomeGroup(WorldConfig, String, int, int, List) Constructor to * properly initialize this biome group manually. */ + @SuppressWarnings("unchecked") public BiomeGroup(WorldConfig config, List args) throws InvalidConfigException { super(config); @@ -57,15 +58,11 @@ public BiomeGroup(WorldConfig config, List args) throws InvalidConfigExc this.name = args.get(0); this.generationDepth = readInt(args.get(1), 0, config.generationDepth); this.groupRarity = readInt(args.get(2), 1, Integer.MAX_VALUE); - for (String biome : readBiomes(args, 3)) + for(int i = 3; i < args.size(); i++) { - this.biomes.put(biome, null); - } - for (int i = 0; i <= config.generationDepth; i++) - { - this.cachedDepthMapOrHigher.add(null); - this.cachedDepthMaps.add(null); + this.biomes.put(args.get(i), null); } + this.cachedDepthMapOrHigher = new WeightedList[config.generationDepth]; } /** @@ -76,21 +73,18 @@ public BiomeGroup(WorldConfig config, List args) throws InvalidConfigExc * @param rarity Rarity value of this biome group. * @param biomes List of names of the biomes that spawn in this group. */ + @SuppressWarnings("unchecked") public BiomeGroup(WorldConfig config, String groupName, int size, int rarity, List biomes) { super(config); this.name = groupName; this.generationDepth = size; this.groupRarity = rarity; - for (String biome : biomes) + for(String biome : biomes) { this.biomes.put(biome, null); } - for (int i = 0; i <= config.generationDepth; i++) - { - this.cachedDepthMapOrHigher.add(null); - this.cachedDepthMaps.add(null); - } + this.cachedDepthMapOrHigher = new WeightedList[config.generationDepth]; } /** @@ -103,7 +97,7 @@ public void processBiomeData(LocalWorld world) { float totalTemp = 0; this.totalGroupRarity = 0; - for (Iterator> it = this.biomes.entrySet().iterator(); it.hasNext();) + for(Iterator> it = this.biomes.entrySet().iterator(); it.hasNext();) { Entry entry = it.next(); String biomeName = entry.getKey(); @@ -113,8 +107,8 @@ public void processBiomeData(LocalWorld world) if(localBiome == null) { - OTG.log(LogMarker.FATAL, "Could not find biome with name '"+ biomeName +"' from biome group "+ this.name); - throw new RuntimeException("Could not find biome with name '"+ biomeName +"' from biome group "+ this.name); + OTG.log(LogMarker.FATAL, "Could not find biome with name '" + biomeName + "' from biome group " + this.name); + throw new RuntimeException("Could not find biome with name '" + biomeName + "' from biome group " + this.name); } BiomeConfig biomeConfig = localBiome.getBiomeConfig(); @@ -130,21 +124,6 @@ public String toString() return "BiomeGroup(" + name + ", " + generationDepth + ", " + groupRarity + ", " + StringHelper.join(biomes.keySet(), ", ") + ")"; } - /** - * Reads all biomes from the start position until the end of the - * list. - * @param strings The input strings. - * @param start The position to start. The first element in the list - * has index 0, the last one size() - 1. - * @return All biome names. - * @throws InvalidConfigException If one of the elements in the list is - * not a valid block id. - */ - private List readBiomes(List strings, int start) throws InvalidConfigException - { - return new ArrayList(strings.subList(start, strings.size())); - } - /** * Gets the name of this biome group. * @return The name. @@ -161,20 +140,20 @@ public String getName() */ void filterBiomes(ArrayList customBiomeNames, boolean logWarnings) { - for (Iterator it = this.biomes.keySet().iterator(); it.hasNext();) + for(Iterator it = this.biomes.keySet().iterator(); it.hasNext();) { String biomeName = it.next(); if(biomeName != null && biomeName.trim().length() > 0) { - if (DefaultBiome.Contain(biomeName) || customBiomeNames.contains(biomeName)) - { - continue; - } - // Invalid biome name, remove - if(logWarnings) - { - OTG.log(LogMarker.WARN, "Invalid biome name {} in biome group {}", biomeName, this.name); - } + if(DefaultBiome.Contain(biomeName) || customBiomeNames.contains(biomeName)) + { + continue; + } + // Invalid biome name, remove + if(logWarnings) + { + OTG.log(LogMarker.WARN, "Invalid biome name {} in biome group {}", biomeName, this.name); + } } it.remove(); } @@ -200,10 +179,9 @@ public boolean containsBiome(String name) */ void setGroupId(int groupId) { - if (groupId > BiomeGroupManager.MAX_BIOME_GROUP_COUNT) + if(groupId > BiomeGroupManager.MAX_BIOME_GROUP_COUNT) { - throw new IllegalArgumentException("Tried to set group id to " + groupId - + ", max allowed is " + BiomeGroupManager.MAX_BIOME_GROUP_COUNT); + throw new IllegalArgumentException("Tried to set group id to " + groupId + ", max allowed is " + BiomeGroupManager.MAX_BIOME_GROUP_COUNT); } this.groupId = groupId; @@ -232,7 +210,7 @@ public boolean isColdGroup() @Override public boolean isAnalogousTo(ConfigFunction other) { - if (other instanceof BiomeGroup) + if(other instanceof BiomeGroup) { BiomeGroup group = (BiomeGroup) other; return group.name.equalsIgnoreCase(this.name); @@ -240,81 +218,42 @@ public boolean isAnalogousTo(ConfigFunction other) return false; } - public SortedMap getDepthMapOrHigher(int depth) + public WeightedList getDepthMapOrHigher(int depth) { - if (depth < 0) { - return getDefaultDepthMap(); - } - - TreeMap map = cachedDepthMapOrHigher.get(depth); - if (map != null) + if(depth < 0) { - return map; + return this.getDefaultDepthMap(); } - - int cumulativeBiomeRarity = 0; - map = new TreeMap<>(); - for (Entry biome : this.biomes.entrySet()) + + WeightedList map = this.cachedDepthMapOrHigher[depth]; + if(map == null) { - if (biome.getValue().getBiomeConfig().biomeSize >= depth) + map = new WeightedList<>(); + for(LocalBiome biome : this.biomes.values()) { - cumulativeBiomeRarity += biome.getValue().getBiomeConfig().biomeRarity; - map.put(cumulativeBiomeRarity, biome.getValue()); + if(biome.getBiomeConfig().biomeSize >= depth) + { + map.add(biome, biome.getBiomeConfig().biomeRarity); + } } + this.cachedDepthMapOrHigher[depth] = map; } - cachedDepthMapOrHigher.set(depth, map); - return map; } - SortedMap getDepthMap(int depth) + private WeightedList getDefaultDepthMap() { - if (depth < 0) - { - return getDefaultDepthMap(); - } - - TreeMap map = cachedDepthMaps.get(depth); - if (map != null) + if(this.defaultDepthMap == null) { - return map; - } - - int cumulativeBiomeRarity = 0; - map = new TreeMap<>(); - for (Entry biome : this.biomes.entrySet()) - { - if (biome.getValue().getBiomeConfig().biomeSize == depth) + this.defaultDepthMap = new WeightedList<>(); + for(LocalBiome biome : this.biomes.values()) { - cumulativeBiomeRarity += biome.getValue().getBiomeConfig().biomeRarity; - map.put(cumulativeBiomeRarity, biome.getValue()); + this.defaultDepthMap.add(biome, biome.getBiomeConfig().biomeRarity); } } - cachedDepthMaps.set(depth, map); - - return map; - } - - SortedMap getDefaultDepthMap() - { - if (defaultDepthMap != null) - { - return defaultDepthMap; - } - - int cumulativeBiomeRarity = 0; - TreeMap map = new TreeMap<>(); - for (Entry biome : this.biomes.entrySet()) - { - cumulativeBiomeRarity += biome.getValue().getBiomeConfig().biomeRarity; - map.put(cumulativeBiomeRarity, biome.getValue()); - } - - defaultDepthMap = map; - - return map; + return this.defaultDepthMap; } public int getGroupRarity() @@ -336,4 +275,16 @@ boolean hasNoBiomes() { return biomes.isEmpty(); } + + boolean isBiomeDepthMapEmpty(int depth) + { + for(LocalBiome biome : this.biomes.values()) + { + if(biome.getBiomeConfig().biomeSize == depth) + { + return false; + } + } + return true; + } } diff --git a/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroupManager.java b/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroupManager.java index 96916b6cb..5f6a0411d 100644 --- a/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroupManager.java +++ b/common/src/main/java/com/pg85/otg/configuration/biome/BiomeGroupManager.java @@ -3,6 +3,10 @@ import com.pg85.otg.OTG; import com.pg85.otg.common.LocalWorld; import com.pg85.otg.logging.LogMarker; +import com.pg85.otg.util.WeightedList; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.util.*; @@ -109,33 +113,29 @@ public int getGroupCount() return nameToGroup.size(); } - // TODO: Turn into array? - HashMap> cachedGroupDepthMaps = new HashMap<>(); - public SortedMap getGroupDepthMap(int depth) + private final Int2ObjectMap> cachedGroupDepthMaps = new Int2ObjectOpenHashMap<>(); + + public WeightedList getGroupDepthMap(int depth) { - TreeMap map = cachedGroupDepthMaps.get(depth); - if(map != null) - { - return map; - } - - map = new TreeMap<>(); - int cumulativeGroupRarity = 0; - for (BiomeGroup group : getGroups()) + WeightedList map = this.cachedGroupDepthMaps.get(depth); + + if(map == null) { - if (group.getGenerationDepth() == depth) + map = new WeightedList<>(); + for(BiomeGroup biomeGroup : this.getGroups()) { - cumulativeGroupRarity += group.getGroupRarity(); - map.put(cumulativeGroupRarity, group); + if(biomeGroup.getGenerationDepth() == depth) + { + map.add(biomeGroup, biomeGroup.getGroupRarity()); + } } + if(map.totalWeight() < map.size() * 100) + { + map.add(null, map.size() * 100); + } + this.cachedGroupDepthMaps.put(depth, map); } - if (cumulativeGroupRarity < map.size() * 100) - { - map.put(map.size() * 100, null); - } - - cachedGroupDepthMaps.put(depth, map); - + return map; } @@ -155,17 +155,12 @@ public boolean isBiomeDepthMapEmpty(int depth) { for (BiomeGroup group : getGroups()) { - if (!group.getDepthMap(depth).isEmpty()) + if (!group.isBiomeDepthMapEmpty(depth)) return false; } return true; } - public static int getMaxRarityFromPossibles(SortedMap sortedMap) - { - return sortedMap.lastKey(); - } - public void processBiomeData(LocalWorld world) { for (BiomeGroup entry : nameToGroup.values()) diff --git a/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplaceBlocks.java b/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplaceBlocks.java index bfca44058..7e6bc1d28 100644 --- a/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplaceBlocks.java +++ b/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplaceBlocks.java @@ -53,7 +53,7 @@ public static List fromJson(String originalJson) throws InvalidCo json = json.replace('{', '('); json = json.replace('}', ')'); - String[] groups = StringHelper.readCommaSeperatedString(json); + List groups = StringHelper.readCommaSeperatedString(json); for (String group : groups) { @@ -66,7 +66,7 @@ public static List fromJson(String originalJson) throws InvalidCo private static ReplaceBlocks readSingleGroup(String json) throws InvalidConfigException { String group = removeFirstAndLastChar(json.trim()); - String[] groupParts = StringHelper.readCommaSeperatedString(group); + List groupParts = StringHelper.readCommaSeperatedString(group); String sourceBlock = null; String targetBlock = null; diff --git a/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplacedBlocksMatrix.java b/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplacedBlocksMatrix.java index 820142a88..cd857aa31 100644 --- a/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplacedBlocksMatrix.java +++ b/common/src/main/java/com/pg85/otg/configuration/biome/settings/ReplacedBlocksMatrix.java @@ -1,40 +1,41 @@ package com.pg85.otg.configuration.biome.settings; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.configuration.standard.PluginStandardValues; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.helpers.StringHelper; import com.pg85.otg.util.materials.MaterialHelper; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map.Entry; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; public class ReplacedBlocksMatrix { - // Redesigned this to use ReplaceBlockEntry instead, since we're now replacing - // blocks when they're placed, not 4x per chunk at the end of population. - // TODO: After removing replaceblocks from localworld, clean this up. - + // Redesigned this to use ReplaceBlockEntry instead, since we're now replacing + // blocks when they're placed, not 4x per chunk at the end of population. + // TODO: After removing replaceblocks from localworld, clean this up. private static final String NO_REPLACE = "None"; - + private class ReplaceBlockEntry { - public final HashMap targetsWithoutBlockData = new HashMap(); - public final HashMap targetsWithBlockData = new HashMap(); + public final Reference2ReferenceMap targetsWithoutBlockData = new Reference2ReferenceOpenHashMap<>(); + public final Reference2ReferenceMap targetsWithBlockData = new Reference2ReferenceOpenHashMap<>(); } - + public static class ReplacedBlocksInstruction { - private final LocalMaterialData from; - private final LocalMaterialData to; + private LocalMaterialData from; + private LocalMaterialData to; private final int minHeight; private final int maxHeight; - + /** * Parses the given instruction string. * @@ -47,25 +48,28 @@ public static class ReplacedBlocksInstruction private ReplacedBlocksInstruction(String instruction, int maxAllowedY) throws InvalidConfigException { String[] values = instruction.split(","); - if (values.length == 5) + if(values.length == 5) { // Replace in TC 2.3 style found - values = new String[] {values[0], values[1] + ":" + values[2], values[3], "" + (Integer.parseInt(values[4]) - 1)}; + values = new String[] + { values[0], values[1] + ":" + values[2], values[3], "" + (Integer.parseInt(values[4]) - 1) }; } - if (values.length != 2 && values.length != 4) + if(values.length != 2 && values.length != 4) { throw new InvalidConfigException("Replace parts must be in the format (from,to) or (from,to,minHeight,maxHeight)"); } - from = MaterialHelper.readMaterial(values[0]); + from = MaterialHelper.readMaterial(values[0]); to = MaterialHelper.readMaterial(values[1]); - if (values.length == 4) + if(values.length == 4) { minHeight = StringHelper.readInt(values[2], 0, maxAllowedY); maxHeight = StringHelper.readInt(values[3], minHeight, maxAllowedY); - } else { + } + else + { minHeight = 0; maxHeight = maxAllowedY; } @@ -120,41 +124,42 @@ public int getMaxHeight() private final int maxHeight; private List instructions; private final ReplaceBlockEntry[] targetsAtHeights; - + public boolean replacesCooledLava = false; - public boolean replacesIce = false; - public boolean replacesWater = false; - public boolean replacesStone = false; - public boolean replacesGround = false; - public boolean replacesSurface = false; - public boolean replacesBedrock = false; - public boolean replacesSandStone = false; - public boolean replacesRedSandStone = false; + public boolean replacesIce = false; + public boolean replacesWater = false; + public boolean replacesStone = false; + public boolean replacesGround = false; + public boolean replacesSurface = false; + public boolean replacesBedrock = false; + public boolean replacesSandStone = false; + public boolean replacesRedSandStone = false; public ReplacedBlocksMatrix(String setting, int maxHeight) throws InvalidConfigException { this.maxHeight = maxHeight; - this.targetsAtHeights = (ReplaceBlockEntry[])new ReplaceBlockEntry[256]; - + this.targetsAtHeights = (ReplaceBlockEntry[]) new ReplaceBlockEntry[256]; + // Parse - if (setting.isEmpty() || setting.equalsIgnoreCase(NO_REPLACE)) + if(setting.isEmpty() || setting.equalsIgnoreCase(NO_REPLACE)) { - setInstructions(Collections. emptyList()); + setInstructions(Collections.emptyList()); return; } List instructions = new ArrayList(); - String[] keys = StringHelper.readCommaSeperatedString(setting); + List keys = StringHelper.readCommaSeperatedString(setting); - for (String key : keys) + for(String key : keys) { int start = key.indexOf('('); int end = key.lastIndexOf(')'); - if (start != -1 && end != -1) + if(start != -1 && end != -1) { String keyWithoutBraces = key.substring(start + 1, end); instructions.add(new ReplacedBlocksInstruction(keyWithoutBraces, maxHeight)); - } else + } + else { throw new InvalidConfigException("One of the parts is missing braces around it."); } @@ -162,174 +167,150 @@ public ReplacedBlocksMatrix(String setting, int maxHeight) throws InvalidConfigE // Set setInstructions(instructions); - + // Fill maps for faster access + this.computeMaps(); + } + + private void computeMaps() + { + Arrays.fill(this.targetsAtHeights, null); + for(ReplacedBlocksInstruction instruction : this.instructions) { - for(int y = instruction.minHeight; y <= instruction.maxHeight; y++) - { - if(y > PluginStandardValues.WORLD_HEIGHT - 1) - { - break; - } - if(y < PluginStandardValues.WORLD_DEPTH) - { - continue; - } - - ReplaceBlockEntry targetsAtHeight = this.targetsAtHeights[y]; - if(targetsAtHeight == null) - { - targetsAtHeight = new ReplaceBlockEntry(); - this.targetsAtHeights[y] = targetsAtHeight; - } - - // Users can chain replacedblocks to replace replacedblocks, instead of actually - // replacing the same block to different materials multiple times, we'll calculate - // the end result in advance. - for(Entry entry : targetsAtHeight.targetsWithoutBlockData.entrySet()) - { - if( - // BLOCK:X replaces BLOCK:X - (instruction.from.hasData() && instruction.from.hashCode() == entry.getValue().hashCode()) || - // BLOCK replaces all BLOCK:X - (!instruction.from.hasData() && instruction.from.hashCodeWithoutBlockData() == entry.getValue().hashCodeWithoutBlockData()) - ) - { - entry.setValue(instruction.to); - } - } - for(Entry entry : targetsAtHeight.targetsWithBlockData.entrySet()) - { - if( - // BLOCK:X replaces BLOCK:X - (instruction.from.hasData() && instruction.from.hashCode() == entry.getValue().hashCode()) || - // BLOCK replaces all BLOCK:X - (!instruction.from.hasData() && instruction.from.hashCodeWithoutBlockData() == entry.getValue().hashCodeWithoutBlockData()) - ) - { - entry.setValue(instruction.to); - } - } - if(instruction.from.hasData()) - { - targetsAtHeight.targetsWithBlockData.put(instruction.from.hashCode(), instruction.to); - } else { - targetsAtHeight.targetsWithoutBlockData.put(instruction.from.hashCodeWithoutBlockData(), instruction.to); - } - } + for(int y = Math.max(instruction.minHeight, 0); y <= Math.min(instruction.maxHeight, this.targetsAtHeights.length - 1); y++) + { + ReplaceBlockEntry targetsAtHeight = this.targetsAtHeights[y]; + if(targetsAtHeight == null) + { + targetsAtHeight = new ReplaceBlockEntry(); + this.targetsAtHeights[y] = targetsAtHeight; + } + + // Users can chain replacedblocks to replace replacedblocks, instead of actually + // replacing the same block to different materials multiple times, we'll calculate + // the end result in advance. + + for(Map.Entry entry : targetsAtHeight.targetsWithoutBlockData.entrySet()) + { + if(instruction.from.matches(entry.getValue())) + { + entry.setValue(instruction.to); + } + } + for(Map.Entry entry : targetsAtHeight.targetsWithBlockData.entrySet()) + { + if(instruction.from.matches(entry.getValue())) + { + entry.setValue(instruction.to); + } + } + if(instruction.from.getBlockDataMask() != -1) + { + targetsAtHeight.targetsWithBlockData.put(instruction.from, instruction.to); + } + else + { + targetsAtHeight.targetsWithoutBlockData.put(instruction.from, instruction.to); + } + } } } - - public void init(LocalMaterialData biomeCooledLavaBlock, LocalMaterialData biomeIceBlock, LocalMaterialData biomeWaterBlock, LocalMaterialData biomeStoneBlock, LocalMaterialData biomeGroundBlock, LocalMaterialData biomeSurfaceBlock, LocalMaterialData biomeBedrockBlock, LocalMaterialData biomeSandStoneBlock, LocalMaterialData biomeRedSandStoneBlock) - { + + public void init(LocalMaterialData biomeCooledLavaBlock, LocalMaterialData biomeIceBlock, LocalMaterialData biomeWaterBlock, LocalMaterialData biomeStoneBlock, LocalMaterialData biomeGroundBlock, LocalMaterialData biomeSurfaceBlock, LocalMaterialData biomeBedrockBlock, LocalMaterialData biomeSandStoneBlock, LocalMaterialData biomeRedSandStoneBlock) + { // Fill maps for faster access for(ReplacedBlocksInstruction instruction : this.instructions) - { - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeCooledLavaBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeCooledLavaBlock.hashCode() - ) - { - this.replacesCooledLava = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeIceBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeIceBlock.hashCode() - ) - { - this.replacesIce = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeWaterBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeWaterBlock.hashCode() - ) - { - this.replacesWater = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeStoneBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeStoneBlock.hashCode() - ) - { - this.replacesStone = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeGroundBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeGroundBlock.hashCode() - ) - { - this.replacesGround = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeSurfaceBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeSurfaceBlock.hashCode() - ) - { - this.replacesSurface = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeBedrockBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeBedrockBlock.hashCode() - ) - { - this.replacesBedrock = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeSandStoneBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeSandStoneBlock.hashCode() - ) - { - this.replacesSandStone = true; - } - if( - !instruction.from.hasData() ? instruction.from.hashCodeWithoutBlockData() == biomeRedSandStoneBlock.hashCodeWithoutBlockData() : - instruction.from.hashCode() == biomeRedSandStoneBlock.hashCode() - ) - { - this.replacesRedSandStone = true; - } + { + if(instruction.from.matches(biomeCooledLavaBlock)) + { + this.replacesCooledLava = true; + } + if(instruction.from.matches(biomeIceBlock)) + { + this.replacesIce = true; + } + if(instruction.from.matches(biomeWaterBlock)) + { + this.replacesWater = true; + } + if(instruction.from.matches(biomeStoneBlock)) + { + this.replacesStone = true; + } + if(instruction.from.matches(biomeGroundBlock)) + { + this.replacesGround = true; + } + if(instruction.from.matches(biomeSurfaceBlock)) + { + this.replacesSurface = true; + } + if(instruction.from.matches(biomeBedrockBlock)) + { + this.replacesBedrock = true; + } + if(instruction.from.matches(biomeSandStoneBlock)) + { + this.replacesSandStone = true; + } + if(instruction.from.matches(biomeRedSandStoneBlock)) + { + this.replacesRedSandStone = true; + } + } + } + + private boolean parsedFallBacks = false; + + public void parseForWorld(LocalWorld world) + { + if(!parsedFallBacks) + { + parsedFallBacks = true; + boolean instructionsChanged = false; + for(ReplacedBlocksInstruction instruction : this.instructions) + { + if(instruction.from != null) + { + instruction.from = instruction.from.parseForWorld(world); + instructionsChanged = true; + } + if(instruction.to != null) + { + instruction.to = instruction.to.parseForWorld(world); + instructionsChanged = true; + } + } + if(instructionsChanged) + { + this.computeMaps(); + } } - } - - private boolean parsedFallBacks = false; - public void parseForWorld(LocalWorld world) - { - if(!parsedFallBacks) - { - parsedFallBacks = true; - for(ReplacedBlocksInstruction instruction : this.instructions) - { - if(instruction.from != null) - { - instruction.from.parseForWorld(world); - } - if(instruction.to != null) - { - instruction.to.parseForWorld(world); - } - } - } - } - + } + public LocalMaterialData replaceBlock(int y, LocalMaterialData material) { - ReplaceBlockEntry targetsAtHeight = targetsAtHeights[y]; - if(targetsAtHeight == null) - { - return material; - } - LocalMaterialData replaceToMaterial = targetsAtHeight.targetsWithoutBlockData.get(material.hashCodeWithoutBlockData()); - if(replaceToMaterial != null) - { - return replaceToMaterial; - } - replaceToMaterial = targetsAtHeight.targetsWithBlockData.get(material.hashCode()); - if(replaceToMaterial != null) - { - return replaceToMaterial; - } - return material; + if(y < 0 || y >= targetsAtHeights.length) + { + return material; + } + ReplaceBlockEntry targetsAtHeight = targetsAtHeights[y]; + if(targetsAtHeight == null) + { + return material; + } + LocalMaterialData replaceToMaterial = targetsAtHeight.targetsWithoutBlockData.get(material.withoutBlockData()); + if(replaceToMaterial != null) + { + return replaceToMaterial; + } + replaceToMaterial = targetsAtHeight.targetsWithBlockData.get(material.withBlockData()); + if(replaceToMaterial != null) + { + return replaceToMaterial; + } + return material; } /** @@ -364,7 +345,7 @@ public void setInstructions(Collection instructions) { this.instructions = Collections.unmodifiableList(new ArrayList(instructions)); - if (this.instructions.size() == 0) + if(this.instructions.size() == 0) { return; } @@ -372,19 +353,19 @@ public void setInstructions(Collection instructions) public String toString() { - if (!this.hasReplaceSettings()) + if(!this.hasReplaceSettings()) { // No replace setting return NO_REPLACE; } StringBuilder builder = new StringBuilder(); - for (ReplacedBlocksInstruction instruction : getInstructions()) + for(ReplacedBlocksInstruction instruction : getInstructions()) { builder.append('('); builder.append(instruction.from); builder.append(',').append(instruction.to); - if (instruction.getMinHeight() != 0 || instruction.getMaxHeight() != this.maxHeight) + if(instruction.getMinHeight() != 0 || instruction.getMaxHeight() != this.maxHeight) { // Add custom height setting builder.append(',').append(instruction.getMinHeight()); @@ -408,26 +389,23 @@ public static ReplacedBlocksMatrix createEmptyMatrix(int maxHeight) try { return new ReplacedBlocksMatrix(NO_REPLACE, maxHeight); - } catch (InvalidConfigException e) + } + catch(InvalidConfigException e) { // Should never happen throw new AssertionError(e); } } - public boolean replacesBlock(LocalMaterialData surfaceBlock) - { + public boolean replacesBlock(LocalMaterialData surfaceBlock) + { for(ReplacedBlocksInstruction instruction : this.instructions) { - if( - instruction.getFrom().hasData() ? - surfaceBlock.hashCode() == instruction.from.hashCode() : - surfaceBlock.hashCodeWithoutBlockData() == instruction.from.hashCodeWithoutBlockData() - ) - { - return true; - } + if(instruction.from.matches(surfaceBlock)) + { + return true; + } } - return false; - } + return false; + } } diff --git a/common/src/main/java/com/pg85/otg/configuration/biome/settings/WeightedMobSpawnGroup.java b/common/src/main/java/com/pg85/otg/configuration/biome/settings/WeightedMobSpawnGroup.java index 2c60efba1..2b0c761aa 100644 --- a/common/src/main/java/com/pg85/otg/configuration/biome/settings/WeightedMobSpawnGroup.java +++ b/common/src/main/java/com/pg85/otg/configuration/biome/settings/WeightedMobSpawnGroup.java @@ -72,7 +72,7 @@ public static List fromJson(String originalJson) throws I json = json.replace('{', '('); json = json.replace('}', ')'); - String[] groups = StringHelper.readCommaSeperatedString(json); + List groups = StringHelper.readCommaSeperatedString(json); for (String group : groups) { @@ -85,7 +85,7 @@ public static List fromJson(String originalJson) throws I private static WeightedMobSpawnGroup readSingleGroup(String json) throws InvalidConfigException { String group = removeFirstAndLastChar(json.trim()); - String[] groupParts = StringHelper.readCommaSeperatedString(group); + List groupParts = StringHelper.readCommaSeperatedString(group); String mobName = null; int weight = -1; int min = -1; diff --git a/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectConfigFunction.java b/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectConfigFunction.java index a0d859af1..f3e48c9df 100644 --- a/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectConfigFunction.java +++ b/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectConfigFunction.java @@ -13,27 +13,25 @@ public abstract class CustomObjectConfigFunction { - public int x; - public int z; - - protected T holder; - - /** - * Has a value when valid == false, otherwise null. - */ - protected String error; - - /** - * Only has a value when {@link #invalidate(String, List, String)} is - * called. - */ - protected List inputArgs; + protected static final int X_BITS = 11; + protected static final int Y_BITS = 10; + protected static final int Z_BITS = 11; + protected static final int X_SHIFT = 0; + protected static final int Y_SHIFT = X_SHIFT + X_BITS; + protected static final int Z_SHIFT = Y_SHIFT + Y_BITS; + protected static final int X_MASK = ((1 << X_BITS) - 1) << X_SHIFT; + protected static final int Y_MASK = ((1 << Y_BITS) - 1) << Y_SHIFT; + protected static final int Z_MASK = ((1 << Z_BITS) - 1) << Z_SHIFT; + protected static final int X_MIN = -1 << X_BITS - 1; + protected static final int X_MAX = (1 << X_BITS - 1) - 1; + protected static final int Y_MIN = -1 << Y_BITS - 1; + protected static final int Y_MAX = (1 << Y_BITS - 1) - 1; + protected static final int Z_MIN = -1 << Z_BITS - 1; + protected static final int Z_MAX = (1 << Z_BITS - 1) - 1; /** - * Only has a value when {@link #invalidate(String, List, String)} is - * called. + * layout: z = {@value #X_BITS} bits [{@value #X_MIN}, {@value #X_MAX}], y = {@value #Y_BITS} bits [{@value #Y_MIN}, {@value #Y_MAX}], x = {@value #Z_BITS} bits [{@value #Z_MIN}, {@value #Z_MAX}] */ - protected String inputName; - protected boolean valid = true; + protected int coords; /** * Convenience method for creating a config function. Used to create @@ -63,10 +61,9 @@ public static final CustomObjectConfigFunction create(T holder, Class args) throws InvalidConfi * @throws IllegalStateException If the object {@link #isValid() is * valid}, so no error occurred. */ - public final String getError() throws IllegalStateException - { - if (isValid()) - { - throw new IllegalStateException("Function is valid, so no error"); - } - return error; - } - - /** - * Gets the holder of this config function. - * @return The holder. - */ - public final T getHolder() + public String getError() throws IllegalStateException { - return holder; + throw new IllegalStateException("Function is valid, so no error"); } /** @@ -136,22 +120,7 @@ public final T getHolder() */ final void init(T holder, List args) throws InvalidConfigException { - this.holder = holder; - load(args); - } - - /** - * Invalidates this resource. - * @param name Name of this resource, for output. - * @param args Arguments used in this resource, for output. - * @param error Error message detailing what went wrong. - */ - final void invalidate(String name, List args, String error) - { - valid = false; - this.inputName = name; - this.inputArgs = args; - this.error = error; + load(holder, args); } /** @@ -170,9 +139,9 @@ final void invalidate(String name, List args, String error) *

* @return Whether this ConfigFunction has a correct syntax. */ - public final boolean isValid() + public boolean isValid() { - return valid; + return true; } /** @@ -182,7 +151,7 @@ public final boolean isValid() * @param args The arguments to parse. * @throws InvalidConfigException If the syntax is invalid. */ - protected abstract void load(List args) throws InvalidConfigException; + protected abstract void load(T holder, List args) throws InvalidConfigException; /** * Formats the material list as a string list. @@ -200,6 +169,15 @@ protected final String makeMaterials(MaterialSet materials) */ public abstract String makeString(); + public void readXYZ(List args, int index) throws InvalidConfigException + { + this.assureSize(index + 3, args); + int x = this.readInt(args.get(index + 0), X_MIN, X_MAX); + int y = this.readInt(args.get(index + 1), Y_MIN, Y_MAX); + int z = this.readInt(args.get(index + 2), Z_MIN, Z_MAX); + coords = ((x << X_SHIFT) & X_MASK) | ((y << Y_SHIFT) & Y_MASK) | ((z << Z_SHIFT) & Z_MASK); + } + /** * Parses the string and returns a number between minValue and * maxValue. @@ -261,57 +239,41 @@ protected final LocalMaterialData readMaterial(String string) throws InvalidConf */ protected final MaterialSet readMaterials(List strings, int start) throws InvalidConfigException { - MaterialSet materials = new MaterialSet(); - for (int i = start; i < strings.size(); i++) - { - materials.parseAndAdd(strings.get(i)); - } + return MaterialSet.create(strings.subList(start, strings.size())); + } - return materials; + public final String write() + { + return makeString(); } - /** - * Sets the holder to the given parameter. Must only be used when manually - * constructing this function. The holder must of the type returned by - * {@link #getHolderType()}. - * @param holder The hoilder. - * @see #init(Object, List). - */ - public final void setHolder(T holder) + public void x(int x) { - this.holder = holder; + coords = (coords & ~X_MASK) | ((x << X_SHIFT) & X_MASK); } - /** - * @deprecated Use {@link #invalidate(String, List, String)} to invalidate - * the object. Manually validating an object is no longer needed. - * Re-validating is no longer possible, just create a new instance. - */ - @Deprecated - public final void setValid(boolean valid) + public int x() { - if (valid == false) - { - throw new UnsupportedOperationException("Use the invalidate method"); - } - if (valid == true && !isValid()) - { - throw new UnsupportedOperationException("Revalidating objects is no longer supported"); - } - // So (valid == true && isValid()), so it's safe to do nothing + return coords << (Integer.SIZE - (X_SHIFT + X_BITS)) >> (Integer.SIZE - X_BITS); } - public final String write() + public void y(int y) { - if (!valid) - { - // Show error message - return "## INVALID " + inputName.toUpperCase() + " - " + error + " ##" + System.getProperty("line.separator") + inputName + "(" - + StringHelper.join(inputArgs, ",") + ")"; - } else - { - return makeString(); - } + coords = (coords & ~Y_MASK) | ((y << Y_SHIFT) & Y_MASK); + } + + public int y() + { + return coords << (Integer.SIZE - (Y_SHIFT + Y_BITS)) >> (Integer.SIZE - Y_BITS); } + public void z(int z) + { + coords = (coords & ~Z_MASK) | ((z << Z_SHIFT) & Z_MASK); + } + + public int z() + { + return coords << (Integer.SIZE - (Z_SHIFT + Z_BITS)) >> (Integer.SIZE - Z_BITS); + } } diff --git a/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectErroredFunction.java b/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectErroredFunction.java index faed77252..fd4df9fb4 100644 --- a/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectErroredFunction.java +++ b/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectErroredFunction.java @@ -10,6 +10,7 @@ public final class CustomObjectErroredFunction extends CustomObjectConfigFunc private final Class holder; private final String name; private final List args; + private final String error; CustomObjectErroredFunction(String name, T holder, List args, String error) { @@ -18,7 +19,7 @@ public final class CustomObjectErroredFunction extends CustomObjectConfigFunc this.holder = holderClass; this.name = name; this.args = args; - this.invalidate(name, args, error); + this.error = error; } @Override @@ -28,15 +29,27 @@ public Class getHolderType() } @Override - protected void load(List args) throws InvalidConfigException + protected void load(T holder, List args) throws InvalidConfigException { throw new UnsupportedOperationException(); } + @Override + public boolean isValid() + { + return false; + } + + @Override + public String getError() + { + return error; + } + @Override public String makeString() { - return name + "(" + StringHelper.join(args, ",") + ")"; + return "## INVALID " + name.toUpperCase() + " - " + error + " ##" + System.getProperty("line.separator") + name + "(" + StringHelper.join(args, ",") + ")"; } @Override diff --git a/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectResourcesManager.java b/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectResourcesManager.java index f8e1761b4..3e5cd8021 100644 --- a/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectResourcesManager.java +++ b/common/src/main/java/com/pg85/otg/configuration/customobjects/CustomObjectResourcesManager.java @@ -84,7 +84,7 @@ public CustomObjectConfigFunction getConfigFunction(String name, T holder configFunction.init(holder, args); } catch (InvalidConfigException e) { - configFunction.invalidate(name, args, e.getMessage()); + configFunction = new CustomObjectErroredFunction<>(name, holder, args, e.getMessage()); } break; } diff --git a/common/src/main/java/com/pg85/otg/configuration/dimensions/DimensionConfigBase.java b/common/src/main/java/com/pg85/otg/configuration/dimensions/DimensionConfigBase.java index 1f62b5692..3586675e0 100644 --- a/common/src/main/java/com/pg85/otg/configuration/dimensions/DimensionConfigBase.java +++ b/common/src/main/java/com/pg85/otg/configuration/dimensions/DimensionConfigBase.java @@ -1,7 +1,5 @@ package com.pg85.otg.configuration.dimensions; -import java.util.ArrayList; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; @@ -60,13 +58,10 @@ public DimensionConfigBase() { } this.Settings.DimensionAboveHeight = worldConfig.dimensionAboveHeight; this.Settings.DimensionBelow = worldConfig.dimensionBelow; this.Settings.DimensionBelowHeight = worldConfig.dimensionBelowHeight; - LocalMaterialData[] portalMats = worldConfig.dimensionPortalMaterials.toArray(new LocalMaterialData[0]); - ArrayList portalMaterials = new ArrayList(); - for(LocalMaterialData mat : portalMats) - { - portalMaterials.add(mat.getName().toUpperCase()); - } - this.Settings.DimensionPortalMaterials = portalMaterials.toArray(new String[0]); + this.Settings.DimensionPortalMaterials = worldConfig.dimensionPortalMaterials.stream() + .map(LocalMaterialData::getName) + .map(String::toUpperCase) + .toArray(String[]::new); this.Settings.PortalColor = worldConfig.portalColor; this.Settings.PortalParticleType = worldConfig.portalParticleType; this.Settings.PortalMobType = worldConfig.portalMobType; diff --git a/common/src/main/java/com/pg85/otg/configuration/io/FileSettingsReaderOTGPlus.java b/common/src/main/java/com/pg85/otg/configuration/io/FileSettingsReaderOTGPlus.java index 4de9216eb..fa461cfe4 100644 --- a/common/src/main/java/com/pg85/otg/configuration/io/FileSettingsReaderOTGPlus.java +++ b/common/src/main/java/com/pg85/otg/configuration/io/FileSettingsReaderOTGPlus.java @@ -11,8 +11,9 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.Map.Entry; @@ -116,7 +117,7 @@ public List> getConfigFunctions(T holder, bool int bracketIndex = configFunctionString.indexOf('('); String functionName = configFunctionString.substring(0, bracketIndex); String parameters = configFunctionString.substring(bracketIndex + 1, configFunctionString.length() - 1); - List args = Arrays.asList(StringHelper.readCommaSeperatedString(parameters)); + List args = StringHelper.readCommaSeperatedString(parameters); CustomObjectConfigFunction function = manager.getConfigFunction(functionName, holder, args); if(function == null) { @@ -217,65 +218,51 @@ public void putSetting(Setting setting, S value) public void readSettings() { - BufferedReader settingsReader = null; + Path path = this.file.toPath(); - if (!file.exists()) + if(!Files.exists(path)) { return; } - try + try(BufferedReader reader = Files.newBufferedReader(path)) { - settingsReader = new BufferedReader(new FileReader(file)); int lineNumber = 0; - String thisLine; - while ((thisLine = settingsReader.readLine()) != null) + String line; + while((line = reader.readLine()) != null) { lineNumber++; - if (thisLine.trim().isEmpty()) + line = line.trim(); + if(line.isEmpty()) { // Empty line, ignore - } else if (thisLine.startsWith("#") || thisLine.startsWith("<")) + continue; + } + if(line.startsWith("#") || line.startsWith("<")) { // Comment, ignore - } else if (thisLine.contains(":") || thisLine.toLowerCase().contains("(")) + continue; + } + + int i = line.indexOf(':'); + if(line.lastIndexOf('(', i >= 0 ? i - 1 : line.length() - 1) >= 0) { - // Setting or resource - if (thisLine.contains("(") && (!thisLine.contains(":") || thisLine.indexOf('(') < thisLine.indexOf(':'))) - { - // ( is first, so it's a resource - this.configFunctions.add(new StringOnLine(thisLine.trim(), lineNumber)); - } else - { - // : is first, so it's a setting - String[] splitSettings = thisLine.split(":", 2); - this.settingsCache - .put(splitSettings[0].trim().toLowerCase(), new StringOnLine(splitSettings[1].trim(), lineNumber)); - } - } else if (thisLine.contains("=")) + // ( is first, so it's a resource + this.configFunctions.add(new StringOnLine(line, lineNumber)); + } + else if(i >= 0 || (i = line.indexOf('=')) >= 0) { + // : is first, so it's a setting + // or // Setting (old style), split it and add it - String[] splitSettings = thisLine.split("=", 2); - this.settingsCache.put(splitSettings[0].trim().toLowerCase(), new StringOnLine(splitSettings[1].trim(), lineNumber)); + this.settingsCache.put(line.substring(0, i).trim().toLowerCase(), new StringOnLine(line.substring(i + 1).trim(), lineNumber)); } } } - catch (IOException e) + catch(IOException e) { OTG.printStackTrace(LogMarker.FATAL, e); - } finally { - if (settingsReader != null) - { - try - { - settingsReader.close(); - } catch (IOException localIOException2) - { - OTG.printStackTrace(LogMarker.FATAL, localIOException2); - } - } } - } @Override diff --git a/common/src/main/java/com/pg85/otg/configuration/io/SimpleSettingsMap.java b/common/src/main/java/com/pg85/otg/configuration/io/SimpleSettingsMap.java index fd383bc87..1b06752d0 100644 --- a/common/src/main/java/com/pg85/otg/configuration/io/SimpleSettingsMap.java +++ b/common/src/main/java/com/pg85/otg/configuration/io/SimpleSettingsMap.java @@ -83,7 +83,7 @@ public List> getConfigFunctions(T holder, boolean useFallb int bracketIndex = configFunctionString.indexOf('('); String functionName = configFunctionString.substring(0, bracketIndex); String parameters = configFunctionString.substring(bracketIndex + 1, configFunctionString.length() - 1); - List args = Arrays.asList(StringHelper.readCommaSeperatedString(parameters)); + List args = StringHelper.readCommaSeperatedString(parameters); ConfigFunction function = manager.getConfigFunction(functionName, holder, args); if (function == null) { diff --git a/common/src/main/java/com/pg85/otg/configuration/settingType/DoubleArraySetting.java b/common/src/main/java/com/pg85/otg/configuration/settingType/DoubleArraySetting.java index 0dd172305..5efdc7501 100644 --- a/common/src/main/java/com/pg85/otg/configuration/settingType/DoubleArraySetting.java +++ b/common/src/main/java/com/pg85/otg/configuration/settingType/DoubleArraySetting.java @@ -1,5 +1,7 @@ package com.pg85.otg.configuration.settingType; +import java.util.List; + import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.helpers.StringHelper; @@ -31,12 +33,12 @@ public double[] read(String string) throws InvalidConfigException { return new double[0]; } - String[] split = StringHelper.readCommaSeperatedString(string); - double[] values = new double[split.length]; - for (int i = 0; i < split.length; i++) + List split = StringHelper.readCommaSeperatedString(string); + double[] values = new double[split.size()]; + for (int i = 0; i < split.size(); i++) { // Trimming the values allows "Value1, Value2" - values[i] = StringHelper.readDouble(split[i], -Double.MAX_VALUE, Double.MAX_VALUE); + values[i] = StringHelper.readDouble(split.get(i), -Double.MAX_VALUE, Double.MAX_VALUE); } return values; } diff --git a/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialListSetting.java b/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialListSetting.java index 4611ec505..be039b793 100644 --- a/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialListSetting.java +++ b/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialListSetting.java @@ -2,7 +2,7 @@ import java.util.ArrayList; -import com.pg85.otg.OTG; +import com.pg85.otg.OTGEngine; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.helpers.StringHelper; @@ -11,7 +11,7 @@ /** * Reads and writes a material. Materials are read using - * {@link OTG#readMaterial(String)} and written using + * {@link OTGEngine#readMaterial(String)} and written using * {@link LocalMaterialData#toString()}. * */ diff --git a/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetSetting.java b/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetSetting.java index 1ad63ee39..776992223 100644 --- a/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetSetting.java +++ b/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetSetting.java @@ -15,53 +15,29 @@ */ class MaterialSetSetting extends Setting { - private final String[] defaultValues; + private final MaterialSet defaultValues; MaterialSetSetting(String name, DefaultMaterial... defaultMaterials) { super(name); - - // Convert to string list - this.defaultValues = new String[defaultMaterials.length]; - for (int i = 0; i < defaultMaterials.length; i++) - { - this.defaultValues[i] = defaultMaterials[i].toString(); - } + this.defaultValues = MaterialSet.create(defaultMaterials); } public MaterialSetSetting(String name, String... defaultValues) { super(name); - this.defaultValues = defaultValues; + this.defaultValues = MaterialSet.create(defaultValues); } @Override public MaterialSet getDefaultValue() { - try - { - MaterialSet blocks = new MaterialSet(); - for (String blockName : defaultValues) - { - blocks.parseAndAdd(blockName); - } - return blocks; - } catch (InvalidConfigException e) - { - throw new AssertionError(e); - } + return defaultValues; } @Override public MaterialSet read(String string) throws InvalidConfigException { - MaterialSet blocks = new MaterialSet(); - - for (String blockName : StringHelper.readCommaSeperatedString(string)) - { - blocks.parseAndAdd(blockName); - } - - return blocks; + return MaterialSet.create(StringHelper.readCommaSeperatedString(string)); } } diff --git a/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetting.java b/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetting.java index 98ba72bc4..26024eb9d 100644 --- a/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetting.java +++ b/common/src/main/java/com/pg85/otg/configuration/settingType/MaterialSetting.java @@ -1,6 +1,6 @@ package com.pg85.otg.configuration.settingType; -import com.pg85.otg.OTG; +import com.pg85.otg.OTGEngine; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.materials.MaterialHelper; @@ -8,7 +8,7 @@ /** * Reads and writes a material. Materials are read using - * {@link OTG#readMaterial(String)} and written using + * {@link OTGEngine#readMaterial(String)} and written using * {@link LocalMaterialData#toString()}. * */ diff --git a/common/src/main/java/com/pg85/otg/configuration/settingType/StringListSetting.java b/common/src/main/java/com/pg85/otg/configuration/settingType/StringListSetting.java index d3f48136d..a28e6ce54 100644 --- a/common/src/main/java/com/pg85/otg/configuration/settingType/StringListSetting.java +++ b/common/src/main/java/com/pg85/otg/configuration/settingType/StringListSetting.java @@ -31,7 +31,7 @@ public List getDefaultValue() @Override public List read(String string) throws InvalidConfigException { - return Arrays.asList(StringHelper.readCommaSeperatedString(string)); + return StringHelper.readCommaSeperatedString(string); } @Override diff --git a/common/src/main/java/com/pg85/otg/configuration/settingType/SurfaceGeneratorSetting.java b/common/src/main/java/com/pg85/otg/configuration/settingType/SurfaceGeneratorSetting.java index 796af4a5a..c8c4d6a84 100644 --- a/common/src/main/java/com/pg85/otg/configuration/settingType/SurfaceGeneratorSetting.java +++ b/common/src/main/java/com/pg85/otg/configuration/settingType/SurfaceGeneratorSetting.java @@ -1,5 +1,7 @@ package com.pg85.otg.configuration.settingType; +import java.util.List; + import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.generator.surface.MesaSurfaceGenerator; import com.pg85.otg.generator.surface.MultipleLayersSurfaceGenerator; @@ -35,7 +37,7 @@ public SurfaceGenerator read(String string) throws InvalidConfigException { return mesa; } - String[] parts = StringHelper.readCommaSeperatedString(string); + List parts = StringHelper.readCommaSeperatedString(string); return new MultipleLayersSurfaceGenerator(parts); } return new SimpleSurfaceGenerator(); diff --git a/common/src/main/java/com/pg85/otg/configuration/world/WorldConfig.java b/common/src/main/java/com/pg85/otg/configuration/world/WorldConfig.java index 3b0a40363..1d6003c36 100644 --- a/common/src/main/java/com/pg85/otg/configuration/world/WorldConfig.java +++ b/common/src/main/java/com/pg85/otg/configuration/world/WorldConfig.java @@ -35,6 +35,8 @@ import com.pg85.otg.util.materials.MaterialHelper; import com.pg85.otg.util.minecraft.defaults.DefaultBiome; +import it.unimi.dsi.fastutil.ints.Int2IntMap; + public class WorldConfig extends ConfigFile { public final File settingsDir; @@ -109,7 +111,7 @@ public class WorldConfig extends ConfigFile public int imageXOffset; public int imageZOffset; - public HashMap biomeColorMap; + public Int2IntMap biomeColorMap; // Look settings public int worldFog; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo2/BO2.java b/common/src/main/java/com/pg85/otg/customobjects/bo2/BO2.java index d8dc2b6d0..b132e3239 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo2/BO2.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo2/BO2.java @@ -433,11 +433,11 @@ private void readCoordinates() ObjectCoordinate coordinate = coordinates.get(i); data[0][i] = coordinate; - coordinate = coordinate.rotate(); + coordinate = coordinate.rotate(Rotation.WEST); data[1][i] = coordinate; - coordinate = coordinate.rotate(); + coordinate = coordinate.rotate(Rotation.WEST); data[2][i] = coordinate; - coordinate = coordinate.rotate(); + coordinate = coordinate.rotate(Rotation.WEST); data[3][i] = coordinate; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo2/ObjectCoordinate.java b/common/src/main/java/com/pg85/otg/customobjects/bo2/ObjectCoordinate.java index 4feaeec0f..a1d4204ab 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo2/ObjectCoordinate.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo2/ObjectCoordinate.java @@ -2,6 +2,7 @@ import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.exception.InvalidConfigException; +import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.materials.MaterialHelper; class ObjectCoordinate @@ -42,17 +43,37 @@ public int hashCode() return hash; } - ObjectCoordinate rotate() + ObjectCoordinate rotate(Rotation rotation) { - ObjectCoordinate newCoordinate = new ObjectCoordinate(this.z, this.y, (this.x * -1)); - newCoordinate.material = this.material.rotate(); + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z; + rotatedZ = -x; + break; + case SOUTH: + rotatedX = -x; + rotatedZ = -z; + break; + case EAST: + rotatedX = -z; + rotatedZ = x; + break; + default: + throw new IllegalArgumentException(); + } + + ObjectCoordinate newCoordinate = new ObjectCoordinate(rotatedX, this.y, rotatedZ); + newCoordinate.material = this.material.rotate(rotation); newCoordinate.branchOdds = this.branchOdds; if (this.branchDirection != -1) { - newCoordinate.branchDirection = this.branchDirection + 1; - if (newCoordinate.branchDirection > 3) - newCoordinate.branchDirection = 0; + newCoordinate.branchDirection = (this.branchDirection + rotation.ordinal()) % 4; } return newCoordinate; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3.java index f2eb2f513..b8046d858 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3.java @@ -1,8 +1,11 @@ package com.pg85.otg.customobjects.bo3; import java.io.File; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; -import java.util.HashSet; +import java.util.Arrays; +import java.util.List; +import java.util.Map.Entry; import java.util.Random; import com.pg85.otg.OTG; @@ -32,8 +35,13 @@ import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.helpers.MathHelper; import com.pg85.otg.util.helpers.RandomHelper; +import com.pg85.otg.util.materials.MaterialSet; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; + public class BO3 implements StructuredCustomObject { private BO3Config settings; @@ -114,76 +122,71 @@ public boolean loadChecks() @Override public boolean spawnFromSapling(LocalWorld world, Random random, Rotation rotation, int x, int y, int z) { - BO3BlockFunction[] blocks = this.settings.getBlocks(rotation.getRotationId()); - - ArrayList blocksToSpawn = new ArrayList(); + List blocksToSpawn = new ArrayList<>(); + ObjectExtrusionHelper extrusionHelper = new ObjectExtrusionHelper(this.settings.extrudeMode, this.settings.extrudeThroughBlocks); - ObjectExtrusionHelper oeh = new ObjectExtrusionHelper(this.settings.extrudeMode, this.settings.extrudeThroughBlocks); - HashSet chunks = new HashSet(); - - LocalMaterialData localMaterial; - for (BO3BlockFunction block : blocks) + for(BO3BlockFunction block : this.settings.blocks(rotation)) { - localMaterial = world.getMaterial(x + block.x, y + block.y, z + block.z, null); + LocalMaterialData localMaterial = world.getMaterial(x + block.x(), y + block.y(), z + block.z(), null); // Ignore blocks in the ground when checking spawn conditions - if (block.y >= 0) + if(block.y() >= 0 && blocksFromSapling(localMaterial)) { // Do not spawn if non-tree blocks are in the way - if ( - !localMaterial.isAir() && - !localMaterial.isMaterial(DefaultMaterial.LOG) && - !localMaterial.isMaterial(DefaultMaterial.LOG_2) && - !localMaterial.isMaterial(DefaultMaterial.LEAVES) && - !localMaterial.isMaterial(DefaultMaterial.LEAVES_2) && - !localMaterial.isMaterial(DefaultMaterial.SAPLING) - ) - { - return false; - } + return false; } // Only overwrite air - if (localMaterial.isAir()) + if(localMaterial.isAir()) { - chunks.add(ChunkCoordinate.fromBlockCoords(x + block.x, z + block.z)); blocksToSpawn.add(block); } - oeh.addBlock((BO3BlockFunction) block); + extrusionHelper.addBlock(block); } - for (BO3BlockFunction block : blocksToSpawn) { - block.spawn(world, random, x + block.x, y + block.y, z + block.z, null, false); + + for(BO3BlockFunction block : blocksToSpawn) + { + block.spawn(world, random, x + block.x(), y + block.y(), z + block.z(), null, false); } - oeh.extrude(world, random, x, y, z, null, false); - handleBO3Functions(null, world, random, rotation, x, y, z, chunks, null); + + extrusionHelper.extrude(world, random, x, y, z, null, false); + handleBO3Functions(null, world, random, rotation, x, y, z, null); return true; } + private static MaterialSet replaceableFromSapling; + + private static boolean blocksFromSapling(LocalMaterialData material) + { + MaterialSet replaceableFromSapling; + if((replaceableFromSapling = BO3.replaceableFromSapling) == null) + { + BO3.replaceableFromSapling = replaceableFromSapling = MaterialSet.create(DefaultMaterial.AIR, DefaultMaterial.LOG, DefaultMaterial.LOG_2, DefaultMaterial.LEAVES, DefaultMaterial.LEAVES_2, DefaultMaterial.SAPLING); + } + return !replaceableFromSapling.contains(material); + } + // Force spawns a BO3 object. Used by /otg spawn and bo3AtSpawn. // This method ignores the maxPercentageOutsideBlock setting @Override public boolean spawnForced(LocalWorld world, Random random, Rotation rotation, int x, int y, int z) { - BO3BlockFunction[] blocks = this.settings.getBlocks(rotation.getRotationId()); - ObjectExtrusionHelper oeh = new ObjectExtrusionHelper(this.settings.extrudeMode, this.settings.extrudeThroughBlocks); - HashSet chunks = new HashSet(); + ObjectExtrusionHelper extrusionHelper = new ObjectExtrusionHelper(this.settings.extrudeMode, this.settings.extrudeThroughBlocks); - for (BO3BlockFunction block : blocks) + for(BO3BlockFunction block : this.settings.blocks(rotation)) { // Places if BO3 is in placeAnyway mode, or if target block is a source block - if (this.settings.outsideSourceBlock == OutsideSourceBlock.placeAnyway - || this.settings.sourceBlocks.contains(block.material)) + if(this.settings.outsideSourceBlock == OutsideSourceBlock.placeAnyway || this.settings.sourceBlocks.contains(world.getMaterial(x + block.x(), y + block.y(), z + block.z(), null))) { - block.spawn(world, random, x + block.x, y + block.y, z + block.z, null, this.doReplaceBlocks()); - oeh.addBlock(block); - chunks.add(ChunkCoordinate.fromBlockCoords(x + block.x, z + block.z)); + block.spawn(world, random, x + block.x(), y + block.y(), z + block.z(), null, this.doReplaceBlocks()); + extrusionHelper.addBlock(block); } } - oeh.extrude(world, random, x, y, z, null, this.doReplaceBlocks()); - handleBO3Functions(null, world, random, rotation, x, y, z, chunks, null); + extrusionHelper.extrude(world, random, x, y, z, null, this.doReplaceBlocks()); + handleBO3Functions(null, world, random, rotation, x, y, z, null); return true; } @@ -253,104 +256,89 @@ private boolean spawn(LocalWorld world, Random random, int x, int z, int minY, i offsetY = baseY + this.getOffsetAndVariance(random, this.settings.spawnHeightOffset, this.settings.spawnHeightVariance); return trySpawnAt(null, world, random, rotation, x, offsetY, z, minY, maxY, baseY, chunkBeingPopulated, replaceBlocks); } - + // Used for trees, customobjects and customstructures during population. public boolean trySpawnAt(CustomStructure structure, LocalWorld world, Random random, Rotation rotation, int x, int y, int z, int minY, int maxY, int baseY, ChunkCoordinate chunkBeingPopulated, boolean replaceBlocks) { - if (y < PluginStandardValues.WORLD_DEPTH || y >= PluginStandardValues.WORLD_HEIGHT) // Isn't this already done before this method is called? + if(y < PluginStandardValues.WORLD_DEPTH || y >= PluginStandardValues.WORLD_HEIGHT) // Isn't this already done before this method is called? { return false; } // Height check - if (y < minY || y > maxY) + if(y < minY || y > maxY) { return false; } - - BO3Check[] checks = this.settings.bo3Checks[rotation.getRotationId()]; // Check for spawning - for (BO3Check check : checks) + for(BO3Check check : this.settings.bo3Checks[rotation.getRotationId()]) { - // Don't apply spawn height offset/variance to block checks, - // they should only be used with highestBlock/highestSolidBlock, - // and need to check for things like grass at the original spawn y. - if (check.preventsSpawn(world, x + check.x, baseY + check.y, z + check.z, chunkBeingPopulated)) + // Don't apply spawn height offset/variance to block checks, + // they should only be used with highestBlock/highestSolidBlock, + // and need to check for things like grass at the original spawn y. + if(check.preventsSpawn(world, x + check.x(), baseY + check.y(), z + check.z(), chunkBeingPopulated)) { // A check failed return false; } - } + } + + boolean spawnAllBlocks = this.settings.outsideSourceBlock == OutsideSourceBlock.placeAnyway && this.settings.maxPercentageOutsideSourceBlock >= 100; + int maxBlocksOutsideSourceBlock = (int) (this.settings.blockCount() * (this.settings.maxPercentageOutsideSourceBlock / 100.0)); + + List blocksToSpawn = !spawnAllBlocks || rotation != Rotation.NORTH ? new ArrayList<>() : null; + LongSet chunks; + Entry structureInfo; + if(structure != null) + { + chunks = new LongOpenHashSet(); + structureInfo = new SimpleEntry<>(structure, chunks); + } + else + { + chunks = null; + structureInfo = null; + } + ObjectExtrusionHelper extrusionHelper = new ObjectExtrusionHelper(this.settings.extrudeMode, this.settings.extrudeThroughBlocks); - BO3BlockFunction[] blocks = this.settings.getBlocks(rotation.getRotationId()); - HashSet loadedChunks = new HashSet(); - ChunkCoordinate chunkCoord; - for (BO3BlockFunction block : blocks) + for(BO3BlockFunction block : this.settings.blocks(rotation)) { - if (y + block.y < PluginStandardValues.WORLD_DEPTH || y + block.y >= PluginStandardValues.WORLD_HEIGHT) + if(y + block.y() < PluginStandardValues.WORLD_DEPTH || y + block.y() >= PluginStandardValues.WORLD_HEIGHT) + { + return false; + } + if(chunkBeingPopulated != null && !OTG.IsInAreaBeingPopulated(x + block.x(), z + block.z(), chunkBeingPopulated)) { return false; } - chunkCoord = ChunkCoordinate.fromBlockCoords(x + block.x, z + block.z); - if(!loadedChunks.contains(chunkCoord)) - { - if(chunkBeingPopulated != null && !OTG.IsInAreaBeingPopulated(x + block.x, z + block.z, chunkBeingPopulated)) - //if(!world.chunkExists(x + block.x, y + block.y, z + block.z)) - { - // Cannot spawn BO3, part of world is not loaded - return false; - } - loadedChunks.add(chunkCoord); - } - } - - ArrayList blocksToSpawn = new ArrayList(); - - ObjectExtrusionHelper oeh = new ObjectExtrusionHelper(this.settings.extrudeMode, this.settings.extrudeThroughBlocks); - HashSet chunks = new HashSet(); - - int blocksOutsideSourceBlock = 0; - int maxBlocksOutsideSourceBlock = (int) Math.ceil( - blocks.length * (this.settings.maxPercentageOutsideSourceBlock / 100.0)); - for (BO3BlockFunction block : blocks) - { - if ( - ( - ( - this.settings.maxPercentageOutsideSourceBlock < 100 && - blocksOutsideSourceBlock <= maxBlocksOutsideSourceBlock - ) || - this.settings.outsideSourceBlock == OutsideSourceBlock.dontPlace - ) && - !this.settings.sourceBlocks.contains(world.getMaterial(x + block.x, y + block.y, z + block.z, chunkBeingPopulated)) - ) + boolean outsideSourceBlock = !spawnAllBlocks && !this.settings.sourceBlocks.contains(world.getMaterial(x + block.x(), y + block.y(), z + block.z(), chunkBeingPopulated)); + // this.settings.maxPercentageOutsideSourceBlock < 100 + // and + // !this.settings.sourceBlocks.contains(world.getMaterial(x + block.x(), y + block.y(), z + block.z(), chunkBeingPopulated)) + if(outsideSourceBlock && this.settings.maxPercentageOutsideSourceBlock < 100) { - blocksOutsideSourceBlock++; - if (blocksOutsideSourceBlock > maxBlocksOutsideSourceBlock) + if(--maxBlocksOutsideSourceBlock < 0) { - // Too many blocks outside source block return false; } - - if (this.settings.outsideSourceBlock == OutsideSourceBlock.placeAnyway) - { - chunks.add(ChunkCoordinate.fromBlockCoords(x + block.x, z + block.z)); - blocksToSpawn.add(block); - } - } else { - chunks.add(ChunkCoordinate.fromBlockCoords(x + block.x, z + block.z)); - blocksToSpawn.add(block); } - if (block instanceof BO3BlockFunction) + // this.settings.outsideSourceBlock == OutsideSourceBlock.placeAnyway + // or + // this.settings.sourceBlocks.contains(world.getMaterial(x + block.x(), y + block.y(), z + block.z(), chunkBeingPopulated)) + if(!outsideSourceBlock || this.settings.outsideSourceBlock == OutsideSourceBlock.placeAnyway) { - oeh.addBlock((BO3BlockFunction) block); + if(blocksToSpawn != null) + blocksToSpawn.add(block); + if(chunks != null) + chunks.add(ChunkCoordinate.packed((x + block.x()) >> 4, (z + block.z()) >> 4)); } + extrusionHelper.addBlock(block); } // Call event - if (!OTG.fireCanCustomObjectSpawnEvent(this, world, x, y, z)) + if(!OTG.fireCanCustomObjectSpawnEvent(this, world, x, y, z)) { // Cancelled return false; @@ -358,48 +346,55 @@ public boolean trySpawnAt(CustomStructure structure, LocalWorld world, Random ra // Spawn - for (BO3BlockFunction block : blocksToSpawn) + for(BO3BlockFunction block : blocksToSpawn == null ? Arrays.asList(this.settings.getBlocks()) : blocksToSpawn) { - block.spawn(world, random, x + block.x, y + block.y, z + block.z, chunkBeingPopulated, replaceBlocks); + block.spawn(world, random, x + block.x(), y + block.y(), z + block.z(), chunkBeingPopulated, replaceBlocks); } - oeh.extrude(world, random, x, y, z, chunkBeingPopulated, replaceBlocks); - handleBO3Functions(structure, world, random, rotation, x, y, z, chunks, chunkBeingPopulated); + extrusionHelper.extrude(world, random, x, y, z, chunkBeingPopulated, replaceBlocks); + handleBO3Functions(structureInfo, world, random, rotation, x, y, z, chunkBeingPopulated); return true; } - public void handleBO3Functions(CustomStructure structure, LocalWorld world, Random random, Rotation rotation, int x, int y, int z, HashSet chunks, ChunkCoordinate chunkBeingPopulated) + private void handleBO3Functions(Entry structureInfo, LocalWorld world, Random random, Rotation rotation, int x, int y, int z, ChunkCoordinate chunkBeingPopulated) { - HashSet chunksCustomObject = new HashSet(); + CustomStructure structure; + LongSet chunks; + boolean placeholder; + if(placeholder = (structureInfo == null)) + { + structure = new BO3CustomStructure(new BO3CustomStructureCoordinate(world, this, this.getName(), Rotation.NORTH, x, (short) 0, z)); + chunks = new LongOpenHashSet(); + } + else + { + structure = structureInfo.getKey(); + chunks = structureInfo.getValue(); + } - HashSet newModDataInObject = new HashSet(); - BO3ModDataFunction[] modDataInObject = this.settings.modDataFunctions[rotation.getRotationId()]; - for (BO3ModDataFunction modData : modDataInObject) + for(BO3ModDataFunction modData : this.settings.modDataFunctions[rotation.getRotationId()]) { BO3ModDataFunction newModData = new BO3ModDataFunction(); - newModData.y = y + modData.y; - newModData.x = x + modData.x; - newModData.z = z + modData.z; + newModData.y(y + modData.y()); + newModData.x(x + modData.x()); + newModData.z(z + modData.z()); newModData.modData = modData.modData; newModData.modId = modData.modId; - newModDataInObject.add(newModData); - chunks.add(ChunkCoordinate.fromBlockCoords(newModData.x, newModData.z)); - chunksCustomObject.add(ChunkCoordinate.fromBlockCoords(newModData.x, newModData.z)); + structure.modDataManager.modData.add(newModData); + chunks.add(ChunkCoordinate.packed(newModData.x() >> 4, newModData.z() >> 4)); } - HashSet newSpawnerDataInObject = new HashSet(); - BO3SpawnerFunction[] spawnerDataInObject = settings.spawnerFunctions[rotation.getRotationId()]; - for (BO3SpawnerFunction spawnerData : spawnerDataInObject) + for(BO3SpawnerFunction spawnerData : settings.spawnerFunctions[rotation.getRotationId()]) { BO3SpawnerFunction newSpawnerData = new BO3SpawnerFunction(); - newSpawnerData.y = y + spawnerData.y; - newSpawnerData.x = x + spawnerData.x; - newSpawnerData.z = z + spawnerData.z; + newSpawnerData.y(y + spawnerData.y()); + newSpawnerData.x(x + spawnerData.x()); + newSpawnerData.z(z + spawnerData.z()); newSpawnerData.mobName = spawnerData.mobName; newSpawnerData.originalnbtFileName = spawnerData.originalnbtFileName; @@ -422,20 +417,17 @@ public void handleBO3Functions(CustomStructure structure, LocalWorld world, Rand newSpawnerData.yaw = spawnerData.yaw; newSpawnerData.pitch = spawnerData.pitch; - newSpawnerDataInObject.add(newSpawnerData); - chunks.add(ChunkCoordinate.fromBlockCoords(newSpawnerData.x, newSpawnerData.z)); - chunksCustomObject.add(ChunkCoordinate.fromBlockCoords(newSpawnerData.x, newSpawnerData.z)); + structure.spawnerManager.spawnerData.add(newSpawnerData); + chunks.add(ChunkCoordinate.packed(newSpawnerData.x() >> 4, newSpawnerData.z() >> 4)); } - HashSet newParticleDataInObject = new HashSet(); - BO3ParticleFunction[] particleDataInObject = this.settings.particleFunctions[rotation.getRotationId()]; - for (BO3ParticleFunction particleData : particleDataInObject) + for(BO3ParticleFunction particleData : this.settings.particleFunctions[rotation.getRotationId()]) { BO3ParticleFunction newParticleData = new BO3ParticleFunction(); - newParticleData.y = y + particleData.y; - newParticleData.x = x + particleData.x; - newParticleData.z = z + particleData.z; + newParticleData.y(y + particleData.y()); + newParticleData.x(x + particleData.x()); + newParticleData.z(z + particleData.z()); newParticleData.particleName = particleData.particleName; @@ -449,41 +441,23 @@ public void handleBO3Functions(CustomStructure structure, LocalWorld world, Rand newParticleData.velocityYSet = particleData.velocityYSet; newParticleData.velocityZSet = particleData.velocityZSet; - newParticleDataInObject.add(newParticleData); - chunks.add(ChunkCoordinate.fromBlockCoords(newParticleData.x, newParticleData.z)); - chunksCustomObject.add(ChunkCoordinate.fromBlockCoords(newParticleData.x, newParticleData.z)); + structure.particlesManager.particleData.add(newParticleData); + chunks.add(ChunkCoordinate.packed(newParticleData.x() >> 4, newParticleData.z() >> 4)); } - if (structure != null) + for(LongIterator iterator = chunks.iterator(); iterator.hasNext();) { - structure.modDataManager.modData.addAll(newModDataInObject); - structure.particlesManager.particleData.addAll(newParticleDataInObject); - structure.spawnerManager.spawnerData.addAll(newSpawnerDataInObject); - - for (ChunkCoordinate structureCoord : chunks) - { - world.getStructureCache().addBo3ToStructureCache(structureCoord, structure, true); - } - } else { - CustomStructure placeHolderStructure = new BO3CustomStructure(new BO3CustomStructureCoordinate(world, this, this.getName(), Rotation.NORTH, x, (short) 0, z)); - placeHolderStructure.modDataManager.modData.addAll(newModDataInObject); - placeHolderStructure.particlesManager.particleData.addAll(newParticleDataInObject); - placeHolderStructure.spawnerManager.spawnerData.addAll(newSpawnerDataInObject); - - for (ChunkCoordinate structureCoord : chunksCustomObject) - { - world.getStructureCache().addBo3ToStructureCache(structureCoord, placeHolderStructure, false); - } + long packed = iterator.nextLong(); + world.getStructureCache().addBo3ToStructureCache(ChunkCoordinate.fromPacked(packed), structure, !placeholder); } - BO3EntityFunction[] entityDataInObject = this.settings.entityFunctions[rotation.getRotationId()]; - for (BO3EntityFunction entity : entityDataInObject) + for(BO3EntityFunction entity : this.settings.entityFunctions[rotation.getRotationId()]) { BO3EntityFunction newEntityData = new BO3EntityFunction(); - newEntityData.y = y + entity.y; - newEntityData.x = x + entity.x; - newEntityData.z = z + entity.z; + newEntityData.y(y + entity.y()); + newEntityData.x(x + entity.x()); + newEntityData.z(z + entity.z()); newEntityData.name = entity.name; newEntityData.resourceLocation = entity.resourceLocation; @@ -493,7 +467,7 @@ public void handleBO3Functions(CustomStructure structure, LocalWorld world, Rand newEntityData.namedBinaryTag = entity.namedBinaryTag; newEntityData.rotation = entity.rotation; - world.spawnEntity(newEntityData, chunkBeingPopulated); + world.spawnEntity(newEntityData, chunkBeingPopulated); } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Config.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Config.java index 3dc85ecda..ef0dd928c 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Config.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Config.java @@ -4,9 +4,10 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; -import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; import com.pg85.otg.configuration.io.SettingsReaderOTGPlus; @@ -20,7 +21,6 @@ import com.pg85.otg.customobjects.bo3.bo3function.BO3EntityFunction; import com.pg85.otg.customobjects.bo3.bo3function.BO3ModDataFunction; import com.pg85.otg.customobjects.bo3.bo3function.BO3ParticleFunction; -import com.pg85.otg.customobjects.bo3.bo3function.BO3RandomBlockFunction; import com.pg85.otg.customobjects.bo3.bo3function.BO3SpawnerFunction; import com.pg85.otg.customobjects.bo3.bo3function.BO3WeightedBranchFunction; import com.pg85.otg.customobjects.bo3.checks.BO3Check; @@ -28,7 +28,7 @@ import com.pg85.otg.customobjects.bo3.checks.ModCheckNot; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.bo3.BoundingBox; -import com.pg85.otg.util.bo3.NamedBinaryTag; +import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.materials.MaterialSet; import com.pg85.otg.util.minecraft.defaults.DefaultStructurePart; @@ -60,23 +60,7 @@ public class BO3Config extends CustomObjectConfigFile int maxPercentageOutsideSourceBlock; OutsideSourceBlock outsideSourceBlock; - // Store blocks in arrays instead of as BO3BlockFunctions, - // since that gives way too much overhead memory wise. - // We may have tens of millions of blocks, java doesn't handle lots of small - // classes well. - private byte[][] blocksX; - private short[][] blocksY; - private byte[][] blocksZ; - private LocalMaterialData[][] blocksMaterial; - private String[] blocksMetaDataName; - private NamedBinaryTag[] blocksMetaDataTag; - - private LocalMaterialData[][][] randomBlocksBlocks; - private byte[][] randomBlocksBlockChances; - private String[][] randomBlocksMetaDataNames; - private NamedBinaryTag[][] randomBlocksMetaDataTags; - private byte[] randomBlocksBlockCount; - // + private BO3BlockFunction[] blocks; BO3Check[][] bo3Checks = new BO3Check[4][]; int maxBranchDepth; @@ -128,7 +112,7 @@ private void readResources() throws InvalidConfigException if (res instanceof BO3BlockFunction) { BO3BlockFunction block = (BO3BlockFunction) res; - box.expandToFit(block.x, block.y, block.z); + box.expandToFit(block.x(), block.y(), block.z()); tempBlocksList.add(block); } else { if (res instanceof BO3Check) @@ -179,45 +163,10 @@ public void setBranches(List branches) this.branches[0] = branches.toArray(new BO3BranchFunction[branches.size()]); } - public void extractBlocks(List tempBlocksList) - { - this.blocksX = new byte[4][tempBlocksList.size()]; - this.blocksY = new short[4][tempBlocksList.size()]; - this.blocksZ = new byte[4][tempBlocksList.size()]; - this.blocksMaterial = new LocalMaterialData[4][tempBlocksList.size()]; - this.blocksMetaDataName = new String[tempBlocksList.size()]; - this.blocksMetaDataTag = new NamedBinaryTag[tempBlocksList.size()]; - - this.randomBlocksBlocks = new LocalMaterialData[4][tempBlocksList.size()][]; - this.randomBlocksBlockChances = new byte[tempBlocksList.size()][]; - this.randomBlocksMetaDataNames = new String[tempBlocksList.size()][]; - this.randomBlocksMetaDataTags = new NamedBinaryTag[tempBlocksList.size()][]; - this.randomBlocksBlockCount = new byte[tempBlocksList.size()]; - - for (int i = 0; i < tempBlocksList.size(); i++) - { - BO3BlockFunction block = tempBlocksList.get(i); - // We can probably just break if null? - if (block != null) - { - this.blocksX[0][i] = (byte) block.x; - this.blocksY[0][i] = (short) block.y; - this.blocksZ[0][i] = (byte) block.z; - this.blocksMaterial[0][i] = block.material; - this.blocksMetaDataName[i] = block.metaDataName; - this.blocksMetaDataTag[i] = block.metaDataTag; - - if (block instanceof BO3RandomBlockFunction) - { - this.randomBlocksBlocks[0][i] = ((BO3RandomBlockFunction) block).blocks; - this.randomBlocksBlockChances[i] = ((BO3RandomBlockFunction) block).blockChances; - this.randomBlocksMetaDataNames[i] = ((BO3RandomBlockFunction) block).metaDataNames; - this.randomBlocksMetaDataTags[i] = ((BO3RandomBlockFunction) block).metaDataTags; - this.randomBlocksBlockCount[i] = ((BO3RandomBlockFunction) block).blockCount; - } - } - } - } + public void extractBlocks(List tempBlocksList) + { + this.blocks = tempBlocksList.toArray(new BO3BlockFunction[tempBlocksList.size()]); + } /** * Gets the file this config will be written to. May be null if the config will @@ -230,37 +179,42 @@ public File getFile() return this.reader.getFile(); } - public BO3BlockFunction[] getBlocks(int rotation) - { - BO3BlockFunction[] blocksOTGPlus = new BO3BlockFunction[this.blocksX[rotation].length]; - - BO3BlockFunction block; - for (int i = 0; i < this.blocksX[rotation].length; i++) - { - if (this.randomBlocksBlocks[rotation][i] != null) - { - block = new BO3RandomBlockFunction(this); - ((BO3RandomBlockFunction) block).blocks = this.randomBlocksBlocks[rotation][i]; - ((BO3RandomBlockFunction) block).blockChances = this.randomBlocksBlockChances[i]; - ((BO3RandomBlockFunction) block).metaDataNames = this.randomBlocksMetaDataNames[i]; - ((BO3RandomBlockFunction) block).metaDataTags = this.randomBlocksMetaDataTags[i]; - ((BO3RandomBlockFunction) block).blockCount = this.randomBlocksBlockCount[i]; - } else { - block = new BO3BlockFunction(this); - } - - block.x = this.blocksX[rotation][i]; - block.y = this.blocksY[rotation][i]; - block.z = this.blocksZ[rotation][i]; - block.material = this.blocksMaterial[rotation][i]; - block.metaDataName = this.blocksMetaDataName[i]; - block.metaDataTag = this.blocksMetaDataTag[i]; - - blocksOTGPlus[i] = block; - } - - return blocksOTGPlus; - } + public BO3BlockFunction[] getBlocks() + { + return this.blocks; + } + + public int blockCount() + { + return this.blocks.length; + } + + public Iterable blocks(Rotation rotation) + { + if(rotation == Rotation.NORTH) + { + return Arrays.asList(this.blocks); + } + + return () -> new Iterator() + { + int i; + + @Override + public boolean hasNext() + { + return this.i < BO3Config.this.blocks.length; + } + + @Override + public BO3BlockFunction next() + { + if(this.i >= BO3Config.this.blocks.length) + throw new NoSuchElementException(); + return BO3Config.this.blocks[this.i++].rotate(rotation); + } + }; + } protected BO3BranchFunction[] getbranches() { @@ -456,31 +410,10 @@ private void writeResources(SettingsWriterOTGPlus writer) throws IOException writer.comment(" MinecraftObject(0,0,0," + DefaultStructurePart.IGLOO_BOTTOM.getPath() + ")"); writer.comment(" spawns the bottom part of an igloo."); - for (int i = 0; i < this.blocksX[0].length; i++) - { - BO3BlockFunction blockFunction; - - if (this.randomBlocksBlocks[0][i] != null) - { - blockFunction = new BO3RandomBlockFunction(this); - ((BO3RandomBlockFunction) blockFunction).blocks = this.randomBlocksBlocks[0][i]; - ((BO3RandomBlockFunction) blockFunction).blockChances = this.randomBlocksBlockChances[i]; - ((BO3RandomBlockFunction) blockFunction).metaDataNames = this.randomBlocksMetaDataNames[i]; - ((BO3RandomBlockFunction) blockFunction).metaDataTags = this.randomBlocksMetaDataTags[i]; - ((BO3RandomBlockFunction) blockFunction).blockCount = this.randomBlocksBlockCount[i]; - } else { - blockFunction = new BO3BlockFunction(this); - } - - blockFunction.x = this.blocksX[0][i]; - blockFunction.y = this.blocksY[0][i]; - blockFunction.z = this.blocksZ[0][i]; - blockFunction.material = this.blocksMaterial[0][i]; - blockFunction.metaDataTag = this.blocksMetaDataTag[i]; - blockFunction.metaDataName = this.blocksMetaDataName[i]; - - writer.function(blockFunction); - } + for(BO3BlockFunction block : this.blocks) + { + writer.function(block); + } writer.bigTitle("BO3 checks"); writer.comment("Require a condition at a certain location in order for the BO3 to be spawned."); @@ -700,72 +633,43 @@ public void rotateBlocksAndChecks() { for (int i = 1; i < 4; i++) { - BO3BlockFunction[] blocks = getBlocks(i); - BO3BlockFunction[] blocksPreviousRotation = getBlocks(i - 1); - - // Blocks (blocks[i - 1] is previous rotation) - this.blocksX[i] = new byte[this.blocksX[i - 1].length]; - this.blocksY[i] = new short[this.blocksX[i - 1].length]; - this.blocksZ[i] = new byte[this.blocksX[i - 1].length]; - this.blocksMaterial[i] = new LocalMaterialData[this.blocksX[i - 1].length]; - - this.randomBlocksBlocks[i] = new LocalMaterialData[this.blocksX[i - 1].length][]; - - for (int j = 0; j < blocks.length; j++) - { - blocks[j] = blocksPreviousRotation[j].rotate(); - } - for (int h = 0; h < blocks.length; h++) - { - BO3BlockFunction block = blocks[h]; - this.blocksX[i][h] = (byte) block.x; - this.blocksY[i][h] = (short) block.y; - this.blocksZ[i][h] = (byte) block.z; - this.blocksMaterial[i][h] = block.material; - - if (block instanceof BO3RandomBlockFunction) - { - this.randomBlocksBlocks[i][h] = ((BO3RandomBlockFunction) block).blocks; - } - } - // BO3 checks this.bo3Checks[i] = new BO3Check[this.bo3Checks[i - 1].length]; for (int j = 0; j < this.bo3Checks[i].length; j++) { - this.bo3Checks[i][j] = this.bo3Checks[i - 1][j].rotate(); + this.bo3Checks[i][j] = this.bo3Checks[i - 1][j].rotate(Rotation.WEST); } // Branches this.branches[i] = new BO3BranchFunction[this.branches[i - 1].length]; for (int j = 0; j < this.branches[i].length; j++) { - this.branches[i][j] = this.branches[i - 1][j].rotate(); + this.branches[i][j] = this.branches[i - 1][j].rotate(Rotation.WEST); } // Bounding box - this.boundingBoxes[i] = this.boundingBoxes[i - 1].rotate(); + this.boundingBoxes[i] = this.boundingBoxes[i - 1].rotate(Rotation.WEST); this.entityFunctions[i] = new BO3EntityFunction[this.entityFunctions[i - 1].length]; for (int j = 0; j < this.entityFunctions[i].length; j++) { - this.entityFunctions[i][j] = this.entityFunctions[i - 1][j].rotate(); + this.entityFunctions[i][j] = this.entityFunctions[i - 1][j].rotate(Rotation.WEST); } this.particleFunctions[i] = new BO3ParticleFunction[this.particleFunctions[i - 1].length]; for (int j = 0; j < this.particleFunctions[i].length; j++) { - this.particleFunctions[i][j] = this.particleFunctions[i - 1][j].rotate(); + this.particleFunctions[i][j] = this.particleFunctions[i - 1][j].rotate(Rotation.WEST); } this.spawnerFunctions[i] = new BO3SpawnerFunction[this.spawnerFunctions[i - 1].length]; for (int j = 0; j < this.spawnerFunctions[i].length; j++) { - this.spawnerFunctions[i][j] = this.spawnerFunctions[i - 1][j].rotate(); + this.spawnerFunctions[i][j] = this.spawnerFunctions[i - 1][j].rotate(Rotation.WEST); } this.modDataFunctions[i] = new BO3ModDataFunction[this.modDataFunctions[i - 1].length]; for (int j = 0; j < this.modDataFunctions[i].length; j++) { - this.modDataFunctions[i][j] = this.modDataFunctions[i - 1][j].rotate(); + this.modDataFunctions[i][j] = this.modDataFunctions[i - 1][j].rotate(Rotation.WEST); } } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Loader.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Loader.java index 659a8bd01..bbeff7261 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Loader.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/BO3Loader.java @@ -1,5 +1,7 @@ package com.pg85.otg.customobjects.bo3; +import java.io.File; + import com.pg85.otg.OTG; import com.pg85.otg.configuration.customobjects.CustomObjectResourcesManager; import com.pg85.otg.customobjects.CustomObject; @@ -18,22 +20,9 @@ import com.pg85.otg.customobjects.bo3.checks.LightCheck; import com.pg85.otg.customobjects.bo3.checks.ModCheck; import com.pg85.otg.customobjects.bo3.checks.ModCheckNot; -import com.pg85.otg.logging.LogMarker; -import com.pg85.otg.util.bo3.NamedBinaryTag; - -import java.io.*; -import java.util.HashMap; -import java.util.Map; public class BO3Loader implements CustomObjectLoader { - - // TOOD: Update this - /** A list of already loaded meta Tags. The path is the key, a NBT Tag is - * the value. - */ - private static Map LoadedTags = new HashMap(); - public BO3Loader() { // Register BO3 ConfigFunctions @@ -71,143 +60,12 @@ public BO3Loader() @Override public CustomObject loadFromFile(String objectName, File file) { - return new BO3(objectName, file); - } - - public static NamedBinaryTag loadMetadata(String name, File bo3Folder) - { - String path = bo3Folder.getParent() + File.separator + name; - - if (LoadedTags.containsKey(path)) - { - // Found a cached one - return LoadedTags.get(path); - } - - NamedBinaryTag tag = loadTileEntityFromNBT(path); - registerMetadata(path, tag); - return tag; - } - - private static NamedBinaryTag loadTileEntityFromNBT(String path) - { - // Load from file - NamedBinaryTag metadata; - FileInputStream stream = null; - try - { - // Read it from a file next to the BO3 - stream = new FileInputStream(path); - // Get the tag - metadata = NamedBinaryTag.readFrom(stream, true); - } catch (FileNotFoundException e) - { - // File not found - if(OTG.getPluginConfig().spawnLog) - { - OTG.log(LogMarker.WARN, "NBT file {} not found", (Object) path); - } - return null; - } catch (IOException e) - { - tryToClose(stream); - - // Not a compressed NBT file, try uncompressed - FileInputStream streamForUncompressed = null; - try - { - // Read it from a file next to the BO3 - streamForUncompressed = new FileInputStream(path); - // Get the tag - metadata = NamedBinaryTag.readFrom(streamForUncompressed, false); - } - catch (java.lang.ArrayIndexOutOfBoundsException corruptFile) - { - if(OTG.getPluginConfig().spawnLog) - { - OTG.log(LogMarker.ERROR, "Failed to read NBT meta file: ", e.getMessage()); - OTG.printStackTrace(LogMarker.ERROR, corruptFile); - } - return null; - } - catch (IOException corruptFile) - { - if(OTG.getPluginConfig().spawnLog) - { - OTG.log(LogMarker.ERROR, "Failed to read NBT meta file: ", e.getMessage()); - OTG.printStackTrace(LogMarker.ERROR, corruptFile); - } - return null; - } finally - { - tryToClose(streamForUncompressed); - } - } finally - { - tryToClose(stream); - } - - if(metadata != null) - { - // The file can be structured in two ways: - // 1. chest.nbt with all the contents directly in it - // 2. chest.nbt with a Compound tag in it with all the data - - // Check for type 1 by searching for an id tag - NamedBinaryTag idTag = metadata.getTag("id"); - if (idTag != null) - { - // Found id tag, so return the root tag - return metadata; - } - // No id tag found, so check for type 2 - if (metadata.getValue() instanceof NamedBinaryTag[]) - { - NamedBinaryTag[] subtag = (NamedBinaryTag[]) metadata.getValue(); - if (subtag.length != 0) - { - return subtag[0]; - } - } - } - // Unknown/bad structure - OTG.log(LogMarker.WARN, "Structure of NBT file is incorrect: " + path); - return null; - } - - /** - * Caches and returns the provided Meta data - * @param pathOnDisk The path of the meta data - * @param metadata The Tag object to be cached - * @return the meta data that was cached - */ - private static NamedBinaryTag registerMetadata(String pathOnDisk, NamedBinaryTag metadata) - { - // Add it to the cache - LoadedTags.put(pathOnDisk, metadata); - // Return it - return metadata; - } - - private static void tryToClose(InputStream stream) - { - if (stream != null) - { - try - { - stream.close(); - } catch (IOException ignored) - { - // Ignore - } - } + return new BO3(objectName, file); } @Override public void onShutdown() { - // Clean up the cache - LoadedTags.clear(); - } + } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/ObjectExtrusionHelper.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/ObjectExtrusionHelper.java index cc1cfe34c..dd22d8f3b 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/ObjectExtrusionHelper.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/ObjectExtrusionHelper.java @@ -59,16 +59,16 @@ void addBlock(BO3BlockFunction block) { if (extrudeMode != BO3Settings.ExtrudeMode.None) { - if (extrudeMode == BO3Settings.ExtrudeMode.BottomDown && block.y < blockExtrusionY) + if (extrudeMode == BO3Settings.ExtrudeMode.BottomDown && block.y() < blockExtrusionY) { blocksToExtrude.clear(); - blockExtrusionY = block.y; - } else if (extrudeMode == BO3Settings.ExtrudeMode.TopUp && block.y > blockExtrusionY) + blockExtrusionY = block.y(); + } else if (extrudeMode == BO3Settings.ExtrudeMode.TopUp && block.y() > blockExtrusionY) { blocksToExtrude.clear(); - blockExtrusionY = block.y; + blockExtrusionY = block.y(); } - if (block.y == blockExtrusionY) + if (block.y() == blockExtrusionY) { blocksToExtrude.add(block); } @@ -91,19 +91,19 @@ void extrude(LocalWorld world, Random random, int x, int y, int z, ChunkCoordina { if (extrudeMode == BO3Settings.ExtrudeMode.BottomDown) { - for (int yi = y + block.y - 1; - yi > extrudeMode.getEndingHeight() && extrudeThroughBlocks.contains(world.getMaterial(x + block.x, yi, z + block.z, chunkBeingPopulated)); + for (int yi = y + block.y() - 1; + yi > extrudeMode.getEndingHeight() && extrudeThroughBlocks.contains(world.getMaterial(x + block.x(), yi, z + block.z(), chunkBeingPopulated)); --yi) { - world.setBlock(x + block.x, yi, z + block.z, block.material, block.metaDataTag, chunkBeingPopulated, replaceBlock); + world.setBlock(x + block.x(), yi, z + block.z(), block.material(), block.tag(), chunkBeingPopulated, replaceBlock); } } else if (extrudeMode == BO3Settings.ExtrudeMode.TopUp) { - for (int yi = y + block.y + 1; - yi < extrudeMode.getEndingHeight() && extrudeThroughBlocks.contains(world.getMaterial(x + block.x, yi, z + block.z, chunkBeingPopulated)); + for (int yi = y + block.y() + 1; + yi < extrudeMode.getEndingHeight() && extrudeThroughBlocks.contains(world.getMaterial(x + block.x(), yi, z + block.z(), chunkBeingPopulated)); ++yi) { - world.setBlock(x + block.x, yi, z + block.z, block.material, block.metaDataTag, chunkBeingPopulated, replaceBlock); + world.setBlock(x + block.x(), yi, z + block.z(), block.material(), block.tag(), chunkBeingPopulated, replaceBlock); } } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BlockFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BlockFunction.java index fe28f3b20..6e8f65932 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BlockFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BlockFunction.java @@ -6,36 +6,49 @@ import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.BlockFunction; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.bo3.Rotation; /** * Represents a block in a BO3. */ public class BO3BlockFunction extends BlockFunction { - public BO3BlockFunction() { } - - public BO3BlockFunction(BO3Config holder) + public BO3BlockFunction rotate(Rotation rotation) { - this.holder = holder; - } - - public BO3BlockFunction rotate() - { - BO3BlockFunction rotatedBlock = new BO3BlockFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; - rotatedBlock.material = material.rotate(); - rotatedBlock.metaDataTag = metaDataTag; - rotatedBlock.metaDataName = metaDataName; + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3BlockFunction rotatedBlock = new BO3BlockFunction(); + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); + rotatedBlock.blockContainer = blockContainer.rotate(rotation); return rotatedBlock; } @Override public void spawn(LocalWorld world, Random random, int x, int y, int z, ChunkCoordinate chunkBeingPopulated, boolean replaceBlock) { - world.setBlock(x, y, z, material, metaDataTag, chunkBeingPopulated, replaceBlock); + world.setBlock(x, y, z, material(), tag(), chunkBeingPopulated, replaceBlock); } @Override diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BranchFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BranchFunction.java index 09662adce..f65aeef7d 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BranchFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3BranchFunction.java @@ -17,18 +17,40 @@ */ public class BO3BranchFunction extends BranchFunction { - public BO3BranchFunction rotate() + public BO3BranchFunction rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3BranchFunction rotatedBranch = new BO3BranchFunction(); - rotatedBranch.x = z; - rotatedBranch.y = y; - rotatedBranch.z = -x; + rotatedBranch.x(rotatedX); + rotatedBranch.y(y()); + rotatedBranch.z(rotatedZ); rotatedBranch.branches = new TreeSet(); rotatedBranch.totalChance = totalChance; rotatedBranch.totalChanceSet = totalChanceSet; for (BranchNode holder : this.branches) { - rotatedBranch.branches.add(new BranchNode(holder.getRotation().next(), holder.getChance(), holder.getCustomObject(false, null), holder.customObjectName)); + rotatedBranch.branches.add(new BranchNode(holder.getRotation().next(rotation), holder.getChance(), holder.getCustomObject(false, null), holder.customObjectName)); } return rotatedBranch; } @@ -39,9 +61,7 @@ protected double readArgs(List args, boolean accumulateChances) throws I { double cumulativeChance = 0; assureSize(6, args); - x = readInt(args.get(0), -32, 32); - y = readInt(args.get(1), -64, 64); - z = readInt(args.get(2), -32, 32); + readXYZ(args, 0); int i; for (i = 3; i < args.size() - 2; i += 3) { @@ -78,7 +98,7 @@ public CustomStructureCoordinate toCustomObjectCoordinate(LocalWorld world, Rand double randomChance = random.nextDouble() * totalChance; if (randomChance < branch.getChance()) { - return new BO3CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, branch.getRotation(), x + this.x, (short)(y + this.y), z + this.z); + return new BO3CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, branch.getRotation(), x + this.x(), (short)(y + this.y()), z + this.z()); } } return null; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3EntityFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3EntityFunction.java index ec97ee06f..d80c06ed1 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3EntityFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3EntityFunction.java @@ -2,25 +2,48 @@ import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.EntityFunction; +import com.pg85.otg.util.bo3.Rotation; /** * Represents an entity in a BO3. */ public class BO3EntityFunction extends EntityFunction { - public BO3EntityFunction rotate() + public BO3EntityFunction rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3EntityFunction rotatedBlock = new BO3EntityFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); rotatedBlock.name = name; rotatedBlock.resourceLocation = resourceLocation; rotatedBlock.groupSize = groupSize; rotatedBlock.originalNameTagOrNBTFileName = originalNameTagOrNBTFileName; rotatedBlock.nameTagOrNBTFileName = nameTagOrNBTFileName; rotatedBlock.namedBinaryTag = namedBinaryTag; - rotatedBlock.rotation = (rotation + 1) % 4; + rotatedBlock.rotation = (this.rotation + rotation.getRotationId()) % 4; return rotatedBlock; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3MinecraftObjectFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3MinecraftObjectFunction.java index 2ee364076..11fe52339 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3MinecraftObjectFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3MinecraftObjectFunction.java @@ -2,19 +2,42 @@ import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.MinecraftObjectFunction; +import com.pg85.otg.util.bo3.Rotation; /** * Represents a block in a BO3. */ public class BO3MinecraftObjectFunction extends MinecraftObjectFunction { - public BO3MinecraftObjectFunction rotate() + public BO3MinecraftObjectFunction rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3MinecraftObjectFunction rotatedBlock = new BO3MinecraftObjectFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; - rotatedBlock.rotation = rotation.next(); + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); + rotatedBlock.rotation = this.rotation.next(rotation); return rotatedBlock; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ModDataFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ModDataFunction.java index 7e5092583..07a22ce8f 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ModDataFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ModDataFunction.java @@ -1,19 +1,59 @@ package com.pg85.otg.customobjects.bo3.bo3function; +import java.io.DataInput; +import java.io.IOException; + import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.ModDataFunction; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents a block in a BO3. */ public class BO3ModDataFunction extends ModDataFunction { - public BO3ModDataFunction rotate() + public static BO3ModDataFunction read(DataInput in) throws IOException { + BO3ModDataFunction modDataFunction = new BO3ModDataFunction(); + + modDataFunction.x(in.readInt()); + modDataFunction.y(in.readInt()); + modDataFunction.z(in.readInt()); + modDataFunction.modId = StreamHelper.readStringFromStream(in); + modDataFunction.modData = StreamHelper.readStringFromStream(in); + + return modDataFunction; + } + + public BO3ModDataFunction rotate(Rotation rotation) + { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3ModDataFunction rotatedBlock = new BO3ModDataFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); rotatedBlock.modId = modId; rotatedBlock.modData = modData; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ParticleFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ParticleFunction.java index 907785f47..7a2fc59f7 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ParticleFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3ParticleFunction.java @@ -1,19 +1,65 @@ package com.pg85.otg.customobjects.bo3.bo3function; +import java.io.DataInput; +import java.io.IOException; + import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.ParticleFunction; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents a block in a BO3. */ public class BO3ParticleFunction extends ParticleFunction { - public BO3ParticleFunction rotate() + public static BO3ParticleFunction read(DataInput in) throws IOException + { + BO3ParticleFunction particleFunction = new BO3ParticleFunction(); + + particleFunction.x(in.readInt()); + particleFunction.y(in.readInt()); + particleFunction.z(in.readInt()); + particleFunction.particleName = StreamHelper.readStringFromStream(in); + particleFunction.interval = in.readDouble(); + particleFunction.velocityX = in.readDouble(); + particleFunction.velocityY = in.readDouble(); + particleFunction.velocityZ = in.readDouble(); + particleFunction.velocityXSet = in.readByte() != 0; + particleFunction.velocityYSet = in.readByte() != 0; + particleFunction.velocityZSet = in.readByte() != 0; + + return particleFunction; + } + + public BO3ParticleFunction rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3ParticleFunction rotatedBlock = new BO3ParticleFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); rotatedBlock.particleName = particleName; rotatedBlock.interval = interval; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3RandomBlockFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3RandomBlockFunction.java index 41fe665c0..8745dd254 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3RandomBlockFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3RandomBlockFunction.java @@ -3,146 +3,136 @@ import java.util.List; import java.util.Random; +import com.pg85.otg.common.BlockContainer; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; import com.pg85.otg.customobjects.bo3.BO3Config; -import com.pg85.otg.customobjects.bo3.BO3Loader; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.bo3.NamedBinaryTag; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.helpers.StringHelper; import com.pg85.otg.util.materials.MaterialHelper; public class BO3RandomBlockFunction extends BO3BlockFunction { - public LocalMaterialData[] blocks; + public BlockContainer[] blockContainers; public byte[] blockChances; - public String[] metaDataNames; - public NamedBinaryTag[] metaDataTags; public byte blockCount = 0; - public BO3RandomBlockFunction() { } - - public BO3RandomBlockFunction(BO3Config holder) - { - super(holder); - } - - public BO3RandomBlockFunction rotate() + public BO3RandomBlockFunction rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3RandomBlockFunction rotatedBlock = new BO3RandomBlockFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); + rotatedBlock.blockContainer = blockContainer.rotate(rotation); rotatedBlock.blockCount = blockCount; - rotatedBlock.blocks = new LocalMaterialData[blockCount]; - for (int i = 0; i < blockCount; i++) + rotatedBlock.blockContainers = new BlockContainer[blockCount]; + for(int i = 0; i < blockCount; i++) { - rotatedBlock.blocks[i] = blocks[i].rotate(); + rotatedBlock.blockContainers[i] = blockContainers[i].rotate(rotation); } rotatedBlock.blockChances = blockChances; - rotatedBlock.metaDataTags = metaDataTags; - rotatedBlock.metaDataNames = metaDataNames; - return rotatedBlock; } @Override - public void load(List args) throws InvalidConfigException + public void load(BO3Config holder, List args) throws InvalidConfigException { assureSize(5, args); - x = readInt(args.get(0), -100, 100); - y = (short) readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); // Now read the random parts int i = 3; int size = args.size(); // Get number of blocks first, params can vary so can't just count. - while (i < size) - { - i++; - if (i >= size) + while(i < size) + { + i++; + if(i >= size) { throw new InvalidConfigException("Missing chance parameter"); } - try - { - readInt(args.get(i), 1, 100); - } - catch (InvalidConfigException e) + if(!StringHelper.isNumber(args.get(i))) { - // Get the chance i++; - if (i >= size) + if(i >= size) { throw new InvalidConfigException("Missing chance parameter"); } - readInt(args.get(i), 1, 100); + if(!StringHelper.isNumber(args.get(i))) + { + throw new NumberFormatException("'" + args.get(i) + "'" + " is not a number!"); + } } i++; blockCount++; } - this.blocks = new LocalMaterialData[blockCount]; + this.blockContainers = new BlockContainer[blockCount]; this.blockChances = new byte[blockCount]; - this.metaDataNames = new String[blockCount]; - this.metaDataTags = new NamedBinaryTag[blockCount]; i = 3; blockCount = 0; - while (i < size) + while(i < size) { // Parse chance and metadata - this.blocks[blockCount] = MaterialHelper.readMaterial(args.get(i)); + LocalMaterialData material = MaterialHelper.readMaterial(args.get(i)); i++; - if (i >= size) - { - throw new InvalidConfigException("Missing chance parameter"); - } - try + if(StringHelper.isNumber(args.get(i))) { + blockContainers[blockCount] = material.blockContainer(); blockChances[blockCount] = (byte) readInt(args.get(i), 1, 100); + i++; } - catch (InvalidConfigException e) + else { - // Maybe it's a NBT file? - - // Get the file - NamedBinaryTag metaData = BO3Loader.loadMetadata(args.get(i), this.getHolder().getFile()); - if (metaData != null) - { - metaDataNames[blockCount] = args.get(i); - metaDataTags[blockCount] = metaData; - } - - // Get the chance + blockContainers[blockCount] = material.blockContainer(holder.getFile().getParentFile().toPath(), args.get(i)); i++; - if (i >= size) - { - throw new InvalidConfigException("Missing chance parameter"); - } blockChances[blockCount] = (byte) readInt(args.get(i), 1, 100); + i++; } - - i++; blockCount++; } + blockContainer = blockContainers.length > 0 ? blockContainers[0] : BlockContainer.of(MaterialHelper.AIR); } @Override public String makeString() { - String text = "RandomBlock(" + x + "," + y + "," + z; + String text = "RandomBlock(" + x() + "," + y() + "," + z(); for (int i = 0; i < blockCount; i++) { - if (metaDataTags[i] == null) + if (!blockContainers[i].hasTag()) { - text += "," + blocks[i] + "," + blockChances[i]; + text += "," + blockContainers[i].material() + "," + blockChances[i]; } else { - text += "," + blocks[i] + "," + metaDataNames[i] + "," + blockChances[i]; + text += "," + blockContainers[i].material() + "," + blockContainers[i].tagPath() + "," + blockChances[i]; } } return text + ")"; @@ -155,7 +145,7 @@ public void spawn(LocalWorld world, Random random, int x, int y, int z, ChunkCoo { if (random.nextInt(100) < blockChances[i]) { - world.setBlock(x, y, z, blocks[i], metaDataTags[i], chunkBeingPopulated, replaceBlock); + world.setBlock(x, y, z, blockContainers[i].material(), blockContainers[i].tag(), chunkBeingPopulated, replaceBlock); break; } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3SpawnerFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3SpawnerFunction.java index d60910f89..4b0515540 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3SpawnerFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3SpawnerFunction.java @@ -1,19 +1,73 @@ package com.pg85.otg.customobjects.bo3.bo3function; +import java.io.DataInput; +import java.io.IOException; + import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.SpawnerFunction; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents a block in a BO3. */ public class BO3SpawnerFunction extends SpawnerFunction { - public BO3SpawnerFunction rotate() + public static BO3SpawnerFunction read(DataInput in) throws IOException { + BO3SpawnerFunction spawnerFunction = new BO3SpawnerFunction(); + + spawnerFunction.x(in.readInt()); + spawnerFunction.y(in.readInt()); + spawnerFunction.z(in.readInt()); + spawnerFunction.mobName = StreamHelper.readStringFromStream(in); + spawnerFunction.originalnbtFileName = StreamHelper.readStringFromStream(in); + spawnerFunction.nbtFileName = StreamHelper.readStringFromStream(in); + spawnerFunction.groupSize = in.readInt(); + spawnerFunction.interval = in.readInt(); + spawnerFunction.spawnChance = in.readInt(); + spawnerFunction.maxCount = in.readInt(); + spawnerFunction.despawnTime = in.readInt(); + spawnerFunction.velocityX = in.readDouble(); + spawnerFunction.velocityY = in.readDouble(); + spawnerFunction.velocityZ = in.readDouble(); + spawnerFunction.velocityXSet = in.readByte() != 0; + spawnerFunction.velocityYSet = in.readByte() != 0; + spawnerFunction.velocityZSet = in.readByte() != 0; + spawnerFunction.yaw = in.readFloat(); + spawnerFunction.pitch = in.readFloat(); + + return spawnerFunction; + } + + public BO3SpawnerFunction rotate(Rotation rotation) + { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } + BO3SpawnerFunction rotatedBlock = new BO3SpawnerFunction(); - rotatedBlock.x = z; - rotatedBlock.y = y; - rotatedBlock.z = -x; + rotatedBlock.x(rotatedX); + rotatedBlock.y(y()); + rotatedBlock.z(rotatedZ); rotatedBlock.mobName = mobName; rotatedBlock.originalnbtFileName = originalnbtFileName; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3WeightedBranchFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3WeightedBranchFunction.java index 83d2b07bb..a8785e4b1 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3WeightedBranchFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/bo3function/BO3WeightedBranchFunction.java @@ -1,6 +1,7 @@ package com.pg85.otg.customobjects.bo3.bo3function; import com.pg85.otg.common.LocalWorld; +import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.customobjects.bofunctions.BranchNode; import com.pg85.otg.customobjects.structures.CustomStructureCoordinate; import com.pg85.otg.customobjects.structures.bo3.BO3CustomStructureCoordinate; @@ -16,7 +17,7 @@ public class BO3WeightedBranchFunction extends BO3BranchFunction private double cumulativeChance = 0; @Override - public void load(List args) throws InvalidConfigException + public void load(BO3Config holder, List args) throws InvalidConfigException { branches = new TreeSet(); cumulativeChance = readArgs(args, true); @@ -35,7 +36,7 @@ public CustomStructureCoordinate toCustomObjectCoordinate(LocalWorld world, Rand { if (branch.getChance() >= randomChance) { - return new BO3CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, branch.getRotation(), x + this.x, (short)(y + this.y), z + this.z); + return new BO3CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, branch.getRotation(), x + this.x(), (short)(y + this.y()), z + this.z()); } } return null; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BO3Check.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BO3Check.java index ed8a6dbd8..4b1be7384 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BO3Check.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BO3Check.java @@ -5,6 +5,7 @@ import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.bo3.Rotation; /** * Represents a check - something that can prevent the BO3 from spawning if this @@ -12,11 +13,6 @@ */ public abstract class BO3Check extends CustomObjectConfigFunction { - /** - * Y position relative to the object origin. - */ - public int y; - /** * Returns whether this check would prevent spawning at the given position. * The given x, y and z positions are simply the relative coords in this @@ -43,8 +39,8 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } BO3Check check = (BO3Check) other; - return check.x == x && check.y == y && check.z == z; + return check.x() == x() && check.y() == y() && check.z() == z(); } - public abstract BO3Check rotate(); + public abstract BO3Check rotate(Rotation rotation); } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheck.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheck.java index 78b091e36..66304af23 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheck.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheck.java @@ -5,6 +5,7 @@ import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.materials.MaterialSet; import java.util.List; @@ -21,12 +22,10 @@ public boolean preventsSpawn(LocalWorld world, int x, int y, int z, ChunkCoordin } @Override - public void load(List args) throws InvalidConfigException + public void load(BO3Config holder, List args) throws InvalidConfigException { assureSize(4, args); - x = readInt(args.get(0), -100, 100); - y = readInt(args.get(1), -100, 100); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); toCheck = readMaterials(args, 3); } @@ -44,17 +43,38 @@ public String makeString() */ protected String makeString(String name) { - return name + '(' + x + ',' + y + ',' + z + makeMaterials(toCheck) + ')'; + return name + '(' + x() + ',' + y() + ',' + z() + makeMaterials(toCheck) + ')'; } @Override - public BO3Check rotate() + public BO3Check rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } BlockCheck rotatedCheck = new BlockCheck(); - rotatedCheck.x = z; - rotatedCheck.y = y; - rotatedCheck.z = -x; - rotatedCheck.toCheck = this.toCheck.rotate(); + rotatedCheck.x(rotatedX); + rotatedCheck.y(y()); + rotatedCheck.z(rotatedZ); + rotatedCheck.toCheck = this.toCheck.rotate(rotation); return rotatedCheck; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheckNot.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheckNot.java index 7d3ea2f78..98de5ac99 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheckNot.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/BlockCheckNot.java @@ -2,6 +2,7 @@ import com.pg85.otg.common.LocalWorld; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.bo3.Rotation; public final class BlockCheckNot extends BlockCheck { @@ -19,13 +20,34 @@ public String makeString() } @Override - public BO3Check rotate() + public BO3Check rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } BlockCheckNot rotatedCheck = new BlockCheckNot(); - rotatedCheck.x = z; - rotatedCheck.y = y; - rotatedCheck.z = -x; - rotatedCheck.toCheck = toCheck.rotate(); + rotatedCheck.x(rotatedX); + rotatedCheck.y(y()); + rotatedCheck.z(rotatedZ); + rotatedCheck.toCheck = toCheck.rotate(rotation); return rotatedCheck; } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/LightCheck.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/LightCheck.java index aa3ff9acd..e3ed0c947 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/LightCheck.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/LightCheck.java @@ -4,6 +4,7 @@ import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.bo3.Rotation; import java.util.List; @@ -36,12 +37,10 @@ public boolean preventsSpawn(LocalWorld world, int x, int y, int z, ChunkCoordin } @Override - public void load(List args) throws InvalidConfigException + public void load(BO3Config holder, List args) throws InvalidConfigException { assureSize(5, args); - x = readInt(args.get(0), -100, 100); - y = readInt(args.get(1), -100, 100); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); minLightLevel = readInt(args.get(3), 0, 16); maxLightLevel = readInt(args.get(4), minLightLevel, 16); } @@ -49,16 +48,37 @@ public void load(List args) throws InvalidConfigException @Override public String makeString() { - return "LightCheck(" + x + ',' + y + ',' + z + ',' + minLightLevel + ',' + maxLightLevel + ')'; + return "LightCheck(" + x() + ',' + y() + ',' + z() + ',' + minLightLevel + ',' + maxLightLevel + ')'; } @Override - public BO3Check rotate() + public BO3Check rotate(Rotation rotation) { + int rotatedX; + int rotatedZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedX = z(); + rotatedZ = -x(); + break; + case SOUTH: + rotatedX = -x(); + rotatedZ = -z(); + break; + case EAST: + rotatedX = -z(); + rotatedZ = x(); + break; + default: + throw new IllegalArgumentException(); + } LightCheck rotatedCheck = new LightCheck(); - rotatedCheck.x = z; - rotatedCheck.y = y; - rotatedCheck.z = -x; + rotatedCheck.x(rotatedX); + rotatedCheck.y(y()); + rotatedCheck.z(rotatedZ); rotatedCheck.minLightLevel = minLightLevel; rotatedCheck.maxLightLevel = maxLightLevel; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/ModCheck.java b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/ModCheck.java index da12827ea..f7578706b 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/ModCheck.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo3/checks/ModCheck.java @@ -7,6 +7,7 @@ import com.pg85.otg.customobjects.bo3.BO3Config; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.bo3.Rotation; public class ModCheck extends BO3Check { @@ -19,13 +20,13 @@ public boolean preventsSpawn(LocalWorld world, int x, int y, int z, ChunkCoordin } @Override - public BO3Check rotate() + public BO3Check rotate(Rotation rotation) { return this; } @Override - protected void load(List args) throws InvalidConfigException + protected void load(BO3Config holder, List args) throws InvalidConfigException { assureSize(1, args); mods = new String[args.size()]; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4.java index fb32493ab..9eeb08ab4 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4.java @@ -236,9 +236,9 @@ public boolean trySpawnAt(LocalWorld world, Random random, Rotation rotation, Ch boolean followGround = false, followStone = false, followSurface = false; if (replaceBelowMaterial != null) { - if (replaceBelowMaterial.equals(bo3GroundBlock)) followGround = true; - else if (replaceBelowMaterial.equals(bo3StoneBlock)) followStone = true; - else if (replaceBelowMaterial.equals(bo3SurfaceBlock)) followSurface = true; + if (replaceBelowMaterial.matches(bo3GroundBlock)) followGround = true; + else if (replaceBelowMaterial.matches(bo3StoneBlock)) followStone = true; + else if (replaceBelowMaterial.matches(bo3SurfaceBlock)) followSurface = true; } // Get the right coordinates based on rotation @@ -246,7 +246,7 @@ public boolean trySpawnAt(LocalWorld world, Random random, Rotation rotation, Ch ArrayList coordsAboveDone = new ArrayList(); ArrayList coordsBelowDone = new ArrayList(); - BO4BlockFunction blockToQueueForSpawn = new BO4BlockFunction(); + BO4BlockContainer blockToQueueForSpawn = new BO4BlockContainer(); LocalMaterialData sourceBlockMaterial; boolean outOfBounds = false; @@ -254,7 +254,6 @@ public boolean trySpawnAt(LocalWorld world, Random random, Rotation rotation, Ch LocalMaterialData blockAbove; BO4RandomBlockFunction randomBlockFunction; BO4BlockFunction newBlock; - int rotations; boolean bFound; int blockY; int highestBlockToReplace; @@ -264,81 +263,57 @@ public boolean trySpawnAt(LocalWorld world, Random random, Rotation rotation, Ch BO4BlockFunction[] blocks = config.getBlocks(); if(blocks != null) { - for (BO4BlockFunction block : config.getBlocks()) + Rotation blockRotation; + switch(rotation) + { + case NORTH: + blockRotation = Rotation.NORTH; + break; + case WEST: + blockRotation = Rotation.EAST; + break; + case SOUTH: + blockRotation = Rotation.SOUTH; + break; + case EAST: + blockRotation = Rotation.WEST; + break; + default: + throw new IllegalArgumentException(); + } + for (BO4BlockFunction block : blocks) { if(block instanceof BO4RandomBlockFunction) { randomBlockFunction = ((BO4RandomBlockFunction)block); + boolean blockSelected = false; for (int i = 0; i < randomBlockFunction.blockCount; i++) { if (random.nextInt(100) < randomBlockFunction.blockChances[i]) { - block.metaDataName = randomBlockFunction.metaDataNames[i]; - block.metaDataTag = randomBlockFunction.metaDataTags[i]; - block.material = randomBlockFunction.blocks[i]; + block.blockContainer = randomBlockFunction.blockContainers[i]; + blockSelected = true; break; } } + if(!blockSelected) + { + continue; + } } - if(block.material == null) + if(block.material() == null) { continue; } - if(rotation != Rotation.NORTH) + if(blockRotation != Rotation.NORTH) { - newBlock = new BO4BlockFunction(); - rotations = 0; - // How many counter-clockwise rotations have to be applied? - if(rotation == Rotation.WEST) - { - rotations = 1; - } - else if(rotation == Rotation.SOUTH) - { - rotations = 2; - } - else if(rotation == Rotation.EAST) - { - rotations = 3; - } - - // Apply rotation - if(rotations == 0) - { - newBlock.x = block.x; - newBlock.z = block.z; - } - if(rotations == 1) - { - newBlock.x = block.z; - newBlock.z = -block.x + 15; - newBlock.material = block.material.rotate(); - } - if(rotations == 2) - { - newBlock.x = -block.x + 15; - newBlock.z = -block.z + 15; - newBlock.material = block.material.rotate(); - newBlock.material = newBlock.material.rotate(); - } - if(rotations == 3) - { - newBlock.x = -block.z + 15; - newBlock.z = block.x; - newBlock.material = block.material.rotate(); - newBlock.material = newBlock.material.rotate(); - newBlock.material = newBlock.material.rotate(); - } - newBlock.y = block.y; - - newBlock.metaDataName = block.metaDataName; - newBlock.metaDataTag = block.metaDataTag; + newBlock = block.rotate(blockRotation); if(isOnBiomeBorder) { - biome = world.getBiomeForPopulation(x + newBlock.x, z + newBlock.z, chunkBeingPopulated); + biome = world.getBiomeForPopulation(x + newBlock.x(), z + newBlock.z(), chunkBeingPopulated); biomeConfig = biome.getBiomeConfig(); } @@ -349,7 +324,7 @@ else if(rotation == Rotation.EAST) bFound = false; for(Object[] coords : coordsAboveDone) { - if((Integer)coords[0] == x + newBlock.x && (Integer)coords[1] == z + newBlock.z) + if((Integer)coords[0] == x + newBlock.x() && (Integer)coords[1] == z + newBlock.z()) { bFound = true; break; @@ -358,34 +333,31 @@ else if(rotation == Rotation.EAST) if(!bFound) { - coordsAboveDone.add(new Object[] { x + newBlock.x, z + newBlock.z }); - blockY = y + newBlock.y + 1; // TODO: This is wrong, should be the lowest block in the BO4 at these x-z coordinates. ReplaceAbove should be done before any blocks in this column are placed - highestBlockToReplace = world.getHighestBlockYAt(x + newBlock.x, z + newBlock.z, true, true, false, false, true, chunkBeingPopulated); + coordsAboveDone.add(new Object[] { x + newBlock.x(), z + newBlock.z() }); + blockY = y + newBlock.y() + 1; // TODO: This is wrong, should be the lowest block in the BO4 at these x-z coordinates. ReplaceAbove should be done before any blocks in this column are placed + highestBlockToReplace = world.getHighestBlockYAt(x + newBlock.x(), z + newBlock.z(), true, true, false, false, true, chunkBeingPopulated); - while(blockY <= highestBlockToReplace && blockY > y + newBlock.y) + while(blockY <= highestBlockToReplace && blockY > y + newBlock.y()) { - blockToQueueForSpawn = new BO4BlockFunction(); + blockToQueueForSpawn = new BO4BlockContainer(); // TODO: Make override leaves and air configurable // TODO: Make replaceAbove height configurable - blockToQueueForSpawn.x = x + newBlock.x; - blockToQueueForSpawn.y = (short) blockY; - blockToQueueForSpawn.z = z + newBlock.z; + blockToQueueForSpawn.x(x + newBlock.x()); + blockToQueueForSpawn.y((short) blockY); + blockToQueueForSpawn.z(z + newBlock.z()); - blockToQueueForSpawn.metaDataName = newBlock.metaDataName; - blockToQueueForSpawn.metaDataTag = newBlock.metaDataTag; - - destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x, blockToQueueForSpawn.z); + destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x(), blockToQueueForSpawn.z()); if(chunkCoord.equals(destChunk)) { if(spawnUnderWater && blockY >= waterLevel) { - blockToQueueForSpawn.material = MaterialHelper.AIR; + blockToQueueForSpawn.blockContainer = MaterialHelper.AIR.blockContainer(); } else { // ReplaceAbove is not affected by sagc - blockToQueueForSpawn.material = replaceAboveMaterial; + blockToQueueForSpawn.blockContainer = replaceAboveMaterial.blockContainer(); } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); } else { outOfBounds = true; } @@ -395,12 +367,12 @@ else if(rotation == Rotation.EAST) } } - if(replaceBelowMaterial != null && newBlock.y == 0 && !newBlock.material.isEmptyOrAir() && doReplaceAboveBelowOnly) + if(replaceBelowMaterial != null && newBlock.y() == 0 && !newBlock.material().isEmpty() && doReplaceAboveBelowOnly) { bFound = false; for(Object[] coords : coordsBelowDone) { - if((Integer)coords[0] == x + newBlock.x && (Integer)coords[1] == z + newBlock.z) + if((Integer)coords[0] == x + newBlock.x() && (Integer)coords[1] == z + newBlock.z()) { bFound = true; break; @@ -409,8 +381,8 @@ else if(rotation == Rotation.EAST) if(!bFound) { - coordsBelowDone.add(new Object[] { x + newBlock.x, z + newBlock.z }); - blockY = y + newBlock.y - 1; + coordsBelowDone.add(new Object[] { x + newBlock.x(), z + newBlock.z() }); + blockY = y + newBlock.y() - 1; // TODO: Make override leaves and air configurable // TODO: Make replaceBelow height configurable @@ -418,44 +390,42 @@ else if(rotation == Rotation.EAST) { if(blockY < PluginStandardValues.WORLD_HEIGHT) { - sourceBlockMaterial = world.getMaterial(x + newBlock.x, blockY, z + newBlock.z, chunkBeingPopulated); + sourceBlockMaterial = world.getMaterial(x + newBlock.x(), blockY, z + newBlock.z(), chunkBeingPopulated); if(sourceBlockMaterial != null) { if(!sourceBlockMaterial.isSolid()) { - blockToQueueForSpawn = new BO4BlockFunction(); - blockToQueueForSpawn.x = x + newBlock.x; - blockToQueueForSpawn.y = (short) blockY; - blockToQueueForSpawn.z = z + newBlock.z; - blockToQueueForSpawn.material = replaceBelowMaterial; - blockToQueueForSpawn.metaDataName = newBlock.metaDataName; - blockToQueueForSpawn.metaDataTag = newBlock.metaDataTag; + blockToQueueForSpawn = new BO4BlockContainer(); + blockToQueueForSpawn.x(x + newBlock.x()); + blockToQueueForSpawn.y((short) blockY); + blockToQueueForSpawn.z(z + newBlock.z()); + blockToQueueForSpawn.blockContainer = replaceBelowMaterial.blockContainer(); - destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x, blockToQueueForSpawn.z); + destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x(), blockToQueueForSpawn.z()); if(chunkCoord.equals(destChunk)) { // Apply sagc'd biome blocks, or replaced stone block, or replaced replaceBelowMaterial if(replaceWithBiomeBlocks) { if (followGround) - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight - (world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight + (world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); else if (followStone) - blockToQueueForSpawn.material = biomeConfig.getStoneBlockReplaced(world, y); + blockToQueueForSpawn.blockContainer = biomeConfig.getStoneBlockReplaced(world, y).blockContainer(); else if (followSurface) - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight - (world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight + (world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); else - blockToQueueForSpawn.material = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y) : replaceBelowMaterial; + blockToQueueForSpawn.blockContainer = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y()).blockContainer() : replaceBelowMaterial.blockContainer(); } else { - blockToQueueForSpawn.material = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y) : replaceBelowMaterial; - if(blockToQueueForSpawn.material == null) + blockToQueueForSpawn.blockContainer = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y()).blockContainer() : replaceBelowMaterial.blockContainer(); + if(blockToQueueForSpawn.material() == null) { - blockToQueueForSpawn.material = MaterialHelper.DIRT; + blockToQueueForSpawn.blockContainer = MaterialHelper.DIRT.blockContainer(); } } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); } else { outOfBounds = true; } @@ -472,54 +442,51 @@ else if(sourceBlockMaterial.isSolid()) } } - if(y + newBlock.y > 0 && y + newBlock.y < 256 && !doReplaceAboveBelowOnly) + if(y + newBlock.y() > 0 && y + newBlock.y() < 256 && !doReplaceAboveBelowOnly) { - blockToQueueForSpawn = new BO4BlockFunction(); - blockToQueueForSpawn.x = x + newBlock.x; - blockToQueueForSpawn.y = (short) (y + newBlock.y); - blockToQueueForSpawn.z = z + newBlock.z; - blockToQueueForSpawn.material = newBlock.material; - - blockToQueueForSpawn.metaDataName = newBlock.metaDataName; - blockToQueueForSpawn.metaDataTag = newBlock.metaDataTag; + blockToQueueForSpawn = new BO4BlockContainer(); + blockToQueueForSpawn.x(x + newBlock.x()); + blockToQueueForSpawn.y((short) (y + newBlock.y())); + blockToQueueForSpawn.z(z + newBlock.z()); + blockToQueueForSpawn.blockContainer = newBlock.blockContainer; - destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x, blockToQueueForSpawn.z); + destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x(), blockToQueueForSpawn.z()); if(chunkCoord.equals(destChunk)) { if(replaceWithBiomeBlocks) { - if(blockToQueueForSpawn.material.equals(bo3GroundBlock)) + if(blockToQueueForSpawn.material().equals(bo3GroundBlock)) { - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); continue; } - else if(blockToQueueForSpawn.material.equals(bo3StoneBlock)) + else if(blockToQueueForSpawn.material().equals(bo3StoneBlock)) { - blockToQueueForSpawn.material = biomeConfig.getStoneBlockReplaced(world, blockToQueueForSpawn.y); - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + blockToQueueForSpawn.blockContainer = biomeConfig.getStoneBlockReplaced(world, blockToQueueForSpawn.y()).blockContainer(); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); continue; } - else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) + else if(blockToQueueForSpawn.material().equals(bo3SurfaceBlock)) { - blockAbove = world.getMaterial(blockToQueueForSpawn.x, blockToQueueForSpawn.y + 1, blockToQueueForSpawn.z, chunkBeingPopulated); + blockAbove = world.getMaterial(blockToQueueForSpawn.x(), blockToQueueForSpawn.y() + 1, blockToQueueForSpawn.z(), chunkBeingPopulated); if(blockAbove != null && (blockAbove.isSolid() || blockAbove.isLiquid())) { - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); } else { - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); } - if(blockToQueueForSpawn.material.isAir()) + if(blockToQueueForSpawn.material().isAir()) { - if(blockToQueueForSpawn.y < biomeConfig.waterLevelMax) + if(blockToQueueForSpawn.y() < biomeConfig.waterLevelMax) { - blockToQueueForSpawn.material = MaterialHelper.WATER; + blockToQueueForSpawn.blockContainer = MaterialHelper.WATER.blockContainer(); } else { - blockToQueueForSpawn.material = doBiomeConfigReplaceBlocks ? newBlock.material.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y) : newBlock.material; + blockToQueueForSpawn.blockContainer = doBiomeConfigReplaceBlocks ? newBlock.material().parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y()).blockContainer() : newBlock.blockContainer; } } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); continue; } } @@ -527,13 +494,13 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) // Don't spawn torches underwater if( spawnUnderWater && - blockToQueueForSpawn.material.isMaterial(DefaultMaterial.TORCH) && - world.getMaterial(blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, chunkBeingPopulated).isLiquid() + blockToQueueForSpawn.material().isMaterial(DefaultMaterial.TORCH) && + world.getMaterial(blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), chunkBeingPopulated).isLiquid() ) { continue; } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, doBiomeConfigReplaceBlocks); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, doBiomeConfigReplaceBlocks); } else { outOfBounds = true; } @@ -542,7 +509,7 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) if(isOnBiomeBorder) { - biome = world.getBiomeForPopulation(x + block.x, z + block.z, chunkBeingPopulated); + biome = world.getBiomeForPopulation(x + block.x(), z + block.z(), chunkBeingPopulated); biomeConfig = biome.getBiomeConfig(); } @@ -551,7 +518,7 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) bFound = false; for(Object[] coords : coordsAboveDone) { - if((Integer)coords[0] == x + block.x && (Integer)coords[1] == z + block.z) + if((Integer)coords[0] == x + block.x() && (Integer)coords[1] == z + block.z()) { bFound = true; break; @@ -560,33 +527,30 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) if(!bFound) { - coordsAboveDone.add(new Object[] { x + block.x, z + block.z }); - blockY = (y + block.y + 1); // TODO: This is wrong, should be the lowest block in the BO3 at these x-z coordinates. replaceAbove should be done before any blocks in this column are placed - highestBlockToReplace = world.getHighestBlockYAt(x + block.x, z + block.z, true, true, false, false, true, chunkBeingPopulated); + coordsAboveDone.add(new Object[] { x + block.x(), z + block.z() }); + blockY = (y + block.y() + 1); // TODO: This is wrong, should be the lowest block in the BO3 at these x-z coordinates. replaceAbove should be done before any blocks in this column are placed + highestBlockToReplace = world.getHighestBlockYAt(x + block.x(), z + block.z(), true, true, false, false, true, chunkBeingPopulated); - while(blockY <= highestBlockToReplace && blockY > y + block.y) + while(blockY <= highestBlockToReplace && blockY > y + block.y()) { - blockToQueueForSpawn = new BO4BlockFunction(); + blockToQueueForSpawn = new BO4BlockContainer(); if(spawnUnderWater && blockY >= waterLevel)// && replaceAboveMaterial.isLiquid()) { - blockToQueueForSpawn.material = MaterialHelper.AIR; + blockToQueueForSpawn.blockContainer = MaterialHelper.AIR.blockContainer(); } else { // ReplaceAbove is not affected by sagc - blockToQueueForSpawn.material = replaceAboveMaterial; + blockToQueueForSpawn.blockContainer = replaceAboveMaterial.blockContainer(); } - blockToQueueForSpawn.x = x + block.x; - blockToQueueForSpawn.y = (short)blockY; - blockToQueueForSpawn.z = z + block.z; + blockToQueueForSpawn.x(x + block.x()); + blockToQueueForSpawn.y((short)blockY); + blockToQueueForSpawn.z(z + block.z()); - blockToQueueForSpawn.metaDataName = block.metaDataName; - blockToQueueForSpawn.metaDataTag = block.metaDataTag; - - destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x, blockToQueueForSpawn.z); + destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x(), blockToQueueForSpawn.z()); if(chunkCoord.equals(destChunk)) { - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); } else { outOfBounds = true; } @@ -595,12 +559,12 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) } } - if(replaceBelowMaterial != null && block.y == 0 && !block.material.isEmptyOrAir() && doReplaceAboveBelowOnly) + if(replaceBelowMaterial != null && block.y() == 0 && !block.material().isEmpty() && doReplaceAboveBelowOnly) { bFound = false; for(Object[] coords : coordsBelowDone) { - if((Integer)coords[0] == x + block.x && (Integer)coords[1] == z + block.z) + if((Integer)coords[0] == x + block.x() && (Integer)coords[1] == z + block.z()) { bFound = true; break; @@ -609,8 +573,8 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) if(!bFound) { - coordsBelowDone.add(new Object[] { x + block.x, z + block.z }); - blockY = y + block.y - 1; + coordsBelowDone.add(new Object[] { x + block.x(), z + block.z() }); + blockY = y + block.y() - 1; // TODO: Make override leaves and air configurable // TODO: Make replaceBelow height configurable @@ -618,44 +582,42 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) { if(blockY < PluginStandardValues.WORLD_HEIGHT) { - sourceBlockMaterial = world.getMaterial(x + block.x, blockY, z + block.z, chunkBeingPopulated); + sourceBlockMaterial = world.getMaterial(x + block.x(), blockY, z + block.z(), chunkBeingPopulated); if(sourceBlockMaterial != null) { if(!sourceBlockMaterial.isSolid()) { - blockToQueueForSpawn = new BO4BlockFunction(); - blockToQueueForSpawn.x = x + block.x; - blockToQueueForSpawn.y = (short) blockY; - blockToQueueForSpawn.z = z + block.z; - blockToQueueForSpawn.material = replaceBelowMaterial; - blockToQueueForSpawn.metaDataName = block.metaDataName; - blockToQueueForSpawn.metaDataTag = block.metaDataTag; + blockToQueueForSpawn = new BO4BlockContainer(); + blockToQueueForSpawn.x(x + block.x()); + blockToQueueForSpawn.y((short) blockY); + blockToQueueForSpawn.z(z + block.z()); + blockToQueueForSpawn.blockContainer = replaceBelowMaterial.blockContainer(); - destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x, blockToQueueForSpawn.z); + destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x(), blockToQueueForSpawn.z()); if(chunkCoord.equals(destChunk)) { // Apply sagc'd biome blocks, or replaced stone block, or replaced replaceBelowMaterial if(replaceWithBiomeBlocks) { if (followGround) - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight - (world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight + (world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); else if (followStone) - blockToQueueForSpawn.material = biomeConfig.getStoneBlockReplaced(world, y); + blockToQueueForSpawn.blockContainer = biomeConfig.getStoneBlockReplaced(world, y).blockContainer(); else if (followSurface) - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight - (world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight + (world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); else - blockToQueueForSpawn.material = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y) : replaceBelowMaterial; + blockToQueueForSpawn.blockContainer = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y()).blockContainer() : replaceBelowMaterial.blockContainer(); } else { - blockToQueueForSpawn.material = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y) : replaceBelowMaterial; - if(blockToQueueForSpawn.material == null) + blockToQueueForSpawn.blockContainer = doBiomeConfigReplaceBlocks ? replaceBelowMaterial.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y()).blockContainer() : replaceBelowMaterial.blockContainer(); + if(blockToQueueForSpawn.material() == null) { - blockToQueueForSpawn.material = MaterialHelper.DIRT; + blockToQueueForSpawn.blockContainer = MaterialHelper.DIRT.blockContainer(); } } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); } else { outOfBounds = true; } @@ -672,54 +634,51 @@ else if(sourceBlockMaterial.isSolid()) } } - if(y + block.y > 0 && y + block.y < 256 && !doReplaceAboveBelowOnly) + if(y + block.y() > 0 && y + block.y() < 256 && !doReplaceAboveBelowOnly) { - blockToQueueForSpawn = new BO4BlockFunction(); - blockToQueueForSpawn.x = x + block.x; - blockToQueueForSpawn.y = (short) (y + block.y); - blockToQueueForSpawn.z = z + block.z; - blockToQueueForSpawn.material = block.material; - - blockToQueueForSpawn.metaDataName = block.metaDataName; - blockToQueueForSpawn.metaDataTag = block.metaDataTag; + blockToQueueForSpawn = new BO4BlockContainer(); + blockToQueueForSpawn.x(x + block.x()); + blockToQueueForSpawn.y((short) (y + block.y())); + blockToQueueForSpawn.z(z + block.z()); + blockToQueueForSpawn.blockContainer = block.blockContainer; - destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x, blockToQueueForSpawn.z); + destChunk = ChunkCoordinate.fromBlockCoords(blockToQueueForSpawn.x(), blockToQueueForSpawn.z()); if(chunkCoord.equals(destChunk)) { if(replaceWithBiomeBlocks) { - if(blockToQueueForSpawn.material.equals(bo3GroundBlock)) + if(blockToQueueForSpawn.material().equals(bo3GroundBlock)) { - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); continue; } - else if(blockToQueueForSpawn.material.equals(bo3StoneBlock)) + else if(blockToQueueForSpawn.material().equals(bo3StoneBlock)) { - blockToQueueForSpawn.material = biomeConfig.getStoneBlockReplaced(world, blockToQueueForSpawn.y); - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + blockToQueueForSpawn.blockContainer = biomeConfig.getStoneBlockReplaced(world, blockToQueueForSpawn.y()).blockContainer(); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); continue; } - else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) + else if(blockToQueueForSpawn.material().equals(bo3SurfaceBlock)) { - blockAbove = world.getMaterial(blockToQueueForSpawn.x, blockToQueueForSpawn.y + 1, blockToQueueForSpawn.z, chunkBeingPopulated); + blockAbove = world.getMaterial(blockToQueueForSpawn.x(), blockToQueueForSpawn.y() + 1, blockToQueueForSpawn.z(), chunkBeingPopulated); if(blockAbove != null && (blockAbove.isSolid() || blockAbove.isLiquid())) { - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getGroundBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); } else { - blockToQueueForSpawn.material = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z); + blockToQueueForSpawn.blockContainer = biomeConfig.surfaceAndGroundControl.getSurfaceBlockAtHeight(world, biomeConfig, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z()).blockContainer(); } - if(blockToQueueForSpawn.material.isAir()) + if(blockToQueueForSpawn.material().isAir()) { - if(blockToQueueForSpawn.y < biomeConfig.waterLevelMax) + if(blockToQueueForSpawn.y() < biomeConfig.waterLevelMax) { - blockToQueueForSpawn.material = MaterialHelper.WATER; + blockToQueueForSpawn.blockContainer = MaterialHelper.WATER.blockContainer(); } else { - blockToQueueForSpawn.material = doBiomeConfigReplaceBlocks ? block.material.parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y) : block.material; + blockToQueueForSpawn.blockContainer = doBiomeConfigReplaceBlocks ? block.material().parseWithBiomeAndHeight(world, biomeConfig, blockToQueueForSpawn.y()).blockContainer() : block.blockContainer; } } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, false); continue; } } @@ -727,13 +686,13 @@ else if(blockToQueueForSpawn.material.equals(bo3SurfaceBlock)) // Don't spawn torches underwater if( spawnUnderWater && - blockToQueueForSpawn.material.isMaterial(DefaultMaterial.TORCH) && - world.getMaterial(blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, chunkBeingPopulated).isLiquid() + blockToQueueForSpawn.material().isMaterial(DefaultMaterial.TORCH) && + world.getMaterial(blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), chunkBeingPopulated).isLiquid() ) { continue; } - setBlock(world, blockToQueueForSpawn.x, blockToQueueForSpawn.y, blockToQueueForSpawn.z, blockToQueueForSpawn.material, blockToQueueForSpawn.metaDataTag, isStructureAtSpawn, chunkBeingPopulated, biomeConfig, doBiomeConfigReplaceBlocks); + setBlock(world, blockToQueueForSpawn.x(), blockToQueueForSpawn.y(), blockToQueueForSpawn.z(), blockToQueueForSpawn.material(), blockToQueueForSpawn.tag(), isStructureAtSpawn, chunkBeingPopulated, biomeConfig, doBiomeConfigReplaceBlocks); } else { outOfBounds = true; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4BlockContainer.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4BlockContainer.java new file mode 100644 index 000000000..a34b03842 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4BlockContainer.java @@ -0,0 +1,63 @@ +package com.pg85.otg.customobjects.bo4; + +import com.pg85.otg.common.BlockContainer; +import com.pg85.otg.common.LocalMaterialData; +import com.pg85.otg.util.bo3.NamedBinaryTag; + +class BO4BlockContainer +{ + private int x; + private int y; + private int z; + BlockContainer blockContainer; + + void x(int x) + { + this.x = x; + } + + int x() + { + return x; + } + + void y(int y) + { + this.y = y; + } + + int y() + { + return y; + } + + void z(int z) + { + this.z = z; + } + + int z() + { + return z; + } + + void blockContainer(BlockContainer blockContainer) + { + this.blockContainer = blockContainer; + } + + BlockContainer blockContainer() + { + return blockContainer; + } + + LocalMaterialData material() + { + return blockContainer.material(); + } + + NamedBinaryTag tag() + { + return blockContainer.tag(); + } +} diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4Config.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4Config.java index a59db1345..edb1c0b82 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4Config.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/BO4Config.java @@ -1,6 +1,7 @@ package com.pg85.otg.customobjects.bo4; import com.pg85.otg.OTG; +import com.pg85.otg.common.BlockContainer; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; @@ -12,7 +13,6 @@ import com.pg85.otg.configuration.world.WorldConfig.ConfigMode; import com.pg85.otg.customobjects.CustomObject; import com.pg85.otg.customobjects.bo4.BO4Config; -import com.pg85.otg.customobjects.bo4.BO4Settings; import com.pg85.otg.customobjects.bo4.bo4function.BO4BlockFunction; import com.pg85.otg.customobjects.bo4.bo4function.BO4BranchFunction; import com.pg85.otg.customobjects.bo4.bo4function.BO4EntityFunction; @@ -27,25 +27,24 @@ import com.pg85.otg.customobjects.bo3.BO3Settings.SpawnHeightEnum; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.logging.LogMarker; -import com.pg85.otg.util.bo3.NamedBinaryTag; +import com.pg85.otg.util.CompressionUtils; import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.helpers.StreamHelper; import com.pg85.otg.util.materials.MaterialHelper; import com.pg85.otg.util.minecraft.defaults.DefaultStructurePart; +import java.io.BufferedInputStream; +import java.io.DataInputStream; import java.io.DataOutput; import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; +import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; -import java.util.zip.DataFormatException; + +import org.apache.commons.lang3.StringUtils; public class BO4Config extends CustomObjectConfigFile { @@ -152,20 +151,7 @@ public class BO4Config extends CustomObjectConfigFile private String worldName; - // Store blocks in arrays instead of as BO4BlockFunctions, - // since that gives way too much overhead memory wise. - // We may have tens of millions of blocks, java doesn't handle lots of small classes well. - private short[][][]blocks; - private LocalMaterialData[]blocksMaterial; - private String[]blocksMetaDataName; - private NamedBinaryTag[]blocksMetaDataTag; - - private LocalMaterialData[][] randomBlocksBlocks; - private byte[][] randomBlocksBlockChances; - private String[][] randomBlocksMetaDataNames; - private NamedBinaryTag[][] randomBlocksMetaDataTags; - private byte[] randomBlocksBlockCount; - // + private BO4BlockFunction[] blocks; private BO4BranchFunction[] branchesOTGPlus; private BO4ModDataFunction[] modDataOTGPlus; @@ -313,83 +299,42 @@ private BO4BlockFunction[][] getSmoothingHeightMap(BO4 start, boolean fromFile) this.heightMap = new BO4BlockFunction[16][16]; - // make heightmap containing the highest or lowest blocks in this chunk - int blockIndex = 0; - LocalMaterialData material; - boolean isSmoothAreaAnchor; - boolean isRandomBlock; - int y; - for(int x = 0; x < xSize; x++) - { - for(int z = 0; z < zSize; z++) - { - if(blocks[x][z] != null) - { - for(int i = 0; i < blocks[x][z].length; i++) - { - isSmoothAreaAnchor = false; - isRandomBlock = this.randomBlocksBlocks[blockIndex] != null; - y = blocks[x][z][i]; - - if(isRandomBlock) - { - for(LocalMaterialData randomMaterial : this.randomBlocksBlocks[blockIndex]) - { - // TODO: Material should never be null, fix the code in RandomBlockFunction.load() that causes this. - if(randomMaterial == null) - { - continue; - } - if(randomMaterial.isSmoothAreaAnchor(start.getConfig().overrideChildSettings && this.overrideChildSettings ? start.getConfig().smoothStartWood : this.smoothStartWood, start.getConfig().spawnUnderWater)) - { - isSmoothAreaAnchor = true; - break; - } - } - } - - material = this.blocksMaterial[blockIndex]; - if( - isSmoothAreaAnchor || - ( - !isRandomBlock && - material.isSmoothAreaAnchor(start.getConfig().overrideChildSettings && this.overrideChildSettings ? start.getConfig().smoothStartWood : this.smoothStartWood, start.getConfig().spawnUnderWater) - ) - ) - { - if( - (!(start.getConfig().overrideChildSettings && this.overrideChildSettings ? start.getConfig().smoothStartTop : this.smoothStartTop) && y == getminY()) || - ((start.getConfig().overrideChildSettings && this.overrideChildSettings ? start.getConfig().smoothStartTop : this.smoothStartTop) && (this.heightMap[x][z] == null || y > this.heightMap[x][z].y)) - ) - { - BO4BlockFunction blockFunction = null; - if(isRandomBlock) - { - blockFunction = new BO4RandomBlockFunction(); - ((BO4RandomBlockFunction)blockFunction).blocks = this.randomBlocksBlocks[blockIndex]; - ((BO4RandomBlockFunction)blockFunction).blockChances = this.randomBlocksBlockChances[blockIndex]; - ((BO4RandomBlockFunction)blockFunction).metaDataNames = this.randomBlocksMetaDataNames[blockIndex]; - ((BO4RandomBlockFunction)blockFunction).metaDataTags = this.randomBlocksMetaDataTags[blockIndex]; - ((BO4RandomBlockFunction)blockFunction).blockCount = this.randomBlocksBlockCount[blockIndex]; - } else { - blockFunction = new BO4BlockFunction(); - } - blockFunction.material = material; - blockFunction.x = x; - blockFunction.y = (short) y; - blockFunction.z = z; - blockFunction.metaDataName = this.blocksMetaDataName[blockIndex]; - blockFunction.metaDataTag = this.blocksMetaDataTag[blockIndex]; - - this.heightMap[x][z] = blockFunction; - } - } - - blockIndex++; - } - } - } - } + // make heightmap containing the highest or lowest blocks in this chunk + boolean startTop = start.getConfig().overrideChildSettings && overrideChildSettings ? start.getConfig().smoothStartTop : smoothStartTop; + boolean allowWood = start.getConfig().overrideChildSettings && overrideChildSettings ? start.getConfig().smoothStartWood : smoothStartWood; + boolean allowLiquid = start.getConfig().spawnUnderWater; + for(BO4BlockFunction block : blocks) + { + if(!startTop) + { + if(block.y() != getminY()) + { + continue; + } + } + else + { + if(heightMap[block.x()][block.z()] != null && block.y() <= heightMap[block.x()][block.z()].y()) + { + continue; + } + } + if(block instanceof BO4RandomBlockFunction) + { + if(Arrays.stream(((BO4RandomBlockFunction) block).blockContainers).noneMatch(b -> b.material().isSmoothAreaAnchor(allowWood, allowLiquid))) + { + continue; + } + } + else + { + if(!block.material().isSmoothAreaAnchor(allowWood, allowLiquid)) + { + continue; + } + } + heightMap[block.x()][block.z()] = block; + } } return this.heightMap; } @@ -424,45 +369,7 @@ private BO4BlockFunction[] getBlocks(boolean fromFile) } } - BO4BlockFunction[] blocksOTGPlus = new BO4BlockFunction[this.blocksMaterial.length]; - - BO4BlockFunction block; - int blockIndex = 0; - for(int x = 0; x < xSize; x++) - { - for(int z = 0; z < zSize; z++) - { - if(this.blocks[x][z] != null) - { - for(int i = 0; i < this.blocks[x][z].length; i++) - { - if(this.randomBlocksBlocks[blockIndex] != null) - { - block = new BO4RandomBlockFunction(this); - ((BO4RandomBlockFunction)block).blocks = this.randomBlocksBlocks[blockIndex]; - ((BO4RandomBlockFunction)block).blockChances = this.randomBlocksBlockChances[blockIndex]; - ((BO4RandomBlockFunction)block).metaDataNames = this.randomBlocksMetaDataNames[blockIndex]; - ((BO4RandomBlockFunction)block).metaDataTags = this.randomBlocksMetaDataTags[blockIndex]; - ((BO4RandomBlockFunction)block).blockCount = this.randomBlocksBlockCount[blockIndex]; - } else { - block = new BO4BlockFunction(this); - } - - block.x = x; - block.y = this.blocks[x][z][i]; - block.z = z; - block.material = this.blocksMaterial[blockIndex]; - block.metaDataName = this.blocksMetaDataName[blockIndex]; - block.metaDataTag = this.blocksMetaDataTag[blockIndex]; - - blocksOTGPlus[blockIndex] = block; - blockIndex++; - } - } - } - } - - return blocksOTGPlus; + return this.blocks; } protected BO4BranchFunction[] getbranches() @@ -565,7 +472,7 @@ private void loadInheritedBO3() short[][] columnSizes = new short[16][16]; for(BO4BlockFunction block : newBlocks) { - columnSizes[block.x][block.z]++; + columnSizes[block.x()][block.z()]++; } loadBlockArrays(newBlocks, columnSizes); @@ -681,21 +588,21 @@ private void readResources() throws InvalidConfigException !(res instanceof CustomObjectErroredFunction) ) { - if(res.x < minX) + if(res.x() < minX) { - minX = res.x; + minX = res.x(); } - if(res.x > maxX) + if(res.x() > maxX) { - maxX = res.x; + maxX = res.x(); } - if(res.z < minZ) + if(res.z() < minZ) { - minZ = res.z; + minZ = res.z(); } - if(res.z > maxZ) + if(res.z() > maxZ) { - maxZ = res.z; + maxZ = res.z(); } } } @@ -739,8 +646,8 @@ private void readResources() throws InvalidConfigException !(res instanceof CustomObjectErroredFunction) ) { - res.x += xOffset; - res.z += zOffset; + res.x(res.x() + xOffset); + res.z(res.z() + zOffset); } if (res instanceof BO4BlockFunction) @@ -750,39 +657,39 @@ private void readResources() throws InvalidConfigException if(res instanceof BO4RandomBlockFunction) { tempBlocksList.add((BO4RandomBlockFunction)res); - columnSizes[res.x + (this.xSize / 2)][res.z + (this.zSize / 2) - 1]++; + columnSizes[res.x() + (this.xSize / 2)][res.z() + (this.zSize / 2) - 1]++; } else { - if(!this.removeAir || !((BO4BlockFunction)res).material.isAir()) + if(!this.removeAir || !((BO4BlockFunction)res).material().isAir()) { tempBlocksList.add((BO4BlockFunction)res); - columnSizes[res.x + (this.xSize / 2)][res.z + (this.zSize / 2) - 1]++; + columnSizes[res.x() + (this.xSize / 2)][res.z() + (this.zSize / 2) - 1]++; } } // Get the real size of this BO3 - if(res.x < this.minX) + if(res.x() < this.minX) { - this.minX = res.x; + this.minX = res.x(); } - if(res.x > this.maxX) + if(res.x() > this.maxX) { - this.maxX = res.x; + this.maxX = res.x(); } - if(((BO4BlockFunction)res).y < this.minY) + if(((BO4BlockFunction)res).y() < this.minY) { - this.minY = ((BO4BlockFunction)res).y; + this.minY = ((BO4BlockFunction)res).y(); } - if(((BO4BlockFunction)res).y > this.maxY) + if(((BO4BlockFunction)res).y() > this.maxY) { - this.maxY = ((BO4BlockFunction)res).y; + this.maxY = ((BO4BlockFunction)res).y(); } - if(res.z < this.minZ) + if(res.z() < this.minZ) { - this.minZ = res.z; + this.minZ = res.z(); } - if(res.z > this.maxZ) + if(res.z() > this.maxZ) { - this.maxZ = res.z; + this.maxZ = res.z(); } } else { if (res instanceof BO4WeightedBranchFunction) @@ -841,85 +748,33 @@ else if (res instanceof BO4EntityFunction) boolean illegalBlock = false; for(BO4BlockFunction block1 : tempBlocksList) { - block1.x += this.getXOffset(); - block1.z += this.getZOffset(); + block1.x(block1.x() + this.getXOffset()); + block1.z(block1.z() + this.getZOffset()); - if(block1.x > 15 || block1.z > 15) + if(block1.x() > 15 || block1.z() > 15) { illegalBlock = true; } - if(block1.x < 0 || block1.z < 0) + if(block1.x() < 0 || block1.z() < 0) { illegalBlock = true; } } - - this.blocks = new short[this.xSize][this.zSize][]; - this.blocksMaterial = new LocalMaterialData[tempBlocksList.size()]; - this.blocksMetaDataName = new String[tempBlocksList.size()]; - this.blocksMetaDataTag = new NamedBinaryTag[tempBlocksList.size()]; - - this.randomBlocksBlocks = new LocalMaterialData[tempBlocksList.size()][]; - this.randomBlocksBlockChances = new byte[tempBlocksList.size()][]; - this.randomBlocksMetaDataNames = new String[tempBlocksList.size()][]; - this.randomBlocksMetaDataTags = new NamedBinaryTag[tempBlocksList.size()][]; - this.randomBlocksBlockCount = new byte[tempBlocksList.size()]; - - short[][] columnBlockIndex = new short[this.xSize][this.zSize]; - BO4BlockFunction[] blocksSorted = new BO4BlockFunction[tempBlocksList.size()]; - int blocksSortedIndex = 0; - for(int x = 0; x < this.xSize; x++) - { - for(int z = 0; z < this.zSize; z++) - { - for(int h = 0; h < tempBlocksList.size(); h++) - { - if(tempBlocksList.get(h).x == x && tempBlocksList.get(h).z == z) - { - blocksSorted[blocksSortedIndex] = tempBlocksList.get(h); - blocksSortedIndex++; - } - } - } - } - BO4BlockFunction block; - for(int blockIndex = 0; blockIndex < blocksSorted.length; blockIndex++) - { - block = blocksSorted[blockIndex]; - if(this.blocks[block.x][block.z] == null) - { - this.blocks[block.x][block.z] = new short[columnSizes[block.x][block.z]]; - } - this.blocks[block.x][block.z][columnBlockIndex[block.x][block.z]] = (short) block.y; - - this.blocksMaterial[blockIndex] = block.material; - this.blocksMetaDataName[blockIndex] = block.metaDataName; - this.blocksMetaDataTag[blockIndex] = block.metaDataTag; - - if(block instanceof BO4RandomBlockFunction) - { - this.randomBlocksBlocks[blockIndex] = ((BO4RandomBlockFunction)block).blocks; - this.randomBlocksBlockChances[blockIndex] = ((BO4RandomBlockFunction)block).blockChances; - this.randomBlocksMetaDataNames[blockIndex] = ((BO4RandomBlockFunction)block).metaDataNames; - this.randomBlocksMetaDataTags[blockIndex] = ((BO4RandomBlockFunction)block).metaDataTags; - this.randomBlocksBlockCount[blockIndex] = ((BO4RandomBlockFunction)block).blockCount; - } - columnBlockIndex[block.x][block.z]++; - } + this.blocks = tempBlocksList.toArray(new BO4BlockFunction[tempBlocksList.size()]); boolean illegalModData = false; for(BO4ModDataFunction modData : tempModDataList) { - modData.x += this.getXOffset(); - modData.z += this.getZOffset(); + modData.x(modData.x() + this.getXOffset()); + modData.z(modData.z() + this.getZOffset()); - if(modData.x > 15 || modData.z > 15) + if(modData.x() > 15 || modData.z() > 15) { illegalModData = true; } - if(modData.x < 0 || modData.z < 0) + if(modData.x() < 0 || modData.z() < 0) { illegalModData = true; } @@ -929,15 +784,15 @@ else if (res instanceof BO4EntityFunction) boolean illegalSpawnerData = false; for(BO4SpawnerFunction spawnerData : tempSpawnerList) { - spawnerData.x += this.getXOffset(); - spawnerData.z += this.getZOffset(); + spawnerData.x(spawnerData.x() + this.getXOffset()); + spawnerData.z(spawnerData.z() + this.getZOffset()); - if(spawnerData.x > 15 || spawnerData.z > 15) + if(spawnerData.x() > 15 || spawnerData.z() > 15) { illegalSpawnerData = true; } - if(spawnerData.x < 0 || spawnerData.z < 0) + if(spawnerData.x() < 0 || spawnerData.z() < 0) { illegalSpawnerData = true; } @@ -947,15 +802,15 @@ else if (res instanceof BO4EntityFunction) boolean illegalParticleData = false; for(BO4ParticleFunction particleData : tempParticlesList) { - particleData.x += this.getXOffset(); - particleData.z += this.getZOffset(); + particleData.x(particleData.x() + this.getXOffset()); + particleData.z(particleData.z() + this.getZOffset()); - if(particleData.x > 15 || particleData.z > 15) + if(particleData.x() > 15 || particleData.z() > 15) { illegalParticleData = true; } - if(particleData.x < 0 || particleData.z < 0) + if(particleData.x() < 0 || particleData.z() < 0) { illegalParticleData = true; } @@ -965,15 +820,15 @@ else if (res instanceof BO4EntityFunction) boolean illegalEntityData = false; for(BO4EntityFunction entityData : tempEntitiesList) { - entityData.x += this.getXOffset(); - entityData.z += this.getZOffset(); + entityData.x(entityData.x() + this.getXOffset()); + entityData.z(entityData.z() + this.getZOffset()); - if(entityData.x > 15 || entityData.z > 15) + if(entityData.x() > 15 || entityData.z() > 15) { illegalEntityData = true; } - if(entityData.x < 0 || entityData.z < 0) + if(entityData.x() < 0 || entityData.z() < 0) { illegalEntityData = true; } @@ -1350,7 +1205,7 @@ protected void readConfigSettings() throws InvalidConfigException this.doReplaceBlocks = readSettings(BO4Settings.DO_REPLACE_BLOCKS); String fixedRotation = readSettings(BO4Settings.FIXED_ROTATION); - this.fixedRotation = fixedRotation == null || fixedRotation.trim().length() == 0 ? null : Rotation.FromString(fixedRotation); + this.fixedRotation = StringUtils.isBlank(fixedRotation) ? null : Rotation.FromString(fixedRotation); // Read the resources readResources(); @@ -1669,8 +1524,9 @@ public void writeToStream(DataOutput stream) throws IOException if(block instanceof BO4RandomBlockFunction) { randomBlockCount++; - for(LocalMaterialData material : ((BO4RandomBlockFunction)block).blocks) + for(BlockContainer blockContainer : ((BO4RandomBlockFunction)block).blockContainers) { + LocalMaterialData material = blockContainer.material(); if(!materials.contains(material)) { materials.add(material); @@ -1680,13 +1536,13 @@ public void writeToStream(DataOutput stream) throws IOException nonRandomBlockCount++; } - if(block.material != null && !materials.contains(block.material)) + if(block.material() != null && !materials.contains(block.material())) { - materials.add(block.material); + materials.add(block.material()); } - if(block.metaDataName != null && !metaDataNames.contains(block.metaDataName)) + if(block.hasTag() && !metaDataNames.contains(block.tagPath())) { - metaDataNames.add(block.metaDataName); + metaDataNames.add(block.tagPath()); } } @@ -1721,7 +1577,7 @@ public void writeToStream(DataOutput stream) throws IOException { if(!(blockFunction instanceof BO4RandomBlockFunction)) { - if(blockFunction.x == x && blockFunction.z == z) + if(blockFunction.x() == x && blockFunction.z() == z) { blocksInColumn.add(blockFunction); } @@ -1761,7 +1617,7 @@ public void writeToStream(DataOutput stream) throws IOException { if(blockFunction instanceof BO4RandomBlockFunction) { - if(blockFunction.x == x && blockFunction.z == z) + if(blockFunction.x() == x && blockFunction.z() == z) { blocksInColumn.add(blockFunction); } @@ -1791,118 +1647,78 @@ public void writeToStream(DataOutput stream) throws IOException public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigException { - FileInputStream fis; - ByteBuffer bufferCompressed = null; - ByteBuffer bufferDecompressed = null; - try + try(DataInputStream in = new DataInputStream(new BufferedInputStream(CompressionUtils.newInflaterInputStream(this.reader.getFile().toPath())))) { - fis = new FileInputStream(this.reader.getFile()); - try - { - bufferCompressed = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - bufferCompressed.get(compressedBytes); - try { - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - bufferDecompressed = ByteBuffer.wrap(decompressedBytes); - } catch (DataFormatException e1) { - e1.printStackTrace(); - } - - //buffer.get(data, 0, remaining); // do something with data boolean isBO4Data = true; boolean inheritedBO3Loaded = true; - int bo4DataVersion = bufferDecompressed.getInt(); + int bo4DataVersion = in.readInt(); if(bo4DataVersion != this.bo4DataVersion) { - // TODO: Should only need to close the reader? - if(bufferCompressed != null) - { - bufferCompressed.clear(); - } - if(bufferDecompressed != null) - { - bufferDecompressed.clear(); - } - try { - fis.getChannel().close(); - } - catch (IOException e) - { - e.printStackTrace(); - } - try { - fis.close(); - } - catch (IOException e) - { - e.printStackTrace(); - } throw new InvalidConfigException("Could not read BO4Data file " + this.reader.getName() + ", it is outdated. Delete and re-export BO4Data files to fix this, or delete and reinstall your OTG preset."); } - int minimumSizeTop = bufferDecompressed.getInt(); - int minimumSizeBottom = bufferDecompressed.getInt(); - int minimumSizeLeft = bufferDecompressed.getInt(); - int minimumSizeRight = bufferDecompressed.getInt(); - - int minX = bufferDecompressed.getInt(); - int maxX = bufferDecompressed.getInt(); - int minY = bufferDecompressed.getInt(); - int maxY = bufferDecompressed.getInt(); - int minZ = bufferDecompressed.getInt(); - int maxZ = bufferDecompressed.getInt(); - - String author = StreamHelper.readStringFromBuffer(bufferDecompressed); - String description = StreamHelper.readStringFromBuffer(bufferDecompressed); - ConfigMode settingsMode = ConfigMode.valueOf(StreamHelper.readStringFromBuffer(bufferDecompressed)); - int frequency = bufferDecompressed.getInt(); - SpawnHeightEnum spawnHeight = SpawnHeightEnum.valueOf(StreamHelper.readStringFromBuffer(bufferDecompressed)); - int minHeight = bufferDecompressed.getInt(); - int maxHeight = bufferDecompressed.getInt(); - short inheritedBO3sSize = bufferDecompressed.getShort(); + int minimumSizeTop = in.readInt(); + int minimumSizeBottom = in.readInt(); + int minimumSizeLeft = in.readInt(); + int minimumSizeRight = in.readInt(); + + int minX = in.readInt(); + int maxX = in.readInt(); + int minY = in.readInt(); + int maxY = in.readInt(); + int minZ = in.readInt(); + int maxZ = in.readInt(); + + String author = StreamHelper.readStringFromStream(in); + String description = StreamHelper.readStringFromStream(in); + ConfigMode settingsMode = ConfigMode.valueOf(StreamHelper.readStringFromStream(in)); + int frequency = in.readInt(); + SpawnHeightEnum spawnHeight = SpawnHeightEnum.valueOf(StreamHelper.readStringFromStream(in)); + int minHeight = in.readInt(); + int maxHeight = in.readInt(); + short inheritedBO3sSize = in.readShort(); ArrayList inheritedBO3s = new ArrayList(); for(int i = 0; i < inheritedBO3sSize; i++) { - inheritedBO3s.add(StreamHelper.readStringFromBuffer(bufferDecompressed)); + inheritedBO3s.add(StreamHelper.readStringFromStream(in)); } - String inheritBO3 = StreamHelper.readStringFromBuffer(bufferDecompressed); - Rotation inheritBO3Rotation = Rotation.valueOf(StreamHelper.readStringFromBuffer(bufferDecompressed)); - boolean overrideChildSettings = bufferDecompressed.get() != 0; - boolean overrideParentHeight = bufferDecompressed.get() != 0; - boolean canOverride = bufferDecompressed.get() != 0; - int branchFrequency = bufferDecompressed.getInt(); - String branchFrequencyGroup = StreamHelper.readStringFromBuffer(bufferDecompressed); - boolean mustBeBelowOther = bufferDecompressed.get() != 0; - boolean mustBeInsideWorldBorders = bufferDecompressed.get() != 0; - String mustBeInside = StreamHelper.readStringFromBuffer(bufferDecompressed); - String cannotBeInside = StreamHelper.readStringFromBuffer(bufferDecompressed); - String replacesBO3 = StreamHelper.readStringFromBuffer(bufferDecompressed); - boolean canSpawnOnWater = bufferDecompressed.get() != 0; - boolean spawnOnWaterOnly = bufferDecompressed.get() != 0; - boolean spawnUnderWater = bufferDecompressed.get() != 0; - boolean spawnAtWaterLevel = bufferDecompressed.get() != 0; - boolean doReplaceBlocks = bufferDecompressed.get() != 0; - int heightOffset = bufferDecompressed.getInt(); - boolean removeAir = bufferDecompressed.get() != 0; - String replaceAbove = StreamHelper.readStringFromBuffer(bufferDecompressed); - String replaceBelow = StreamHelper.readStringFromBuffer(bufferDecompressed); - boolean replaceWithBiomeBlocks = bufferDecompressed.get() != 0; - String replaceWithSurfaceBlock = StreamHelper.readStringFromBuffer(bufferDecompressed); - String replaceWithGroundBlock = StreamHelper.readStringFromBuffer(bufferDecompressed); - String replaceWithStoneBlock = StreamHelper.readStringFromBuffer(bufferDecompressed); - int smoothRadius = bufferDecompressed.getInt(); - int smoothHeightOffset = bufferDecompressed.getInt(); - boolean smoothStartTop = bufferDecompressed.get() != 0; - boolean smoothStartWood = bufferDecompressed.get() != 0; - String smoothingSurfaceBlock = StreamHelper.readStringFromBuffer(bufferDecompressed); - String smoothingGroundBlock = StreamHelper.readStringFromBuffer(bufferDecompressed); - String bo3Group = StreamHelper.readStringFromBuffer(bufferDecompressed); - boolean isSpawnPoint = bufferDecompressed.get() != 0; - boolean isCollidable = bufferDecompressed.get() != 0; - boolean useCenterForHighestBlock = bufferDecompressed.get() != 0; + String inheritBO3 = StreamHelper.readStringFromStream(in); + Rotation inheritBO3Rotation = Rotation.valueOf(StreamHelper.readStringFromStream(in)); + boolean overrideChildSettings = in.readByte() != 0; + boolean overrideParentHeight = in.readByte() != 0; + boolean canOverride = in.readByte() != 0; + int branchFrequency = in.readInt(); + String branchFrequencyGroup = StreamHelper.readStringFromStream(in); + boolean mustBeBelowOther = in.readByte() != 0; + boolean mustBeInsideWorldBorders = in.readByte() != 0; + String mustBeInside = StreamHelper.readStringFromStream(in); + String cannotBeInside = StreamHelper.readStringFromStream(in); + String replacesBO3 = StreamHelper.readStringFromStream(in); + boolean canSpawnOnWater = in.readByte() != 0; + boolean spawnOnWaterOnly = in.readByte() != 0; + boolean spawnUnderWater = in.readByte() != 0; + boolean spawnAtWaterLevel = in.readByte() != 0; + boolean doReplaceBlocks = in.readByte() != 0; + int heightOffset = in.readInt(); + boolean removeAir = in.readByte() != 0; + String replaceAbove = StreamHelper.readStringFromStream(in); + String replaceBelow = StreamHelper.readStringFromStream(in); + boolean replaceWithBiomeBlocks = in.readByte() != 0; + String replaceWithSurfaceBlock = StreamHelper.readStringFromStream(in); + String replaceWithGroundBlock = StreamHelper.readStringFromStream(in); + String replaceWithStoneBlock = StreamHelper.readStringFromStream(in); + int smoothRadius = in.readInt(); + int smoothHeightOffset = in.readInt(); + boolean smoothStartTop = in.readByte() != 0; + boolean smoothStartWood = in.readByte() != 0; + String smoothingSurfaceBlock = StreamHelper.readStringFromStream(in); + String smoothingGroundBlock = StreamHelper.readStringFromStream(in); + String bo3Group = StreamHelper.readStringFromStream(in); + boolean isSpawnPoint = in.readByte() != 0; + boolean isCollidable = in.readByte() != 0; + boolean useCenterForHighestBlock = in.readByte() != 0; HashMap branchFrequencyGroups = new HashMap(); if(branchFrequencyGroup != null && branchFrequencyGroup.trim().length() > 0) @@ -1989,48 +1805,48 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce } } - int branchesOTGPlusLength = bufferDecompressed.getInt(); + int branchesOTGPlusLength = in.readInt(); boolean branchType; BO4BranchFunction branch; BO4BranchFunction[] branchesOTGPlus = new BO4BranchFunction[branchesOTGPlusLength]; for(int i = 0; i < branchesOTGPlusLength; i++) { - branchType = bufferDecompressed.get() != 0; + branchType = in.readByte() != 0; if(branchType) { - branch = BO4WeightedBranchFunction.fromStream(this, bufferDecompressed); + branch = BO4WeightedBranchFunction.fromStream(this, in); } else { - branch = BO4BranchFunction.fromStream(this, bufferDecompressed); + branch = BO4BranchFunction.fromStream(this, in); } branchesOTGPlus[i] = branch; } - int entityDataOTGPlusLength = bufferDecompressed.getInt(); + int entityDataOTGPlusLength = in.readInt(); BO4EntityFunction[] entityDataOTGPlus = new BO4EntityFunction[entityDataOTGPlusLength]; for(int i = 0; i < entityDataOTGPlusLength; i++) { - entityDataOTGPlus[i] = BO4EntityFunction.fromStream(this, bufferDecompressed); + entityDataOTGPlus[i] = BO4EntityFunction.fromStream(this, in); } - int particleDataOTGPlusLength = bufferDecompressed.getInt(); + int particleDataOTGPlusLength = in.readInt(); BO4ParticleFunction[] particleDataOTGPlus = new BO4ParticleFunction[particleDataOTGPlusLength]; for(int i = 0; i < particleDataOTGPlusLength; i++) { - particleDataOTGPlus[i] = BO4ParticleFunction.fromStream(this, bufferDecompressed); + particleDataOTGPlus[i] = BO4ParticleFunction.fromStream(this, in); } - int spawnerDataOTGPlusLength = bufferDecompressed.getInt(); + int spawnerDataOTGPlusLength = in.readInt(); BO4SpawnerFunction[] spawnerDataOTGPlus = new BO4SpawnerFunction[spawnerDataOTGPlusLength]; for(int i = 0; i < spawnerDataOTGPlusLength; i++) { - spawnerDataOTGPlus[i] = BO4SpawnerFunction.fromStream(this, bufferDecompressed); + spawnerDataOTGPlus[i] = BO4SpawnerFunction.fromStream(this, in); } - int modDataOTGPlusLength = bufferDecompressed.getInt(); + int modDataOTGPlusLength = in.readInt(); BO4ModDataFunction[] modDataOTGPlus = new BO4ModDataFunction[modDataOTGPlusLength]; for(int i = 0; i < modDataOTGPlusLength; i++) { - modDataOTGPlus[i] = BO4ModDataFunction.fromStream(this, bufferDecompressed); + modDataOTGPlus[i] = BO4ModDataFunction.fromStream(this, in); } ArrayList newBlocks = new ArrayList(); @@ -2046,18 +1862,18 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce // Reconstruct blocks if(getBlocks) { - short metaDataNamesArrLength = bufferDecompressed.getShort(); + short metaDataNamesArrLength = in.readShort(); String[] metaDataNames = new String[metaDataNamesArrLength]; for(int i = 0; i < metaDataNamesArrLength; i++) { - metaDataNames[i] = StreamHelper.readStringFromBuffer(bufferDecompressed); + metaDataNames[i] = StreamHelper.readStringFromStream(in); } - short blocksArrArrLength = bufferDecompressed.getShort(); + short blocksArrArrLength = in.readShort(); LocalMaterialData[] blocksArr = new LocalMaterialData[blocksArrArrLength]; for(int i = 0; i < blocksArrArrLength; i++) { - String materialName = StreamHelper.readStringFromBuffer(bufferDecompressed); + String materialName = StreamHelper.readStringFromStream(in); try { blocksArr[i] = MaterialHelper.readMaterial(materialName); } catch (InvalidConfigException e) { @@ -2073,7 +1889,7 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce // TODO: This assumes that loading blocks in a different order won't matter, which may not be true? // Anything that spawns on top, entities/spawners etc, should be spawned last tho, so shouldn't be a problem? - int nonRandomBlockCount = bufferDecompressed.getInt(); + int nonRandomBlockCount = in.readInt(); int nonRandomBlockIndex = 0; ArrayList nonRandomBlocks = new ArrayList(); if(nonRandomBlockCount > 0) @@ -2082,11 +1898,11 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce { for(int z = this.getminZ(); z < this.zSize; z++) { - short blocksInColumnSize = bufferDecompressed.getShort(); + short blocksInColumnSize = in.readShort(); for(int j = 0; j < blocksInColumnSize; j++) { columnSizes[x][z]++; - nonRandomBlocks.add(BO4BlockFunction.fromStream(x, z, metaDataNames, blocksArr, this, bufferDecompressed)); + nonRandomBlocks.add(BO4BlockFunction.fromStream(x, z, metaDataNames, blocksArr, this, in)); nonRandomBlockIndex++; if(nonRandomBlockCount == nonRandomBlockIndex) { @@ -2105,7 +1921,7 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce } } - int randomBlockCount = bufferDecompressed.getInt(); + int randomBlockCount = in.readInt(); int randomBlockIndex = 0; ArrayList randomBlocks = new ArrayList(); if(randomBlockCount > 0) @@ -2114,11 +1930,11 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce { for(int z = this.getminZ(); z < this.zSize; z++) { - short blocksInColumnSize = bufferDecompressed.getShort(); + short blocksInColumnSize = in.readShort(); for(int j = 0; j < blocksInColumnSize; j++) { columnSizes[x][z]++; - randomBlocks.add(BO4RandomBlockFunction.fromStream(x, z, metaDataNames, blocksArr, this, bufferDecompressed)); + randomBlocks.add(BO4RandomBlockFunction.fromStream(x, z, metaDataNames, blocksArr, this, in)); randomBlockIndex++; if(randomBlockCount == randomBlockIndex) { @@ -2211,67 +2027,15 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce { loadBlockArrays(newBlocks, columnSizes); } - } - catch (Exception | Error e1) - { - // TODO: Should only need to close the reader? - if(bufferCompressed != null) - { - bufferCompressed.clear(); - } - if(bufferDecompressed != null) - { - bufferDecompressed.clear(); - } - try { - fis.getChannel().close(); - } - catch (IOException e) - { - e.printStackTrace(); - } - try { - fis.close(); - } - catch (IOException e) - { - e.printStackTrace(); - } - - e1.printStackTrace(); - throw new InvalidConfigException("Could not read BO4Data file " + this.reader.getName() + ", it may be outdated or corrupted. Delete and re-export BO4Data files to fix this, or delete and reinstall your OTG preset."); - } - - // When finished - - // TODO: Should only need to close the reader? - if(bufferCompressed != null) - { - bufferCompressed.clear(); - } - if(bufferDecompressed != null) - { - bufferDecompressed.clear(); - } - try { - fis.getChannel().close(); - } - catch (IOException e) - { - e.printStackTrace(); - } - try { - fis.close(); - } - catch (IOException e) - { - e.printStackTrace(); - } } - catch (FileNotFoundException e2) + catch (NoSuchFileException e) { + e.printStackTrace(); + return null; + } + catch (IOException e) { - e2.printStackTrace(); - return null; + e.printStackTrace(); + throw new InvalidConfigException("Could not read BO4Data file " + this.reader.getName() + ", it may be outdated or corrupted. Delete and re-export BO4Data files to fix this, or delete and reinstall your OTG preset."); } return this; @@ -2279,71 +2043,7 @@ public BO4Config readFromBO4DataFile(boolean getBlocks) throws InvalidConfigExce private void loadBlockArrays(ArrayList newBlocks, short[][] columnSizes) { - // Store blocks in arrays instead of BO4BlockFunctions, - // since that gives way too much overhead memory wise. - // We may have tens of millions of blocks, java doesn't handle lots of small classes well. - this.blocks = new short[xSize][zSize][]; - this.blocksMaterial = new LocalMaterialData[newBlocks.size()]; - this.blocksMetaDataName = new String[newBlocks.size()]; - this.blocksMetaDataTag = new NamedBinaryTag[newBlocks.size()]; - - this.randomBlocksBlocks = new LocalMaterialData[newBlocks.size()][]; - this.randomBlocksBlockChances = new byte[newBlocks.size()][]; - this.randomBlocksMetaDataNames = new String[newBlocks.size()][]; - this.randomBlocksMetaDataTags = new NamedBinaryTag[newBlocks.size()][]; - this.randomBlocksBlockCount = new byte[newBlocks.size()]; - - BO4BlockFunction block; - short[][] columnBlockIndex = new short[xSize][zSize]; - for(int x = 0; x < xSize; x++) - { - for(int z = 0; z < zSize; z++) - { - if(this.blocks[x][z] == null) - { - this.blocks[x ][z] = new short[columnSizes[x][z]]; - } - } - } - for(int i = 0; i < newBlocks.size(); i++) - { - block = newBlocks.get(i); - - this.blocks[block.x][block.z][columnBlockIndex[block.x][block.z]] = (short) block.y; - - int blockIndex = columnBlockIndex[block.x][block.z] + getColumnBlockIndex(columnSizes, block.x, block.z); - - this.blocksMaterial[blockIndex] = block.material; - this.blocksMetaDataName[blockIndex] = block.metaDataName; - this.blocksMetaDataTag[blockIndex] = block.metaDataTag; - - if(block instanceof BO4RandomBlockFunction) - { - this.randomBlocksBlocks[blockIndex] = ((BO4RandomBlockFunction)block).blocks; - this.randomBlocksBlockChances[blockIndex] = ((BO4RandomBlockFunction)block).blockChances; - this.randomBlocksMetaDataNames[blockIndex] = ((BO4RandomBlockFunction)block).metaDataNames; - this.randomBlocksMetaDataTags[blockIndex] = ((BO4RandomBlockFunction)block).metaDataTags; - this.randomBlocksBlockCount[blockIndex] = ((BO4RandomBlockFunction)block).blockCount; - } - columnBlockIndex[block.x][block.z]++; - } - } - - private int getColumnBlockIndex(short[][] columnSizes, int columnX, int columnZ) - { - int blockIndex = 0; - for(int x = 0; x < 16; x++) - { - for(int z = 0; z < 16; z++) - { - if(columnX == x && columnZ == z) - { - return blockIndex; - } - blockIndex += columnSizes[x][z]; - } - } - return blockIndex; + this.blocks = newBlocks.toArray(new BO4BlockFunction[newBlocks.size()]); } @Override diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BlockFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BlockFunction.java index e51723253..1a99be643 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BlockFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BlockFunction.java @@ -1,14 +1,12 @@ package com.pg85.otg.customobjects.bo4.bo4function; +import java.io.DataInput; import java.io.DataOutput; -import java.io.File; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.Random; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.customobjects.bo3.BO3Loader; import com.pg85.otg.customobjects.bo4.BO4Config; import com.pg85.otg.customobjects.bofunctions.BlockFunction; import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructureCoordinate; @@ -19,53 +17,24 @@ * Represents a block in a BO3. */ public class BO4BlockFunction extends BlockFunction -{ - public BO4BlockFunction() { } - - public BO4BlockFunction(BO4Config holder) - { - this.holder = holder; - } - +{ @Override public void spawn(LocalWorld world, Random random, int x, int y, int z, ChunkCoordinate chunkBeingPopulated, boolean replaceBlock) { - world.setBlock(x, y, z, material, metaDataTag, chunkBeingPopulated, true); + world.setBlock(x, y, z, material(), tag(), chunkBeingPopulated, true); } public BO4BlockFunction rotate(Rotation rotation) { - BO4BlockFunction rotatedBlock = new BO4BlockFunction(this.getHolder()); - - rotatedBlock.material = material; // TODO: Make sure this won't cause problems - - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x, y, z, rotation); - - rotatedBlock.x = rotatedCoords.getX(); - rotatedBlock.y = rotatedCoords.getY(); - rotatedBlock.z = rotatedCoords.getZ(); - - // TODO: This makes no sense, why is rotation inverted??? Should be: NORTH:0,WEST:1,SOUTH:2,EAST:3 - - // Apply rotation - if(rotation.getRotationId() == 3) - { - rotatedBlock.material = rotatedBlock.material.rotate(1); - } - if(rotation.getRotationId() == 2) - { - rotatedBlock.material = rotatedBlock.material.rotate(2); - } - if(rotation.getRotationId() == 1) - { - rotatedBlock.material = rotatedBlock.material.rotate(3); - } - - rotatedBlock.metaDataTag = metaDataTag; - rotatedBlock.metaDataName = metaDataName; + BO4BlockFunction rotatedBlock = new BO4BlockFunction(); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x(), y(), z(), rotation); + rotatedBlock.x(rotatedCoords.getX()); + rotatedBlock.y(rotatedCoords.getY()); + rotatedBlock.z(rotatedCoords.getZ()); + rotatedBlock.blockContainer = blockContainer.rotate(rotation); return rotatedBlock; - } + } @Override public Class getHolderType() @@ -75,13 +44,13 @@ public Class getHolderType() public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, DataOutput stream) throws IOException { - stream.writeShort(this.y); + stream.writeShort(this.y()); boolean bFound = false; - if(this.material != null) + if(this.material() != null) { for(int i = 0; i < materials.length; i++) { - if(materials[i].equals(this.material)) + if(materials[i].equals(this.material())) { stream.writeShort(i); bFound = true; @@ -94,11 +63,11 @@ public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, stream.writeShort(-1); } bFound = false; - if(this.metaDataName != null) + if(this.hasTag()) { for(int i = 0; i < metaDataNames.length; i++) { - if(metaDataNames[i].equals(this.metaDataName)) + if(metaDataNames[i].equals(this.tagPath())) { stream.writeShort(i); bFound = true; @@ -112,36 +81,23 @@ public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, } } - public static BO4BlockFunction fromStream(int x, int z, String[] metaDataNames, LocalMaterialData[] materials, BO4Config holder, ByteBuffer buffer) throws IOException + public static BO4BlockFunction fromStream(int x, int z, String[] metaDataNames, LocalMaterialData[] materials, BO4Config holder, DataInput in) throws IOException { - BO4BlockFunction rbf = new BO4BlockFunction(holder); - - File file = holder.getFile(); + BO4BlockFunction rbf = new BO4BlockFunction(); - rbf.x = x; - rbf.y = buffer.getShort(); - rbf.z = z; - - short materialId = buffer.getShort(); - if(materialId != -1) - { - rbf.material = materials[materialId]; - } + rbf.x(x); + rbf.y(in.readShort()); + rbf.z(z); - short metaDataNameId = buffer.getShort(); - if(metaDataNameId != -1) - { - rbf.metaDataName = metaDataNames[metaDataNameId]; - } - - if(rbf.metaDataName != null) - { - // Get the file - rbf.metaDataTag = BO3Loader.loadMetadata(rbf.metaDataName, file); - if(rbf.metaDataTag == null) - { - rbf.metaDataName = null; - } + short materialId = in.readShort(); + short metaDataNameId = in.readShort(); + if(metaDataNameId != -1) + { + rbf.blockContainer = materials[materialId].blockContainer(holder.getFile().getParentFile().toPath(), metaDataNames[metaDataNameId]); + } + else + { + rbf.blockContainer = materials[materialId].blockContainer(); } return rbf; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BranchFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BranchFunction.java index 5378245c7..667fda035 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BranchFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4BranchFunction.java @@ -12,9 +12,9 @@ import com.pg85.otg.util.helpers.StreamHelper; import com.pg85.otg.util.helpers.StringHelper; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.*; /** @@ -27,20 +27,13 @@ public class BO4BranchFunction extends BranchFunction String branchGroup = ""; boolean isRequiredBranch = false; - public BO4BranchFunction() { } - - public BO4BranchFunction(BO4Config holder) - { - this.holder = holder; - } - public BO4BranchFunction rotate(Rotation rotation) { - BO4BranchFunction rotatedBranch = new BO4BranchFunction(this.getHolder()); + BO4BranchFunction rotatedBranch = new BO4BranchFunction(); - rotatedBranch.x = x; - rotatedBranch.y = y; - rotatedBranch.z = z; + rotatedBranch.x(x()); + rotatedBranch.y(y()); + rotatedBranch.z(z()); rotatedBranch.totalChance = totalChance; rotatedBranch.totalChanceSet = totalChanceSet; @@ -50,28 +43,22 @@ public BO4BranchFunction rotate(Rotation rotation) rotatedBranch.branchesOTGPlus = branchesOTGPlus; // TODO: Make sure this won't cause problems - rotatedBranch.holder = holder; - rotatedBranch.valid = valid; - rotatedBranch.inputName = inputName; - rotatedBranch.inputArgs = inputArgs; - rotatedBranch.error = error; - - int newX = rotatedBranch.x; - int newZ = rotatedBranch.z; + int newX = rotatedBranch.x(); + int newZ = rotatedBranch.z(); for(int i = 0; i < rotation.getRotationId(); i++) { - newX = rotatedBranch.z; - newZ = -rotatedBranch.x; + newX = rotatedBranch.z(); + newZ = -rotatedBranch.x(); - rotatedBranch.x = newX; - rotatedBranch.y = rotatedBranch.y; - rotatedBranch.z = newZ; + rotatedBranch.x(newX); + rotatedBranch.y(rotatedBranch.y()); + rotatedBranch.z(newZ); ArrayList rotatedBranchBranches = new ArrayList(); for (BO4BranchNode holder : rotatedBranch.branchesOTGPlus) { - rotatedBranchBranches.add(new BO4BranchNode(holder.branchDepth, holder.isRequiredBranch, holder.isWeightedBranch, holder.getRotation().next(), holder.getChance(), holder.getCustomObject(false, null), holder.customObjectName, holder.branchGroup)); + rotatedBranchBranches.add(new BO4BranchNode(holder.branchDepth, holder.isRequiredBranch, holder.isWeightedBranch, holder.getRotation().next(rotation), holder.getChance(), holder.getCustomObject(false, null), holder.customObjectName, holder.branchGroup)); } rotatedBranch.branchesOTGPlus = rotatedBranchBranches; } @@ -80,7 +67,7 @@ public BO4BranchFunction rotate(Rotation rotation) } @Override - public void load(List args) throws InvalidConfigException + public void load(BO4Config holder, List args) throws InvalidConfigException { branchesOTGPlus = new ArrayList(); readArgs(args, false); @@ -93,9 +80,7 @@ protected double readArgs(List args, boolean accumulateChances) throws I // assureSize only returns false if size() < size assureSize(8, args); - x = readInt(args.get(0), -10000, 10000); - y = readInt(args.get(1), -255, 255); - z = readInt(args.get(2), -10000, 10000); + readXYZ(args, 0); isRequiredBranch = readBoolean(args.get(3)); int i; @@ -159,9 +144,9 @@ public String makeString() { StringBuilder output = new StringBuilder(getConfigName()) .append('(') - .append(x).append(',') - .append(y).append(',') - .append(z).append(','); + .append(x()).append(',') + .append(y()).append(',') + .append(z()).append(','); output.append(isRequiredBranch); for (Iterator it = branchesOTGPlus.iterator(); it.hasNext();) @@ -194,9 +179,8 @@ public CustomStructureCoordinate toCustomObjectCoordinate(LocalWorld world, Rand double randomChance = random.nextDouble() * totalChance; if (randomChance <= branch.getChance()) { - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedCoord(this.x, this.y, this.z, rotation); - Rotation newRotation = Rotation.getRotation((rotation.getRotationId() + branch.getRotation().getRotationId()) % 4); - return new BO4CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, newRotation, x + rotatedCoords.getX(), (short)(y + rotatedCoords.getY()), z + rotatedCoords.getZ(), branch.branchDepth, branch.isRequiredBranch, branch.isWeightedBranch, branch.branchGroup); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedCoord(this.x(), this.y(), this.z(), rotation); + return new BO4CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, branch.getRotation().next(rotation), x + rotatedCoords.getX(), (short)(y + rotatedCoords.getY()), z + rotatedCoords.getZ(), branch.branchDepth, branch.isRequiredBranch, branch.isWeightedBranch, branch.branchGroup); } } return null; @@ -213,14 +197,14 @@ public void writeToStream(DataOutput stream) throws IOException StreamHelper.writeStringToStream(stream, makeString()); } - public static BO4BranchFunction fromStream(BO4Config holder, ByteBuffer buffer) throws IOException, InvalidConfigException + public static BO4BranchFunction fromStream(BO4Config holder, DataInput in) throws IOException, InvalidConfigException { - BO4BranchFunction branchFunction = new BO4BranchFunction(holder); - String configFunctionString = StreamHelper.readStringFromBuffer(buffer); + BO4BranchFunction branchFunction = new BO4BranchFunction(); + String configFunctionString = StreamHelper.readStringFromStream(in); int bracketIndex = configFunctionString.indexOf('('); String parameters = configFunctionString.substring(bracketIndex + 1, configFunctionString.length() - 1); - List args = Arrays.asList(StringHelper.readCommaSeperatedString(parameters)); - branchFunction.load(args); + List args = StringHelper.readCommaSeperatedString(parameters); + branchFunction.load(holder, args); return branchFunction; } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4EntityFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4EntityFunction.java index 0809077c7..d9c89852d 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4EntityFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4EntityFunction.java @@ -1,8 +1,8 @@ package com.pg85.otg.customobjects.bo4.bo4function; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.nio.ByteBuffer; import com.pg85.otg.customobjects.bo4.BO4Config; import com.pg85.otg.customobjects.bofunctions.EntityFunction; import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructureCoordinate; @@ -14,22 +14,15 @@ */ public class BO4EntityFunction extends EntityFunction { - public BO4EntityFunction() { } - - public BO4EntityFunction(BO4Config holder) - { - this.holder = holder; - } - public BO4EntityFunction rotate(Rotation rotation) { - BO4EntityFunction rotatedBlock = new BO4EntityFunction(this.getHolder()); + BO4EntityFunction rotatedBlock = new BO4EntityFunction(); - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x, y, z, rotation); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x(), y(), z(), rotation); - rotatedBlock.x = rotatedCoords.getX(); - rotatedBlock.y = rotatedCoords.getY(); - rotatedBlock.z = rotatedCoords.getZ(); + rotatedBlock.x(rotatedCoords.getX()); + rotatedBlock.y(rotatedCoords.getY()); + rotatedBlock.z(rotatedCoords.getZ()); rotatedBlock.name = name; rotatedBlock.resourceLocation = resourceLocation; @@ -51,14 +44,14 @@ public Class getHolderType() @Override public EntityFunction createNewInstance() { - return new BO4EntityFunction(this.getHolder()); + return new BO4EntityFunction(); } public void writeToStream(DataOutput stream) throws IOException { - stream.writeInt(this.x); - stream.writeInt(this.y); - stream.writeInt(this.z); + stream.writeInt(this.x()); + stream.writeInt(this.y()); + stream.writeInt(this.z()); StreamHelper.writeStringToStream(stream, this.resourceLocation); stream.writeInt(this.groupSize); @@ -66,21 +59,21 @@ public void writeToStream(DataOutput stream) throws IOException StreamHelper.writeStringToStream(stream, this.originalNameTagOrNBTFileName); } - public static BO4EntityFunction fromStream(BO4Config holder, ByteBuffer buffer) throws IOException + public static BO4EntityFunction fromStream(BO4Config holder, DataInput in) throws IOException { - BO4EntityFunction entityFunction = new BO4EntityFunction(holder); + BO4EntityFunction entityFunction = new BO4EntityFunction(); - entityFunction.x = buffer.getInt(); - entityFunction.y = buffer.getInt(); - entityFunction.z = buffer.getInt(); + entityFunction.x(in.readInt()); + entityFunction.y(in.readInt()); + entityFunction.z(in.readInt()); - entityFunction.processEntityName(StreamHelper.readStringFromBuffer(buffer)); - entityFunction.groupSize = buffer.getInt(); - entityFunction.nameTagOrNBTFileName= StreamHelper.readStringFromBuffer(buffer); - entityFunction.originalNameTagOrNBTFileName= StreamHelper.readStringFromBuffer(buffer); + entityFunction.processEntityName(StreamHelper.readStringFromStream(in)); + entityFunction.groupSize = in.readInt(); + entityFunction.nameTagOrNBTFileName= StreamHelper.readStringFromStream(in); + entityFunction.originalNameTagOrNBTFileName= StreamHelper.readStringFromStream(in); if (entityFunction.originalNameTagOrNBTFileName != null) { - entityFunction.processNameTagOrFileName(entityFunction.originalNameTagOrNBTFileName); + entityFunction.processNameTagOrFileName(holder, entityFunction.originalNameTagOrNBTFileName); } entityFunction.rotation = 0; diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ModDataFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ModDataFunction.java index d2233725c..25f109790 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ModDataFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ModDataFunction.java @@ -1,8 +1,9 @@ package com.pg85.otg.customobjects.bo4.bo4function; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.nio.ByteBuffer; + import com.pg85.otg.customobjects.bo4.BO4Config; import com.pg85.otg.customobjects.bofunctions.ModDataFunction; import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructureCoordinate; @@ -14,22 +15,28 @@ */ public class BO4ModDataFunction extends ModDataFunction { - public BO4ModDataFunction() { } - - public BO4ModDataFunction(BO4Config holder) - { - this.holder = holder; - } + public static BO4ModDataFunction read(DataInput in) throws IOException + { + BO4ModDataFunction modDataFunction = new BO4ModDataFunction(); + + modDataFunction.x(in.readInt()); + modDataFunction.y(in.readInt()); + modDataFunction.z(in.readInt()); + modDataFunction.modId = StreamHelper.readStringFromStream(in); + modDataFunction.modData = StreamHelper.readStringFromStream(in); + + return modDataFunction; + } public BO4ModDataFunction rotate(Rotation rotation) { - BO4ModDataFunction rotatedBlock = new BO4ModDataFunction(this.getHolder()); + BO4ModDataFunction rotatedBlock = new BO4ModDataFunction(); - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x, y, z, rotation); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x(), y(), z(), rotation); - rotatedBlock.x = rotatedCoords.getX(); - rotatedBlock.y = rotatedCoords.getY(); - rotatedBlock.z = rotatedCoords.getZ(); + rotatedBlock.x(rotatedCoords.getX()); + rotatedBlock.y(rotatedCoords.getY()); + rotatedBlock.z(rotatedCoords.getZ()); rotatedBlock.modId = modId; rotatedBlock.modData = modData; @@ -46,29 +53,29 @@ public Class getHolderType() @Override public ModDataFunction getNewInstance() { - return new BO4ModDataFunction(this.getHolder()); + return new BO4ModDataFunction(); } public void writeToStream(DataOutput stream) throws IOException { - stream.writeInt(this.x); - stream.writeInt(this.y); - stream.writeInt(this.z); + stream.writeInt(this.x()); + stream.writeInt(this.y()); + stream.writeInt(this.z()); StreamHelper.writeStringToStream(stream, this.modId); StreamHelper.writeStringToStream(stream, this.modData); } - public static BO4ModDataFunction fromStream(BO4Config holder, ByteBuffer buffer) throws IOException + public static BO4ModDataFunction fromStream(BO4Config holder, DataInput in) throws IOException { - BO4ModDataFunction modDataFunction = new BO4ModDataFunction(holder); + BO4ModDataFunction modDataFunction = new BO4ModDataFunction(); - modDataFunction.x = buffer.getInt(); - modDataFunction.y = buffer.getInt(); - modDataFunction.z = buffer.getInt(); + modDataFunction.x(in.readInt()); + modDataFunction.y(in.readInt()); + modDataFunction.z(in.readInt()); - modDataFunction.modId = StreamHelper.readStringFromBuffer(buffer); - modDataFunction.modData = StreamHelper.readStringFromBuffer(buffer); + modDataFunction.modId = StreamHelper.readStringFromStream(in); + modDataFunction.modData = StreamHelper.readStringFromStream(in); return modDataFunction; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ParticleFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ParticleFunction.java index d702f24d2..59d0ca3ac 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ParticleFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4ParticleFunction.java @@ -1,8 +1,9 @@ package com.pg85.otg.customobjects.bo4.bo4function; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.nio.ByteBuffer; + import com.pg85.otg.customobjects.bo4.BO4Config; import com.pg85.otg.customobjects.bofunctions.ParticleFunction; import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructureCoordinate; @@ -14,22 +15,34 @@ */ public class BO4ParticleFunction extends ParticleFunction { - public BO4ParticleFunction() { } - - public BO4ParticleFunction(BO4Config holder) - { - this.holder = holder; - } - + public static BO4ParticleFunction read(DataInput in) throws IOException + { + BO4ParticleFunction particleFunction = new BO4ParticleFunction(); + + particleFunction.x(in.readInt()); + particleFunction.y(in.readInt()); + particleFunction.z(in.readInt()); + particleFunction.particleName = StreamHelper.readStringFromStream(in); + particleFunction.interval = in.readDouble(); + particleFunction.velocityX = in.readDouble(); + particleFunction.velocityY = in.readDouble(); + particleFunction.velocityZ = in.readDouble(); + particleFunction.velocityXSet = in.readByte() != 0; + particleFunction.velocityYSet = in.readByte() != 0; + particleFunction.velocityZSet = in.readByte() != 0; + + return particleFunction; + } + public BO4ParticleFunction rotate(Rotation rotation) { - BO4ParticleFunction rotatedBlock = new BO4ParticleFunction(this.getHolder()); + BO4ParticleFunction rotatedBlock = new BO4ParticleFunction(); - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x, y, z, rotation); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x(), y(), z(), rotation); - rotatedBlock.x = rotatedCoords.getX(); - rotatedBlock.y = rotatedCoords.getY(); - rotatedBlock.z = rotatedCoords.getZ(); + rotatedBlock.x(rotatedCoords.getX()); + rotatedBlock.y(rotatedCoords.getY()); + rotatedBlock.z(rotatedCoords.getZ()); rotatedBlock.velocityX = velocityX; rotatedBlock.velocityY = velocityY; @@ -77,14 +90,14 @@ public Class getHolderType() @Override public ParticleFunction getNewInstance() { - return new BO4ParticleFunction(this.getHolder()); + return new BO4ParticleFunction(); } public void writeToStream(DataOutput stream) throws IOException { - stream.writeInt(this.x); - stream.writeInt(this.y); - stream.writeInt(this.z); + stream.writeInt(this.x()); + stream.writeInt(this.y()); + stream.writeInt(this.z()); stream.writeBoolean(this.firstSpawn); @@ -102,25 +115,25 @@ public void writeToStream(DataOutput stream) throws IOException stream.writeBoolean(this.velocityZSet); } - public static BO4ParticleFunction fromStream(BO4Config holder, ByteBuffer buffer) throws IOException + public static BO4ParticleFunction fromStream(BO4Config holder, DataInput in) throws IOException { - BO4ParticleFunction particleFunction = new BO4ParticleFunction(holder); + BO4ParticleFunction particleFunction = new BO4ParticleFunction(); - particleFunction.x = buffer.getInt(); - particleFunction.y = buffer.getInt(); - particleFunction.z = buffer.getInt(); + particleFunction.x(in.readInt()); + particleFunction.y(in.readInt()); + particleFunction.z(in.readInt()); - particleFunction.firstSpawn = buffer.get() != 0; - particleFunction.particleName = StreamHelper.readStringFromBuffer(buffer); - particleFunction.interval = buffer.getDouble(); - particleFunction.intervalOffset = buffer.getDouble(); - particleFunction.velocityX = buffer.getDouble(); - particleFunction.velocityY = buffer.getDouble(); - particleFunction.velocityZ = buffer.getDouble(); + particleFunction.firstSpawn = in.readByte() != 0; + particleFunction.particleName = StreamHelper.readStringFromStream(in); + particleFunction.interval = in.readDouble(); + particleFunction.intervalOffset = in.readDouble(); + particleFunction.velocityX = in.readDouble(); + particleFunction.velocityY = in.readDouble(); + particleFunction.velocityZ = in.readDouble(); - particleFunction.velocityXSet = buffer.get() != 0; - particleFunction.velocityYSet = buffer.get() != 0; - particleFunction.velocityZSet = buffer.get() != 0; + particleFunction.velocityXSet = in.readByte() != 0; + particleFunction.velocityYSet = in.readByte() != 0; + particleFunction.velocityZSet = in.readByte() != 0; return particleFunction; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4RandomBlockFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4RandomBlockFunction.java index 1732cf283..296a8ba61 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4RandomBlockFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4RandomBlockFunction.java @@ -1,164 +1,107 @@ package com.pg85.otg.customobjects.bo4.bo4function; +import java.io.DataInput; import java.io.DataOutput; -import java.io.File; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.List; import java.util.Random; +import com.pg85.otg.common.BlockContainer; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.customobjects.bo3.BO3Loader; import com.pg85.otg.customobjects.bo4.BO4Config; import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructureCoordinate; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.bo3.NamedBinaryTag; import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.helpers.StringHelper; import com.pg85.otg.util.materials.MaterialHelper; public class BO4RandomBlockFunction extends BO4BlockFunction { - public LocalMaterialData[] blocks; + public BlockContainer[] blockContainers; public byte[] blockChances; - public String[] metaDataNames; - public NamedBinaryTag[] metaDataTags; public byte blockCount = 0; - public BO4RandomBlockFunction() { } - - public BO4RandomBlockFunction(BO4Config holder) - { - super(holder); - } - @Override - public void load(List args) throws InvalidConfigException + public void load(BO4Config holder, List args) throws InvalidConfigException { assureSize(5, args); - x = readInt(args.get(0), -100, 100); - y = (short) readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); // Now read the random parts int i = 3; int size = args.size(); // Get number of blocks first, params can vary so can't just count. - while (i < size) - { - i++; - if (i >= size) + while(i < size) + { + i++; + if(i >= size) { throw new InvalidConfigException("Missing chance parameter"); } - try + if(!StringHelper.isNumber(args.get(i))) { - readInt(args.get(i), 1, 100); - } - catch (InvalidConfigException e) - { - // Get the chance i++; - if (i >= size) + if(i >= size) { throw new InvalidConfigException("Missing chance parameter"); } - readInt(args.get(i), 1, 100); + if(!StringHelper.isNumber(args.get(i))) + { + throw new NumberFormatException("'" + args.get(i) + "'" + " is not a number!"); + } } i++; blockCount++; } - this.blocks = new LocalMaterialData[blockCount]; + this.blockContainers = new BlockContainer[blockCount]; this.blockChances = new byte[blockCount]; - this.metaDataNames = new String[blockCount]; - this.metaDataTags = new NamedBinaryTag[blockCount]; i = 3; blockCount = 0; - while (i < size) + while(i < size) { // Parse chance and metadata - this.blocks[blockCount] = MaterialHelper.readMaterial(args.get(i)); + LocalMaterialData material = MaterialHelper.readMaterial(args.get(i)); i++; - if (i >= size) - { - throw new InvalidConfigException("Missing chance parameter"); - } - try + if(StringHelper.isNumber(args.get(i))) { + blockContainers[blockCount] = material.blockContainer(); blockChances[blockCount] = (byte) readInt(args.get(i), 1, 100); + i++; } - catch (InvalidConfigException e) + else { - // Maybe it's a NBT file? - - // Get the file - NamedBinaryTag metaData = BO3Loader.loadMetadata(args.get(i), this.getHolder().getFile()); - if (metaData != null) - { - metaDataNames[blockCount] = args.get(i); - metaDataTags[blockCount] = metaData; - } - - // Get the chance + blockContainers[blockCount] = material.blockContainer(holder.getFile().getParentFile().toPath(), args.get(i)); i++; - if (i >= size) - { - throw new InvalidConfigException("Missing chance parameter"); - } blockChances[blockCount] = (byte) readInt(args.get(i), 1, 100); + i++; } - - i++; blockCount++; } + blockContainer = blockContainers.length > 0 ? blockContainers[0] : BlockContainer.of(MaterialHelper.AIR); } public BO4RandomBlockFunction rotate(Rotation rotation) { - BO4RandomBlockFunction rotatedBlock = new BO4RandomBlockFunction(this.getHolder()); - - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x, y, z, rotation); - - rotatedBlock.x = rotatedCoords.getX(); - rotatedBlock.y = rotatedCoords.getY(); - rotatedBlock.z = rotatedCoords.getZ(); + BO4RandomBlockFunction rotatedBlock = new BO4RandomBlockFunction(); - rotatedBlock.blocks = blocks; - - // TODO: This makes no sense, why is rotation inverted??? Should be: NORTH:0,WEST:1,SOUTH:2,EAST:3 - LocalMaterialData[] rotatedBlockBlocks = new LocalMaterialData[blockCount]; - for (int a = 0; a < blockCount; a++) + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x(), y(), z(), rotation); + rotatedBlock.x(rotatedCoords.getX()); + rotatedBlock.y(rotatedCoords.getY()); + rotatedBlock.z(rotatedCoords.getZ()); + rotatedBlock.blockContainer = blockContainer.rotate(rotation); + rotatedBlock.blockContainers = new BlockContainer[blockCount]; + for(int i = 0; i < blockCount; i++) { - rotatedBlockBlocks[a] = rotatedBlock.blocks[a]; - - // Apply rotation - if(rotation.getRotationId() == 3) - { - rotatedBlockBlocks[a] = rotatedBlockBlocks[a].rotate(1); - } - if(rotation.getRotationId() == 2) - { - rotatedBlockBlocks[a] = rotatedBlockBlocks[a].rotate(2); - } - if(rotation.getRotationId() == 1) - { - rotatedBlockBlocks[a] = rotatedBlockBlocks[a].rotate(3); - } + rotatedBlock.blockContainers[i] = blockContainers[i].rotate(rotation); } - rotatedBlock.blocks = rotatedBlockBlocks; - - rotatedBlock.blockCount = blockCount; - rotatedBlock.blockChances = blockChances; - rotatedBlock.metaDataTag = metaDataTag; - rotatedBlock.metaDataTags = metaDataTags; - rotatedBlock.metaDataName = metaDataName; - rotatedBlock.metaDataNames = metaDataNames; - + rotatedBlock.blockCount = blockCount; + rotatedBlock.blockChances = blockChances; return rotatedBlock; } @@ -169,7 +112,7 @@ public void spawn(LocalWorld world, Random random, int x, int y, int z, ChunkCoo { if (random.nextInt(100) < blockChances[i]) { - world.setBlock(x, y, z, blocks[i], metaDataTags[i], chunkBeingPopulated, true); + world.setBlock(x, y, z, blockContainers[i].material(), blockContainers[i].tag(), chunkBeingPopulated, true); break; } } @@ -178,15 +121,15 @@ public void spawn(LocalWorld world, Random random, int x, int y, int z, ChunkCoo @Override public String makeString() { - String text = "RandomBlock(" + x + "," + y + "," + z; + String text = "RandomBlock(" + x() + "," + y() + "," + z(); for (int i = 0; i < blockCount; i++) { - if (metaDataTags[i] == null) + if (!blockContainers[i].hasTag()) { - text += "," + blocks[i] + "," + blockChances[i]; + text += "," + blockContainers[i].material() + "," + blockChances[i]; } else { - text += "," + blocks[i] + "," + metaDataNames[i] + "," + blockChances[i]; + text += "," + blockContainers[i].material() + "," + blockContainers[i].tagPath() + "," + blockChances[i]; } } return text + ")"; @@ -201,22 +144,22 @@ public Class getHolderType() @Override public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, DataOutput stream) throws IOException { - stream.writeShort(this.y); + stream.writeShort(this.y()); - stream.writeByte(this.blocks.length); + stream.writeByte(this.blockContainers.length); boolean bFound; - for(int i = 0; i < this.blocks.length; i++) + for(int i = 0; i < this.blockContainers.length; i++) { byte blockChance = this.blockChances[i]; stream.writeByte(blockChance); bFound = false; - if(this.blocks[i] != null) + if(this.blockContainers[i] != null) { for(int j = 0; j < materials.length; j++) { - if(materials[j].equals(this.blocks[i])) + if(materials[j].equals(this.blockContainers[i].material())) { stream.writeShort(j); bFound = true; @@ -231,9 +174,9 @@ public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, } boolean metaDataFound = false; - for(int i = 0; i < this.metaDataNames.length; i++) + for(int i = 0; i < this.blockContainers.length; i++) { - if(this.metaDataNames[i] != null) + if(this.blockContainers[i].hasTag()) { metaDataFound = true; break; @@ -242,15 +185,15 @@ public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, if(metaDataFound) { - stream.writeByte(this.blocks.length); - for(int i = 0; i < this.metaDataNames.length; i++) + stream.writeByte(this.blockContainers.length); + for(int i = 0; i < this.blockContainers.length; i++) { bFound = false; - if(this.metaDataNames[i] != null) + if(this.blockContainers[i].hasTag()) { for(int j = 0; j < metaDataNames.length; j++) { - if(metaDataNames[j].equals(this.metaDataNames[i])) + if(metaDataNames[j].equals(this.blockContainers[i].tagPath())) { stream.writeShort(i); bFound = true; @@ -268,55 +211,45 @@ public void writeToStream(String[] metaDataNames, LocalMaterialData[] materials, } } - public static BO4RandomBlockFunction fromStream(int x, int z, String[] metaDataNames, LocalMaterialData[] materials, BO4Config holder, ByteBuffer buffer) throws IOException + public static BO4RandomBlockFunction fromStream(int x, int z, String[] metaDataNames, LocalMaterialData[] materials, BO4Config holder, DataInput in) throws IOException { - BO4RandomBlockFunction rbf = new BO4RandomBlockFunction(holder); - - File file = holder.getFile(); + BO4RandomBlockFunction rbf = new BO4RandomBlockFunction(); - rbf.x = x; - rbf.y = buffer.getShort(); - rbf.z = z; + rbf.x(x); + rbf.y(in.readShort()); + rbf.z(z); - byte blocksLength = buffer.get(); + byte blocksLength = in.readByte(); rbf.blockCount = blocksLength; - rbf.blocks = new LocalMaterialData[blocksLength]; + rbf.blockContainers = new BlockContainer[blocksLength]; rbf.blockChances = new byte[blocksLength]; - rbf.metaDataNames = new String[blocksLength]; - rbf.metaDataTags = new NamedBinaryTag[blocksLength]; + LocalMaterialData[] blocks = new LocalMaterialData[blocksLength]; for(int i = 0; i < blocksLength; i++) { - rbf.blockChances[i] = buffer.get(); - short materialId = buffer.getShort(); + rbf.blockChances[i] = in.readByte(); + short materialId = in.readShort(); if(materialId != -1) { - rbf.blocks[i] = materials[materialId]; + blocks[i] = materials[materialId]; } } - blocksLength = buffer.get(); - for(int i = 0; i < blocksLength; i++) - { - short metaDataNameId = buffer.getShort(); - if(metaDataNameId != -1) - { - rbf.metaDataNames[i] = metaDataNames[metaDataNameId]; - } - if(rbf.metaDataNames[i] != null) - { - // Get the file - NamedBinaryTag metaData = BO3Loader.loadMetadata(rbf.metaDataNames[i], file); - - if (metaData != null) - { - rbf.metaDataTags[i] = metaData; - } else { - rbf.metaDataNames[i] = null; - } - } - } + boolean hasMetaDataTags = in.readByte() != -1; + for(int i = 0; i < blocksLength; i++) + { + short metaDataNameId; + String metaDataName; + if(hasMetaDataTags && (metaDataNameId = in.readShort()) != -1 && (metaDataName = metaDataNames[metaDataNameId]) != null) + { + rbf.blockContainers[i] = materials[i].blockContainer(holder.getFile().getParentFile().toPath(), metaDataName); + } + else + { + rbf.blockContainers[i] = materials[i].blockContainer(); + } + } return rbf; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4SpawnerFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4SpawnerFunction.java index 470f86840..a3f9539bc 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4SpawnerFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4SpawnerFunction.java @@ -1,8 +1,9 @@ package com.pg85.otg.customobjects.bo4.bo4function; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.nio.ByteBuffer; + import com.pg85.otg.customobjects.bo4.BO4Config; import com.pg85.otg.customobjects.bofunctions.SpawnerFunction; import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructureCoordinate; @@ -14,22 +15,42 @@ */ public class BO4SpawnerFunction extends SpawnerFunction { - public BO4SpawnerFunction() { } - - public BO4SpawnerFunction(BO4Config holder) - { - this.holder = holder; - } + public static BO4SpawnerFunction read(DataInput in) throws IOException + { + BO4SpawnerFunction spawnerFunction = new BO4SpawnerFunction(); + + spawnerFunction.x(in.readInt()); + spawnerFunction.y(in.readInt()); + spawnerFunction.z(in.readInt()); + spawnerFunction.mobName = StreamHelper.readStringFromStream(in); + spawnerFunction.originalnbtFileName = StreamHelper.readStringFromStream(in); + spawnerFunction.nbtFileName = StreamHelper.readStringFromStream(in); + spawnerFunction.groupSize = in.readInt(); + spawnerFunction.interval = in.readInt(); + spawnerFunction.spawnChance = in.readInt(); + spawnerFunction.maxCount = in.readInt(); + spawnerFunction.despawnTime = in.readInt(); + spawnerFunction.velocityX = in.readDouble(); + spawnerFunction.velocityY = in.readDouble(); + spawnerFunction.velocityZ = in.readDouble(); + spawnerFunction.velocityXSet = in.readByte() != 0; + spawnerFunction.velocityYSet = in.readByte() != 0; + spawnerFunction.velocityZSet = in.readByte() != 0; + spawnerFunction.yaw = in.readFloat(); + spawnerFunction.pitch = in.readFloat(); + + return spawnerFunction; + } public BO4SpawnerFunction rotate(Rotation rotation) { - BO4SpawnerFunction rotatedBlock = new BO4SpawnerFunction(this.getHolder()); + BO4SpawnerFunction rotatedBlock = new BO4SpawnerFunction(); - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x, y, z, rotation); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedBO3CoordsJustified(x(), y(), z(), rotation); - rotatedBlock.x = rotatedCoords.getX(); - rotatedBlock.y = rotatedCoords.getY(); - rotatedBlock.z = rotatedCoords.getZ(); + rotatedBlock.x(rotatedCoords.getX()); + rotatedBlock.y(rotatedCoords.getY()); + rotatedBlock.z(rotatedCoords.getZ()); rotatedBlock.velocityX = velocityX; rotatedBlock.velocityY = velocityY; @@ -88,14 +109,14 @@ public Class getHolderType() @Override public SpawnerFunction getNewInstance() { - return new BO4SpawnerFunction(this.getHolder()); + return new BO4SpawnerFunction(); } public void writeToStream(DataOutput stream) throws IOException { - stream.writeInt(this.x); - stream.writeInt(this.y); - stream.writeInt(this.z); + stream.writeInt(this.x()); + stream.writeInt(this.y()); + stream.writeInt(this.z()); stream.writeBoolean(this.firstSpawn); @@ -128,43 +149,43 @@ public void writeToStream(DataOutput stream) throws IOException stream.writeBoolean(this.metaDataProcessed); } - public static BO4SpawnerFunction fromStream(BO4Config holder, ByteBuffer buffer) throws IOException + public static BO4SpawnerFunction fromStream(BO4Config holder, DataInput in) throws IOException { - BO4SpawnerFunction spawnerFunction = new BO4SpawnerFunction(holder); + BO4SpawnerFunction spawnerFunction = new BO4SpawnerFunction(); - spawnerFunction.x = buffer.getInt(); - spawnerFunction.y = buffer.getInt(); - spawnerFunction.z = buffer.getInt(); + spawnerFunction.x(in.readInt()); + spawnerFunction.y(in.readInt()); + spawnerFunction.z(in.readInt()); - spawnerFunction.firstSpawn = buffer.get() != 0; + spawnerFunction.firstSpawn = in.readByte() != 0; - spawnerFunction.mobName = StreamHelper.readStringFromBuffer(buffer); + spawnerFunction.mobName = StreamHelper.readStringFromStream(in); - spawnerFunction.nbtFileName = StreamHelper.readStringFromBuffer(buffer); - spawnerFunction.originalnbtFileName = StreamHelper.readStringFromBuffer(buffer); - spawnerFunction.groupSize = buffer.getInt(); + spawnerFunction.nbtFileName = StreamHelper.readStringFromStream(in); + spawnerFunction.originalnbtFileName = StreamHelper.readStringFromStream(in); + spawnerFunction.groupSize = in.readInt(); - spawnerFunction.interval = buffer.getInt(); - spawnerFunction.intervalOffset = buffer.getInt(); + spawnerFunction.interval = in.readInt(); + spawnerFunction.intervalOffset = in.readInt(); - spawnerFunction.spawnChance = buffer.getInt(); - spawnerFunction.maxCount = buffer.getInt(); + spawnerFunction.spawnChance = in.readInt(); + spawnerFunction.maxCount = in.readInt(); - spawnerFunction.despawnTime = buffer.getInt(); + spawnerFunction.despawnTime = in.readInt(); - spawnerFunction.velocityX = buffer.getDouble(); - spawnerFunction.velocityY = buffer.getDouble(); - spawnerFunction.velocityZ = buffer.getDouble(); + spawnerFunction.velocityX = in.readDouble(); + spawnerFunction.velocityY = in.readDouble(); + spawnerFunction.velocityZ = in.readDouble(); - spawnerFunction.yaw = buffer.getFloat(); - spawnerFunction.pitch = buffer.getFloat(); + spawnerFunction.yaw = in.readFloat(); + spawnerFunction.pitch = in.readFloat(); - spawnerFunction.velocityXSet = buffer.get() != 0; - spawnerFunction.velocityYSet = buffer.get() != 0; - spawnerFunction.velocityZSet = buffer.get() != 0; + spawnerFunction.velocityXSet = in.readByte() != 0; + spawnerFunction.velocityYSet = in.readByte() != 0; + spawnerFunction.velocityZSet = in.readByte() != 0; - spawnerFunction.metaDataTag = StreamHelper.readStringFromBuffer(buffer); - spawnerFunction.metaDataProcessed = buffer.get() != 0; + spawnerFunction.metaDataTag = StreamHelper.readStringFromStream(in); + spawnerFunction.metaDataProcessed = in.readByte() != 0; return spawnerFunction; } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4WeightedBranchFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4WeightedBranchFunction.java index 4c62505e4..6776fbc13 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4WeightedBranchFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bo4/bo4function/BO4WeightedBranchFunction.java @@ -9,11 +9,10 @@ import com.pg85.otg.util.helpers.StreamHelper; import com.pg85.otg.util.helpers.StringHelper; +import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Random; @@ -21,13 +20,6 @@ public class BO4WeightedBranchFunction extends BO4BranchFunction { private double cumulativeChance = 0; - public BO4WeightedBranchFunction() { } - - public BO4WeightedBranchFunction(BO4Config holder) - { - super(holder); - } - @Override protected double readArgs(List args, boolean accumulateChances) throws InvalidConfigException { @@ -35,9 +27,7 @@ protected double readArgs(List args, boolean accumulateChances) throws I // assureSize only returns false if size() < size assureSize(8, args); - x = readInt(args.get(0), -32, 32); - y = readInt(args.get(1), -255, 255); - z = readInt(args.get(2), -32, 32); + readXYZ(args, 0); isRequiredBranch = readBoolean(args.get(3)); int i; @@ -81,7 +71,7 @@ protected double readArgs(List args, boolean accumulateChances) throws I } @Override - public void load(List args) throws InvalidConfigException + public void load(BO4Config holder, List args) throws InvalidConfigException { branchesOTGPlus = new ArrayList(); cumulativeChance = readArgs(args, true); @@ -107,9 +97,8 @@ public CustomStructureCoordinate toCustomObjectCoordinate(LocalWorld world, Rand double branchRarity = branch.getChance(); if (branchRarity > 0 && branchRarity >= randomChance) { - BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedCoord(this.x, this.y, this.z, rotation); - Rotation newRotation = Rotation.getRotation((rotation.getRotationId() + branch.getRotation().getRotationId()) % 4); - return new BO4CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, newRotation, x + rotatedCoords.getX(), (short)(y + rotatedCoords.getY()), z + rotatedCoords.getZ(), branch.branchDepth, branch.isRequiredBranch, true, branch.branchGroup); + BO4CustomStructureCoordinate rotatedCoords = BO4CustomStructureCoordinate.getRotatedCoord(this.x(), this.y(), this.z(), rotation); + return new BO4CustomStructureCoordinate(world, branch.getCustomObject(false, world), branch.customObjectName, branch.getRotation().next(rotation), x + rotatedCoords.getX(), (short)(y + rotatedCoords.getY()), z + rotatedCoords.getZ(), branch.branchDepth, branch.isRequiredBranch, true, branch.branchGroup); } randomChance -= branch.getChance(); if(randomChance < 0) @@ -122,11 +111,11 @@ public CustomStructureCoordinate toCustomObjectCoordinate(LocalWorld world, Rand public BO4WeightedBranchFunction rotate(Rotation rotation) { - BO4WeightedBranchFunction rotatedBranch = new BO4WeightedBranchFunction(this.getHolder()); + BO4WeightedBranchFunction rotatedBranch = new BO4WeightedBranchFunction(); - rotatedBranch.x = x; - rotatedBranch.y = y; - rotatedBranch.z = z; + rotatedBranch.x(x()); + rotatedBranch.y(y()); + rotatedBranch.z(z()); rotatedBranch.totalChance = totalChance; rotatedBranch.totalChanceSet = totalChanceSet; @@ -135,30 +124,24 @@ public BO4WeightedBranchFunction rotate(Rotation rotation) rotatedBranch.isRequiredBranch = isRequiredBranch; rotatedBranch.cumulativeChance = cumulativeChance; - rotatedBranch.holder = holder; - rotatedBranch.valid = valid; - rotatedBranch.inputName = inputName; - rotatedBranch.inputArgs = inputArgs; - rotatedBranch.error = error; - rotatedBranch.branchesOTGPlus = this.branchesOTGPlus; // TODO: Make sure this won't cause problems - int newX = rotatedBranch.x; - int newZ = rotatedBranch.z; + int newX = rotatedBranch.x(); + int newZ = rotatedBranch.z(); for(int i = 0; i < rotation.getRotationId(); i++) { - newX = rotatedBranch.z; - newZ = -rotatedBranch.x; + newX = rotatedBranch.z(); + newZ = -rotatedBranch.x(); - rotatedBranch.x = newX; - rotatedBranch.y = rotatedBranch.y; - rotatedBranch.z = newZ; + rotatedBranch.x(newX); + rotatedBranch.y(rotatedBranch.y()); + rotatedBranch.z(newZ); ArrayList rotatedBranchBranches = new ArrayList(); for (BO4BranchNode holder : rotatedBranch.branchesOTGPlus) { - rotatedBranchBranches.add(new BO4BranchNode(holder.branchDepth, holder.isRequiredBranch, holder.isWeightedBranch, holder.getRotation().next(), holder.getChance(), holder.getCustomObject(false, null), holder.customObjectName, holder.branchGroup)); + rotatedBranchBranches.add(new BO4BranchNode(holder.branchDepth, holder.isRequiredBranch, holder.isWeightedBranch, holder.getRotation().next(rotation), holder.getChance(), holder.getCustomObject(false, null), holder.customObjectName, holder.branchGroup)); } rotatedBranch.branchesOTGPlus = rotatedBranchBranches; } @@ -184,14 +167,14 @@ public void writeToStream(DataOutput stream) throws IOException StreamHelper.writeStringToStream(stream, makeString()); } - public static BO4WeightedBranchFunction fromStream(BO4Config holder, ByteBuffer buffer) throws IOException, InvalidConfigException + public static BO4WeightedBranchFunction fromStream(BO4Config holder, DataInput in) throws IOException, InvalidConfigException { - BO4WeightedBranchFunction branchFunction = new BO4WeightedBranchFunction(holder); - String configFunctionString = StreamHelper.readStringFromBuffer(buffer); + BO4WeightedBranchFunction branchFunction = new BO4WeightedBranchFunction(); + String configFunctionString = StreamHelper.readStringFromStream(in); int bracketIndex = configFunctionString.indexOf('('); String parameters = configFunctionString.substring(bracketIndex + 1, configFunctionString.length() - 1); - List args = Arrays.asList(StringHelper.readCommaSeperatedString(parameters)); - branchFunction.load(args); + List args = StringHelper.readCommaSeperatedString(parameters); + branchFunction.load(holder, args); return branchFunction; } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BlockFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BlockFunction.java index 847c16dc7..3e45707c4 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BlockFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BlockFunction.java @@ -3,11 +3,11 @@ import java.util.List; import java.util.Random; +import com.pg85.otg.common.BlockContainer; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; -import com.pg85.otg.customobjects.bo3.BO3Loader; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; import com.pg85.otg.util.bo3.NamedBinaryTag; @@ -17,45 +17,38 @@ */ public abstract class BlockFunction extends CustomObjectConfigFunction { - public LocalMaterialData material; - public short y; - public NamedBinaryTag metaDataTag; - public String metaDataName; + public BlockContainer blockContainer; @Override - public void load(List args) throws InvalidConfigException + public void load(T holder, List args) throws InvalidConfigException { assureSize(4, args); - // Those limits are arbitrary, LocalWorld.setBlock will limit it - // correctly based on what chunks can be accessed - x = readInt(args.get(0), -100, 100); - y = (short) readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); - material = readMaterial(args.get(3)); + LocalMaterialData material = readMaterial(args.get(3)); if(material == null) { throw new InvalidConfigException("Material \"" + args.get(3) + "\" could not be read."); } - if (args.size() == 5) + if(args.size() >= 5) { - metaDataTag = BO3Loader.loadMetadata(args.get(4), getHolder().getFile()); - if (metaDataTag != null) - { - metaDataName = args.get(4); - } + blockContainer = material.blockContainer(holder.getFile().getParentFile().toPath(), args.get(4)); + } + else + { + blockContainer = material.blockContainer(); } } @Override public String makeString() { - String start = "Block(" + x + ',' + y + ',' + z + ',' + material; - if (metaDataTag != null) + String start = "Block(" + x() + ',' + y() + ',' + z() + ',' + blockContainer.material(); + if (blockContainer.hasTag()) { - start += ',' + metaDataName; + start += ',' + blockContainer.tagPath(); } return start + ')'; } @@ -83,6 +76,26 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } BlockFunction block = (BlockFunction) other; - return block.x == x && block.y == y && block.z == z; + return block.x() == x() && block.y() == y() && block.z() == z(); + } + + public LocalMaterialData material() + { + return blockContainer.material(); + } + + public boolean hasTag() + { + return blockContainer.hasTag(); + } + + public NamedBinaryTag tag() + { + return blockContainer.tag(); + } + + public String tagPath() + { + return blockContainer.tagPath(); } } \ No newline at end of file diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BranchFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BranchFunction.java index 9692bfbce..0828aa864 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BranchFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/BranchFunction.java @@ -12,10 +12,6 @@ */ public abstract class BranchFunction extends CustomObjectConfigFunction implements Branch { - /** - * The base Y coordinate where this branch is expected to spawn - */ - protected int y; /** * holds each CustomObject, its spawn chance and its rotation as a node */ @@ -30,7 +26,7 @@ public abstract class BranchFunction extends C protected boolean totalChanceSet = false; @Override - public void load(List args) throws InvalidConfigException + public void load(T holder, List args) throws InvalidConfigException { branches = new TreeSet(); readArgs(args, false); @@ -41,9 +37,9 @@ public String makeString() { StringBuilder output = new StringBuilder(getConfigName()) .append('(') - .append(x).append(',') - .append(y).append(',') - .append(z); + .append(x()).append(',') + .append(y()).append(',') + .append(z()); for (Iterator it = branches.iterator(); it.hasNext();) { @@ -75,6 +71,6 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } BranchFunction branch = (BranchFunction) other; - return branch.x == x && branch.y == y && branch.z == z; + return branch.x() == x() && branch.y() == y() && branch.z() == z(); } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/EntityFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/EntityFunction.java index e9b804e56..d1d47a8e3 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/EntityFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/EntityFunction.java @@ -9,15 +9,15 @@ import com.pg85.otg.util.minecraft.defaults.EntityNames; import java.io.*; +import java.nio.file.NoSuchFileException; +import java.nio.file.Paths; import java.util.List; /** * Represents an entity in a BO3. */ -public abstract class EntityFunction extends CustomObjectConfigFunction +public abstract class EntityFunction extends ExtendedFunction { - public int y; - public String name = ""; public int groupSize = 1; public String nameTagOrNBTFileName = ""; @@ -27,20 +27,16 @@ public abstract class EntityFunction extends C public int rotation = 0; @Override - public void load(List args) throws InvalidConfigException + public void load(T holder, List args) throws InvalidConfigException { assureSize(5, args); - // Those limits are arbitrary, LocalWorld.setBlock will limit it - // correctly based on what chunks can be accessed - x = readInt(args.get(0), -100, 100); - y = readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); processEntityName(args.get(3)); groupSize = readInt(args.get(4), 0, Integer.MAX_VALUE); if(args.size() > 5) { - processNameTagOrFileName(args.get(5)); + processNameTagOrFileName(holder, args.get(5)); } } @@ -61,30 +57,31 @@ public void processEntityName(String name) { this.name = resourceLocation.split(":")[1]; } - public void processNameTagOrFileName(String s) { + public void processNameTagOrFileName(T holder, String s) { originalNameTagOrNBTFileName = s; if(originalNameTagOrNBTFileName != null && originalNameTagOrNBTFileName.toLowerCase().trim().endsWith(".txt")) { - nameTagOrNBTFileName = getHolder().getFile().getParentFile().getAbsolutePath() + File.separator + originalNameTagOrNBTFileName; + nameTagOrNBTFileName = holder.getFile().getParentFile().getAbsolutePath() + File.separator + originalNameTagOrNBTFileName; } else if(originalNameTagOrNBTFileName != null && originalNameTagOrNBTFileName.toLowerCase().trim().endsWith(".nbt")) { - nameTagOrNBTFileName = getHolder().getFile().getParentFile().getAbsolutePath() + File.separator + originalNameTagOrNBTFileName; + nameTagOrNBTFileName = holder.getFile().getParentFile().getAbsolutePath() + File.separator + originalNameTagOrNBTFileName; if (namedBinaryTag == null) { // load NBT data from .nbt file try { - FileInputStream stream = new FileInputStream(nameTagOrNBTFileName); - namedBinaryTag = NamedBinaryTag.readFrom(stream, true); - } catch (FileNotFoundException e) { + namedBinaryTag = NamedBinaryTag.readFrom(Paths.get(nameTagOrNBTFileName)); + } catch (NoSuchFileException e) { if(OTG.getPluginConfig().spawnLog) { OTG.log(LogMarker.WARN, "Could not find file: "+nameTagOrNBTFileName); } // Set it to null so we don't go looking for this later nameTagOrNBTFileName = null; - } catch (IOException e) { + } catch (IOException | InvalidConfigException e) { e.printStackTrace(); + // Set it to null so we don't go looking for this later + nameTagOrNBTFileName = null; } } @@ -99,7 +96,7 @@ else if(originalNameTagOrNBTFileName != null && originalNameTagOrNBTFileName.toL @Override public String makeString() { - return "Entity(" + x + ',' + y + ',' + z + ',' + resourceLocation + ',' + groupSize + (originalNameTagOrNBTFileName != null && originalNameTagOrNBTFileName.length() > 0 ? ',' + originalNameTagOrNBTFileName : "") + ')'; + return "Entity(" + x() + ',' + y() + ',' + z() + ',' + resourceLocation + ',' + groupSize + (originalNameTagOrNBTFileName != null && originalNameTagOrNBTFileName.length() > 0 ? ',' + originalNameTagOrNBTFileName : "") + ')'; } private String metaDataTag; @@ -145,7 +142,7 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } EntityFunction block = (EntityFunction) other; - return block.x == x && block.y == y && block.z == z && block.resourceLocation.equalsIgnoreCase(resourceLocation) && block.groupSize == groupSize && block.originalNameTagOrNBTFileName.equalsIgnoreCase(originalNameTagOrNBTFileName); + return block.x() == x() && block.y() == y() && block.z() == z() && block.resourceLocation.equalsIgnoreCase(resourceLocation) && block.groupSize == groupSize && block.originalNameTagOrNBTFileName.equalsIgnoreCase(originalNameTagOrNBTFileName); } public abstract EntityFunction createNewInstance(); diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ExtendedFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ExtendedFunction.java new file mode 100644 index 000000000..6422e7424 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ExtendedFunction.java @@ -0,0 +1,58 @@ +package com.pg85.otg.customobjects.bofunctions; + +import java.util.List; + +import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; +import com.pg85.otg.exception.InvalidConfigException; + +public abstract class ExtendedFunction extends CustomObjectConfigFunction +{ + private int x; + private int y; + private int z; + + @Override + public void readXYZ(List args, int index) throws InvalidConfigException + { + this.assureSize(index + 3, args); + x = this.readInt(args.get(index + 0), X_MIN, X_MAX); + y = this.readInt(args.get(index + 1), Y_MIN, Y_MAX); + z = this.readInt(args.get(index + 2), Z_MIN, Z_MAX); + } + + @Override + public void x(int x) + { + this.x = x; + } + + @Override + public int x() + { + return x; + } + + @Override + public void y(int y) + { + this.y = y; + } + + @Override + public int y() + { + return y; + } + + @Override + public void z(int z) + { + this.z = z; + } + + @Override + public int z() + { + return z; + } +} diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/MinecraftObjectFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/MinecraftObjectFunction.java index 0e509d731..0de4e7546 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/MinecraftObjectFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/MinecraftObjectFunction.java @@ -29,18 +29,14 @@ public MinecraftObjectFunction() public MinecraftObjectFunction(BO3Config config, List args) throws InvalidConfigException { assureSize(4, args); - // Those limits are arbitrary, LocalWorld.setBlock will limit it - // correctly based on what chunks can be accessed - x = readInt(args.get(0), -100, 100); - y = (short) readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); structurePart = DefaultStructurePart.getDefaultStructurePart(args.get(3)); } @Override public String makeString() { - return "MinecraftObject(" + x + ',' + y + ',' + z + ',' + structurePart + ')'; + return "MinecraftObject(" + x() + ',' + y() + ',' + z() + ',' + structurePart + ')'; } @Override @@ -59,6 +55,6 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } MinecraftObjectFunction block = (MinecraftObjectFunction) other; - return block.x == x && block.y == y && block.z == z; + return block.x() == x() && block.y() == y() && block.z() == z(); } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ModDataFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ModDataFunction.java index 1a3338df2..1c712724a 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ModDataFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ModDataFunction.java @@ -1,28 +1,36 @@ package com.pg85.otg.customobjects.bofunctions; +import java.io.DataOutput; +import java.io.IOException; +import java.util.List; + import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; import com.pg85.otg.exception.InvalidConfigException; -import java.util.List; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents a block in a BO3. */ -public abstract class ModDataFunction extends CustomObjectConfigFunction +public abstract class ModDataFunction extends ExtendedFunction { - public int y; public String modId; public String modData; - + + public static void write(DataOutput out, ModDataFunction modDataFunction) throws IOException + { + out.writeInt(modDataFunction.x()); + out.writeInt(modDataFunction.y()); + out.writeInt(modDataFunction.z()); + StreamHelper.writeStringToStream(out, modDataFunction.modId.replace(":", ":").replace(" ", " ")); + StreamHelper.writeStringToStream(out, modDataFunction.modData.replace(":", ":").replace(" ", " ")); + } + @Override - public void load(List args) throws InvalidConfigException + public void load(T holder, List args) throws InvalidConfigException { assureSize(5, args); - // Those limits are arbitrary, LocalWorld.setBlock will limit it - // correctly based on what chunks can be accessed - x = readInt(args.get(0), -100, 100); - y = readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); modId = args.get(3); modData = args.get(4); } @@ -30,7 +38,7 @@ public void load(List args) throws InvalidConfigException @Override public String makeString() { - return "ModData(" + x + ',' + y + ',' + z + ',' + modId + ',' + modData + ')'; + return "ModData(" + x() + ',' + y() + ',' + z() + ',' + modId + ',' + modData + ')'; } @Override @@ -41,7 +49,7 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } ModDataFunction block = (ModDataFunction) other; - return block.x == x && block.y == y && block.z == z && block.modId.equalsIgnoreCase(modId) && block.modData.equalsIgnoreCase(modData); + return block.x() == x() && block.y() == y() && block.z() == z() && block.modId.equalsIgnoreCase(modId) && block.modData.equalsIgnoreCase(modData); } public abstract ModDataFunction getNewInstance(); diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ParticleFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ParticleFunction.java index e315c6bf7..02241a043 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ParticleFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/ParticleFunction.java @@ -1,17 +1,19 @@ package com.pg85.otg.customobjects.bofunctions; +import java.io.DataOutput; +import java.io.IOException; +import java.util.List; + import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; import com.pg85.otg.exception.InvalidConfigException; -import java.util.List; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents a block in a BO3. */ -public abstract class ParticleFunction extends CustomObjectConfigFunction +public abstract class ParticleFunction extends ExtendedFunction { - public int y; - public Boolean firstSpawn = true; public String particleName = ""; @@ -27,15 +29,26 @@ public abstract class ParticleFunction extends public boolean velocityYSet = false; public boolean velocityZSet = false; + public static void write(DataOutput out, ParticleFunction particleFunction) throws IOException + { + out.writeInt(particleFunction.x()); + out.writeInt(particleFunction.y()); + out.writeInt(particleFunction.z()); + StreamHelper.writeStringToStream(out, particleFunction.particleName.replace(":", ":").replace(" ", " ")); + out.writeDouble(particleFunction.interval); + out.writeDouble(particleFunction.velocityX); + out.writeDouble(particleFunction.velocityY); + out.writeDouble(particleFunction.velocityZ); + out.writeBoolean(particleFunction.velocityXSet); + out.writeBoolean(particleFunction.velocityYSet); + out.writeBoolean(particleFunction.velocityZSet); + } + @Override - public void load(List args) throws InvalidConfigException + public void load(T holder, List args) throws InvalidConfigException { assureSize(5, args); - // Those limits are arbitrary, LocalWorld.setBlock will limit it - // correctly based on what chunks can be accessed - x = readInt(args.get(0), -100, 100); - y = readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); particleName = args.get(3); interval = readDouble(args.get(4), 0, Integer.MAX_VALUE); @@ -60,12 +73,12 @@ public void load(List args) throws InvalidConfigException @Override public String makeString() { - return "Particle(" + x + ',' + y + ',' + z + ',' + particleName + ',' + interval + ',' + velocityX + ',' + velocityY + ',' + velocityZ + ')'; + return "Particle(" + x() + ',' + y() + ',' + z() + ',' + particleName + ',' + interval + ',' + velocityX + ',' + velocityY + ',' + velocityZ + ')'; } public String makeStringForPacket() { - return "Particle(" + x + ',' + y + ',' + z + ',' + particleName + ',' + interval + ',' + velocityX + ',' + velocityY + ',' + velocityZ + ',' + velocityXSet + ',' + velocityYSet + ',' + velocityZSet + ')'; + return "Particle(" + x() + ',' + y() + ',' + z() + ',' + particleName + ',' + interval + ',' + velocityX + ',' + velocityY + ',' + velocityZ + ',' + velocityXSet + ',' + velocityYSet + ',' + velocityZSet + ')'; } @Override @@ -76,7 +89,7 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } ParticleFunction block = (ParticleFunction) other; - return block.x == x && block.y == y && block.z == z && block.particleName.equalsIgnoreCase(particleName) && block.interval == interval && block.velocityX == velocityX && block.velocityY == velocityY && block.velocityZ == velocityZ; + return block.x() == x() && block.y() == y() && block.z() == z() && block.particleName.equalsIgnoreCase(particleName) && block.interval == interval && block.velocityX == velocityX && block.velocityY == velocityY && block.velocityZ == velocityZ; } public abstract ParticleFunction getNewInstance(); diff --git a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/SpawnerFunction.java b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/SpawnerFunction.java index e8782d09a..69a88d2f4 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/bofunctions/SpawnerFunction.java +++ b/common/src/main/java/com/pg85/otg/customobjects/bofunctions/SpawnerFunction.java @@ -1,24 +1,25 @@ package com.pg85.otg.customobjects.bofunctions; -import com.pg85.otg.OTG; -import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; -import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; -import com.pg85.otg.exception.InvalidConfigException; -import com.pg85.otg.logging.LogMarker; import java.io.BufferedReader; +import java.io.DataOutput; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.List; +import com.pg85.otg.OTG; +import com.pg85.otg.configuration.customobjects.CustomObjectConfigFile; +import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; +import com.pg85.otg.exception.InvalidConfigException; +import com.pg85.otg.logging.LogMarker; +import com.pg85.otg.util.helpers.StreamHelper; + /** * Represents a block in a BO3. */ -public abstract class SpawnerFunction extends CustomObjectConfigFunction +public abstract class SpawnerFunction extends ExtendedFunction { - public int y; - public Boolean firstSpawn = true; public String mobName = ""; public String nbtFileName = ""; @@ -45,15 +46,34 @@ public abstract class SpawnerFunction extends protected String metaDataTag; protected boolean metaDataProcessed = false; + public static void write(DataOutput out, SpawnerFunction spawnerFunction) throws IOException + { + out.writeInt(spawnerFunction.x()); + out.writeInt(spawnerFunction.y()); + out.writeInt(spawnerFunction.z()); + StreamHelper.writeStringToStream(out, spawnerFunction.mobName.replace(":", ":").replace(" ", " ")); + StreamHelper.writeStringToStream(out, spawnerFunction.originalnbtFileName.replace(":", ":").replace(" ", " ")); + StreamHelper.writeStringToStream(out, spawnerFunction.nbtFileName.replace(":", ":").replace(" ", " ")); + out.writeInt(spawnerFunction.groupSize); + out.writeInt(spawnerFunction.interval); + out.writeInt(spawnerFunction.spawnChance); + out.writeInt(spawnerFunction.maxCount); + out.writeInt(spawnerFunction.despawnTime); + out.writeDouble(spawnerFunction.velocityX); + out.writeDouble(spawnerFunction.velocityY); + out.writeDouble(spawnerFunction.velocityZ); + out.writeBoolean(spawnerFunction.velocityXSet); + out.writeBoolean(spawnerFunction.velocityYSet); + out.writeBoolean(spawnerFunction.velocityZSet); + out.writeFloat(spawnerFunction.yaw); + out.writeFloat(spawnerFunction.pitch); + } + @Override - public void load(List args) throws InvalidConfigException + public void load(T holder, List args) throws InvalidConfigException { assureSize(8, args); - // Those limits are arbitrary, LocalWorld.setBlock will limit it - // correctly based on what chunks can be accessed - x = readInt(args.get(0), -100, 100); - y = readInt(args.get(1), -1000, 1000); - z = readInt(args.get(2), -100, 100); + readXYZ(args, 0); mobName = args.get(3); boolean param4isNBT = false; @@ -70,7 +90,7 @@ public void load(List args) throws InvalidConfigException if(originalnbtFileName != null && originalnbtFileName.toLowerCase().trim().endsWith(".txt")) { - nbtFileName = getHolder().getFile().getParentFile().getAbsolutePath() + File.separator + originalnbtFileName; + nbtFileName = holder.getFile().getParentFile().getAbsolutePath() + File.separator + originalnbtFileName; } if(param4isNBT) @@ -223,7 +243,7 @@ public String getMetaData() @Override public String makeString() { - return "Spawner(" + x + ',' + y + ',' + z + ',' + mobName + (originalnbtFileName != null && originalnbtFileName.length() > 0 ? "," + originalnbtFileName : "") + ',' + groupSize + ',' + interval + ',' + spawnChance + ',' + maxCount + ',' + despawnTime + ',' + velocityX + ',' + velocityY + ',' + velocityZ + ',' + yaw + ',' + pitch + ')'; + return "Spawner(" + x() + ',' + y() + ',' + z() + ',' + mobName + (originalnbtFileName != null && originalnbtFileName.length() > 0 ? "," + originalnbtFileName : "") + ',' + groupSize + ',' + interval + ',' + spawnChance + ',' + maxCount + ',' + despawnTime + ',' + velocityX + ',' + velocityY + ',' + velocityZ + ',' + yaw + ',' + pitch + ')'; } @Override @@ -234,7 +254,7 @@ public boolean isAnalogousTo(CustomObjectConfigFunction other) return false; } SpawnerFunction block = (SpawnerFunction) other; - return block.x == x && block.y == y && block.z == z && block.mobName.equalsIgnoreCase(mobName) && block.originalnbtFileName.equalsIgnoreCase(originalnbtFileName) && block.groupSize == groupSize && block.interval == interval && block.spawnChance == spawnChance && block.maxCount == maxCount && block.despawnTime == despawnTime && block.velocityX == velocityX && block.velocityY == velocityY && block.velocityZ == velocityZ && block.yaw == yaw && block.pitch == pitch; + return block.x() == x() && block.y() == y() && block.z() == z() && block.mobName.equalsIgnoreCase(mobName) && block.originalnbtFileName.equalsIgnoreCase(originalnbtFileName) && block.groupSize == groupSize && block.interval == interval && block.spawnChance == spawnChance && block.maxCount == maxCount && block.despawnTime == despawnTime && block.velocityX == velocityX && block.velocityY == velocityY && block.velocityZ == velocityZ && block.yaw == yaw && block.pitch == pitch; } public abstract SpawnerFunction getNewInstance(); diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructure.java b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructure.java index 9a8331bf8..db29f6c10 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructure.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructure.java @@ -1,6 +1,9 @@ package com.pg85.otg.customobjects.structures; import com.pg85.otg.customobjects.CustomObject; +import com.pg85.otg.customobjects.bofunctions.ModDataFunction; +import com.pg85.otg.customobjects.bofunctions.ParticleFunction; +import com.pg85.otg.customobjects.bofunctions.SpawnerFunction; import com.pg85.otg.util.ChunkCoordinate; import java.util.*; @@ -15,46 +18,56 @@ public abstract class CustomStructure { // The origin BO3 for this branching structure public CustomStructureCoordinate start; - + public EntitiesManager entitiesManager = new EntitiesManager(); - public ParticlesManager particlesManager = new ParticlesManager(); - public ModDataManager modDataManager = new ModDataManager(); - public SpawnerManager spawnerManager = new SpawnerManager(); - + public ParticlesManager particlesManager = new ParticlesManager(); + public ModDataManager modDataManager = new ModDataManager(); + public SpawnerManager spawnerManager = new SpawnerManager(); + protected Map> objectsToSpawn; protected Random random; - - protected CustomStructure() { } - + @Override - public boolean equals(Object other) + public boolean equals(Object obj) { - return - other != null && - ( - this == other || - ( - other instanceof CustomStructure && - this.start != null && - ((CustomStructure)other).start != null && - ((CustomStructure)other).start.bo3Name.equals(this.start.bo3Name) && - ((CustomStructure)other).start.getX() == this.start.getX() && - ((CustomStructure)other).start.getY() == this.start.getY() && - ((CustomStructure)other).start.getZ() == this.start.getZ() - ) - ) - ; + if(obj == this) + { + return true; + } + if(!(obj instanceof CustomStructure)) + { + return false; + } + CustomStructure other = (CustomStructure) obj; + return Objects.equals(this.start.bo3Name, other.start.bo3Name) + && this.start.getX() == other.start.getX() + && this.start.getY() == other.start.getY() + && this.start.getZ() == other.start.getZ(); } - + @Override public int hashCode() - { - final int prime = 31; - int result = 1; - result = prime * result + ((this.start.bo3Name == null) ? 0 : this.start.bo3Name.hashCode()); - result = prime * result + (int) (this.start.getX() ^ (this.start.getX() >>> 32)); - result = prime * result + (int) (this.start.getY() ^ (this.start.getY() >>> 32)); - result = prime * result + (int) (this.start.getZ() ^ (this.start.getZ() >>> 32)); - return result; + { + int result = 1; + result = 31 * result + Objects.hash(this.start.bo3Name); + result = 31 * result + this.start.getX(); + result = 31 * result + this.start.getY(); + result = 31 * result + this.start.getZ(); + return result; + } + + public HashSet> getModData() + { + return modDataManager.modData; + } + + public HashSet> getSpawnerData() + { + return spawnerManager.spawnerData; + } + + public HashSet> getParticleData() + { + return particlesManager.particleData; } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCache.java b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCache.java index d2ce88882..b27a6b652 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCache.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCache.java @@ -14,13 +14,15 @@ import com.pg85.otg.generator.resource.CustomStructureGen; import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; import com.pg85.otg.util.helpers.RandomHelper; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import java.util.Random; // TODO: spawners/particles/moddata for customobjects also use this, so not just structures. refactor? @@ -32,7 +34,7 @@ public class CustomStructureCache public static final int REGION_SIZE = 100; // BO3 - private FifoMap bo3StructureCache; + private LRUCache bo3StructureCache; // BO4 @@ -56,7 +58,7 @@ public CustomStructureCache(LocalWorld world) this.world = world; this.worldInfoChunks = new HashMap(); this.plotter = new CustomStructurePlotter(); - this.bo3StructureCache = new FifoMap(400); + this.bo3StructureCache = new LRUCache(400); loadStructureCache(); } @@ -235,41 +237,36 @@ public ChunkCoordinate plotBo4Structure(BO4 structure, ArrayList biomes, public void saveToDisk() { - OTG.log(LogMarker.INFO, "Saving structure and pregenerator data."); - boolean firstLog = false; - long starTime = System.currentTimeMillis(); - while(true) - { - // TODO: Make this prettier - synchronized(this.world.getObjectSpawner().lockingObject) - { - if(!this.world.getObjectSpawner().populating) - { - this.world.getObjectSpawner().saving = true; - break; - } - } - if(firstLog) - { - OTG.log(LogMarker.WARN, "SaveToDisk waiting on Populate. Although other mods could be causing this and there may not be any problem, this can potentially cause an endless loop!"); - firstLog = false; - } - int interval = 300; - if(System.currentTimeMillis() - starTime > (interval * 1000)) - { - OTG.log(LogMarker.FATAL, "SaveToDisk waited on populate longer than " + interval + " seconds, something went wrong!"); - throw new RuntimeException("SaveToDisk waited on populate longer than " + interval + " seconds, something went wrong!"); - } - } + OTG.log(LogMarker.INFO, "Saving structure and pregenerator data."); - saveStructureCache(); + int maxWaitTime = 30; + try + { + if(this.world.getObjectSpawner().lock.tryLock(maxWaitTime, TimeUnit.SECONDS)) + { + try + { + saveStructureCache(); + this.world.getObjectSpawner().saveRequired = false; + } + finally + { + this.world.getObjectSpawner().lock.unlock(); + } + } + else + { + OTG.log(LogMarker.FATAL, "SaveToDisk waited on populate longer than " + maxWaitTime + " seconds, something went wrong!"); + throw new RuntimeException("SaveToDisk waited on populate longer than " + maxWaitTime + " seconds, something went wrong!"); + } + } + catch(InterruptedException e) + { + Thread.currentThread().interrupt(); + throw new RuntimeException("SaveToDisk interrupted while trying to acquiring lock.", e); + } - synchronized(this.world.getObjectSpawner().lockingObject) - { - this.world.getObjectSpawner().saveRequired = false; - this.world.getObjectSpawner().saving = false; - } - OTG.log(LogMarker.INFO, "Structure and pregenerator data saved."); + OTG.log(LogMarker.INFO, "Structure and pregenerator data saved."); } private void saveStructureCache() @@ -288,7 +285,7 @@ private void loadStructureCache() this.worldInfoChunks = new HashMap(); - Map> loadedStructures = CustomStructureFileManager.loadStructureData(this.world); + Map> loadedStructures = CustomStructureFileManager.loadStructureData(this.world); if(loadedStructures != null) { if(this.world.isBo4Enabled()) @@ -296,7 +293,7 @@ private void loadStructureCache() this.plotter.loadStructureCache(this.world, loadedStructures); } - for(Entry> loadedStructure : loadedStructures.entrySet()) + for(Entry> loadedStructure : loadedStructures.entrySet()) { if(loadedStructure == null) { @@ -310,17 +307,17 @@ private void loadStructureCache() for(ModDataFunction modDataFunc : loadedStructure.getKey().modDataManager.modData) { - addToWorldInfoChunks(loadedStructure.getKey(), ChunkCoordinate.fromBlockCoords(modDataFunc.x, modDataFunc.z), false); + addToWorldInfoChunks(loadedStructure.getKey(), ChunkCoordinate.fromBlockCoords(modDataFunc.x(), modDataFunc.z()), false); } for(SpawnerFunction spawnerFunc : loadedStructure.getKey().spawnerManager.spawnerData) { - addToWorldInfoChunks(loadedStructure.getKey(), ChunkCoordinate.fromBlockCoords(spawnerFunc.x, spawnerFunc.z), false); + addToWorldInfoChunks(loadedStructure.getKey(), ChunkCoordinate.fromBlockCoords(spawnerFunc.x(), spawnerFunc.z()), false); } for(ParticleFunction particleFunc : loadedStructure.getKey().particlesManager.particleData) { - addToWorldInfoChunks(loadedStructure.getKey(), ChunkCoordinate.fromBlockCoords(particleFunc.x, particleFunc.z), false); + addToWorldInfoChunks(loadedStructure.getKey(), ChunkCoordinate.fromBlockCoords(particleFunc.x(), particleFunc.z()), false); } } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCoordinate.java b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCoordinate.java index 9fdacd511..22da2fa0b 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCoordinate.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureCoordinate.java @@ -1,10 +1,13 @@ package com.pg85.otg.customobjects.structures; +import java.io.DataOutput; +import java.io.IOException; + import com.pg85.otg.OTG; +import com.pg85.otg.customobjects.CustomObject; import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.bo3.Rotation; -import com.pg85.otg.util.helpers.MathHelper; -import com.pg85.otg.customobjects.CustomObject; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents an object along with its location in the world. @@ -17,17 +20,26 @@ public abstract class CustomStructureCoordinate protected transient StructuredCustomObject object; public Rotation rotation; public int x; - public short y; + public int y; public int z; protected CustomStructureCoordinate() { } + + public static void write(DataOutput out, CustomStructureCoordinate structureCoordinate) throws IOException + { + StreamHelper.writeStringToStream(out, structureCoordinate.bo3Name); + out.writeInt(structureCoordinate.rotation.getRotationId()); + out.writeInt(structureCoordinate.getX()); + out.writeInt(structureCoordinate.getY()); + out.writeInt(structureCoordinate.getZ()); + } public int getX() { return x; } - public short getY() + public int getY() { return y; } @@ -44,12 +56,12 @@ public Rotation getRotation() public final int getChunkX() { - return (int)MathHelper.floor(x / (double)16); + return x >> 4; } public final int getChunkZ() { - return (int)MathHelper.floor(z / (double)16); + return z >> 4; } /** @@ -83,7 +95,13 @@ public StructuredCustomObject getObject() @Override public int hashCode() { - return (x >> 13) ^ (y >> 7) ^ z ^ object.getName().hashCode() ^ rotation.toString().hashCode(); + int result = 1; + result = 31 * result + object.getName().hashCode(); + result = 31 * result + rotation.hashCode(); + result = 31 * result + x; + result = 31 * result + y; + result = 31 * result + z; + return result; } @Override diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureFileManager.java b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureFileManager.java index ff7c019b4..2fe28901b 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureFileManager.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/CustomStructureFileManager.java @@ -1,21 +1,28 @@ package com.pg85.otg.customobjects.structures; -import java.io.ByteArrayOutputStream; +import static com.pg85.otg.configuration.standard.WorldStandardValues.StructureDataBackupFileExtension; +import static com.pg85.otg.configuration.standard.WorldStandardValues.StructureDataFileExtension; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutput; import java.io.DataOutputStream; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; +import java.nio.file.Path; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; -import java.util.Stack; import java.util.Map.Entry; +import java.util.Set; +import java.util.Stack; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; import com.pg85.otg.OTG; import com.pg85.otg.common.LocalWorld; @@ -38,1343 +45,313 @@ import com.pg85.otg.customobjects.structures.bo4.smoothing.SmoothingAreaLine; import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.ChunkCoordinate; +import com.pg85.otg.util.DataUtil; +import com.pg85.otg.util.DataUtil.IOBiConsumer; +import com.pg85.otg.util.DataUtil.IOConsumer; import com.pg85.otg.util.bo3.Rotation; -import com.pg85.otg.util.helpers.MathHelper; import com.pg85.otg.util.helpers.StreamHelper; public class CustomStructureFileManager { - // Plotted chunks - - public static void savePlottedChunksData(LocalWorld world, Map populatedChunks) - { - int dimensionId = world.getDimensionId(); + private static Path worldDataDir(LocalWorld world) + { + Path dir = world.getWorldSaveDir().toPath().resolve(PluginStandardValues.PLUGIN_NAME); + return world.getDimensionId() != 0 ? dir.resolve("DIM-" + world.getDimensionId()) : dir; + } - int regionsSaved = 0; - if(populatedChunks.size() > 0) - { - for(Entry chunkPerRegionEntry : populatedChunks.entrySet()) - { - if(!chunkPerRegionEntry.getValue().requiresSave()) - { - continue; - } - chunkPerRegionEntry.getValue().markSaved(); - regionsSaved++; - - File occupiedChunksFile = new File( - world.getWorldSaveDir().getAbsolutePath() + File.separator + - PluginStandardValues.PLUGIN_NAME + File.separator + - (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + - WorldStandardValues.PlottedChunksDataFolderName + File.separator + - chunkPerRegionEntry.getKey().getChunkX() + "_" + - chunkPerRegionEntry.getKey().getChunkZ() + - WorldStandardValues.StructureDataFileExtension - ); - File occupiedChunksBackupFile = new File( - world.getWorldSaveDir().getAbsolutePath() + File.separator + - PluginStandardValues.PLUGIN_NAME + File.separator + - (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + - WorldStandardValues.PlottedChunksDataFolderName + File.separator + - chunkPerRegionEntry.getKey().getChunkX() + "_" + - chunkPerRegionEntry.getKey().getChunkZ() + - WorldStandardValues.StructureDataBackupFileExtension - ); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - - boolean[][] entriesByStructureName = chunkPerRegionEntry.getValue().getArray(); - try - { - int version = 1; - dos.writeInt(version); - dos.writeInt(CustomStructureCache.REGION_SIZE); + private static void write(LocalWorld world, String path, IOConsumer writer) + { + Path dir = worldDataDir(world); + Path file = dir.resolve(path + StructureDataFileExtension); + Path backup = dir.resolve(path + StructureDataBackupFileExtension); + DataUtil.writeCompressed(file, backup, writer); + } - for(int x = 0; x < CustomStructureCache.REGION_SIZE; x++) - { - boolean[] structureArr = entriesByStructureName[x]; - for(int z = 0; z < CustomStructureCache.REGION_SIZE; z++) - { - dos.writeBoolean(structureArr[z]); - } - } - } catch (IOException e1) { - e1.printStackTrace(); - return; - } + private static void read(LocalWorld world, String path, IOConsumer reader) + { + Path dir = worldDataDir(world); + Path file = dir.resolve(path + StructureDataFileExtension); + Path backup = dir.resolve(path + StructureDataBackupFileExtension); + DataUtil.readCompressed(file, backup, reader); + } - DataOutputStream dos2 = null; - FileOutputStream fos = null; - try { - if(!occupiedChunksFile.exists()) - { - occupiedChunksFile.getParentFile().mkdirs(); - } else { - Files.move(occupiedChunksFile.toPath(), occupiedChunksBackupFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - byte[] compressedBytes = com.pg85.otg.util.CompressionUtils.compress(bos.toByteArray()); - dos.close(); - fos = new FileOutputStream(occupiedChunksFile); - dos2 = new DataOutputStream(fos); - dos2.write(compressedBytes, 0, compressedBytes.length); - } - catch (IOException e) - { - e.printStackTrace(); - OTG.log(LogMarker.INFO, "OTG encountered an error writing " + occupiedChunksFile.getAbsolutePath() + ", skipping."); - } finally { - try { - if(dos != null) - { - dos.close(); - } - } catch (Exception e) { } - try { - if(dos2 != null) - { - dos2.close(); - } - } catch (Exception e) { } - try { - if(fos != null) - { - fos.close(); - } - } catch (Exception e) { } - } - } - } - - OTG.log(LogMarker.INFO, regionsSaved + " plotted chunk regions saved."); + private static void readAll(LocalWorld world, String path, IOBiConsumer reader) + { + DataUtil.readCompressed(worldDataDir(world), StructureDataFileExtension, StructureDataBackupFileExtension, reader); } - - public static Map loadPlottedChunksData(LocalWorld world) - { - int dimensionId = world.getDimensionId(); - - HashMap output = new HashMap(); - - File occupiedChunksFolder = new File( - world.getWorldSaveDir().getAbsolutePath() + File.separator + - PluginStandardValues.PLUGIN_NAME + File.separator + - (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + - WorldStandardValues.PlottedChunksDataFolderName + File.separator - ); - - HashMap saveFiles = new HashMap(); - ArrayList mainFiles = new ArrayList(); - ArrayList backupFiles = new ArrayList(); - if(occupiedChunksFolder.exists()) - { - for(File file : occupiedChunksFolder.listFiles()) - { - if( - file.getPath().endsWith(WorldStandardValues.StructureDataFileExtension) && - file.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_").length == 2 && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_")[0]) && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_")[1]) - ) { - mainFiles.add(file); - } - else if( - file.getPath().endsWith(WorldStandardValues.StructureDataBackupFileExtension) && - file.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_").length == 2 && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_")[0]) && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_")[1]) - ) - { - backupFiles.add(file); - } - } - for(File file : mainFiles) - { - boolean bFound = false; - for(File backupFile : backupFiles) - { - if(file.getPath().replace(WorldStandardValues.StructureDataFileExtension, "").equals(backupFile.getPath().replace(WorldStandardValues.StructureDataBackupFileExtension, ""))) - { - saveFiles.put(file, backupFile); - bFound = true; - break; - } - } - if(!bFound) - { - saveFiles.put(file, null); - } - } - } - - for(Entry saveFile : saveFiles.entrySet()) - { - boolean bSuccess = false; - File occupiedChunksFile = saveFile.getKey(); - File occupiedChunksBackupFile = saveFile.getValue(); - - if( - (occupiedChunksFile == null || !occupiedChunksFile.exists()) && - (occupiedChunksBackupFile == null || !occupiedChunksBackupFile.exists()) - ) - { - continue; - } + private static String toFileName(ChunkCoordinate chunkCoordinate) + { + return chunkCoordinate.getChunkX() + "_" + chunkCoordinate.getChunkZ(); + } - ChunkCoordinate regionCoord = null; - - if(occupiedChunksFile != null && occupiedChunksFile.exists()) - { - FileInputStream fis = null; - PlottedChunksRegion result = null; - try { - String[] chunkCoords = occupiedChunksFile.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_"); - int regionX = Integer.parseInt(chunkCoords[0]); - int regionZ = Integer.parseInt(chunkCoords[1]); - regionCoord = ChunkCoordinate.fromChunkCoords(regionX, regionZ); - - fis = new FileInputStream(occupiedChunksFile); - ByteBuffer buffer = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - buffer.get(compressedBytes); - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - buffer = ByteBuffer.wrap(decompressedBytes); - result = parsePlottedChunksFileFromStream(buffer, world); - } - catch (Exception ex) - { - ex.printStackTrace(); - OTG.log(LogMarker.INFO, "Failed to load " + occupiedChunksFile.getAbsolutePath() + ", trying to load backup."); - } finally { - if(fis != null) - { - try { - fis.getChannel().close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - if(result != null) - { - bSuccess = true; - output.put(regionCoord, result); - } - } - - if(!bSuccess && occupiedChunksBackupFile != null && occupiedChunksBackupFile.exists()) - { - FileInputStream fis = null; - PlottedChunksRegion result = null; - try { - String[] chunkCoords = occupiedChunksFile.getName().replace(WorldStandardValues.StructureDataBackupFileExtension, "").split("_"); - int regionX = Integer.parseInt(chunkCoords[0]); - int regionZ = Integer.parseInt(chunkCoords[1]); - regionCoord = ChunkCoordinate.fromChunkCoords(regionX, regionZ); - - fis = new FileInputStream(occupiedChunksBackupFile); - ByteBuffer buffer = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - buffer.get(compressedBytes); - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - buffer = ByteBuffer.wrap(decompressedBytes); - result = parsePlottedChunksFileFromStream(buffer, world); - } - catch (Exception ex) - { - ex.printStackTrace(); - } finally { - if(fis != null) - { - try { - fis.getChannel().close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - if(result != null) - { - bSuccess = true; - output.put(regionCoord, result); - } - } - - if(!bSuccess) - { - if(regionCoord != null) - { - output.put(regionCoord, PlottedChunksRegion.getFilledRegion()); - OTG.log(LogMarker.INFO, - "OTG encountered an error loading " + occupiedChunksFile.getAbsolutePath() + " and could not load a backup, substituting a default filled region. " - + "This may result in areas with missing BO4's, smoothing areas, /otg structure info and spawners/particles/moddata." - ); - } else { - throw new RuntimeException( - "OTG encountered a critical error loading " + occupiedChunksFile.getAbsolutePath() + " and could not load a backup, exiting. " - + "OTG automatically backs up files before writing and will try to use the backup when loading. " - + "If your dimension's structure data files and backups have been corrupted, you can delete them," - + "at the risk of losing data for unspawned structure parts." - ); - } - } - } - - return output.size() > 0 ? output : null; - } - - private static PlottedChunksRegion parsePlottedChunksFileFromStream(ByteBuffer buffer, LocalWorld world) throws IOException - { - int version = buffer.getInt(); - int regionSize = buffer.getInt(); - boolean[][] chunksMatrix = new boolean[CustomStructureCache.REGION_SIZE][CustomStructureCache.REGION_SIZE]; - if(regionSize == CustomStructureCache.REGION_SIZE) - { - for(int x = 0; x < regionSize; x++) - { - for(int z = 0; z < regionSize; z++) - { - chunksMatrix[x][z] = buffer.get() != 0; - } - } - } else { - OTG.log(LogMarker.INFO, "PlottedChunks region files were corrupted or exported with an incompatible version of OTG, ignoring."); - return PlottedChunksRegion.getFilledRegion(); - } - return new PlottedChunksRegion(chunksMatrix); - } - - - // Structure cache + private static ChunkCoordinate parseChunkCoordinate(Path file) throws IllegalArgumentException + { + try + { + String s = file.getFileName().toString(); + s = StringUtils.removeEnd(s, StructureDataFileExtension); + s = StringUtils.removeEnd(s, StructureDataBackupFileExtension); + int i = s.indexOf('_'); + if(i < 0) + { + throw new IllegalArgumentException("File name \"" + file.getFileName() + "\" cannot be converted to chunk coordinate. Missing underscore (_) delimiter."); + } + + int x = Integer.parseInt(s.substring(0, i)); + int z = Integer.parseInt(s.substring(i + 1)); + return ChunkCoordinate.fromChunkCoords(x, z); + } + catch(NumberFormatException e) + { + throw new IllegalArgumentException("File name \"" + file.getFileName() + "\" cannot be converted to chunk coordinate.", e); + } + } - // TODO: Since we're using regions, use short/byte for (internal) coords? - static void saveStructureData(Map worldInfoChunks, LocalWorld world) - { - int dimensionId = world.getDimensionId(); - - // Collect all structure start points (and chunks that have bo3's with spawners/moddata/particles in them) - // and group them by BO name (or "NULL" for bo3's with spawners/moddata/particles). - // Structure starts are saved per region, if a BO4 structure has chunk data in multiple regions, each region gets - // its own BO4CustomStructure containing only the chunk data for that region. When loading, structures that have - // their structure start in a different region are loaded as CustomStructurePlaceHolder instead of BO4CustomStructure. - // When loading regions, we'll reconstitute/update worldInfoChunks by replacing any CustomStructurePlaceHolders with - // BO4CustomStructures as soon as they're loaded from disk. Fully spawned chunks that are part of structures are saved - // to disk inside their structure start/placeholder, but are only cached/kept in memory in worldInfoChunks. - // (BO4CustomStructures only cache data for unspawned structure parts and spawners/moddata/particles, worldInfoChunks - // caches data about fully spawned structure chunks, plottedChunks caches/persists info about plotted chunks etc). - int regionsSaved = 0; - for (Entry cachedRegion : worldInfoChunks.entrySet()) - { - if(cachedRegion.getValue().requiresSave()) - { - cachedRegion.getValue().markSaved(); - regionsSaved++; - - HashMap>> structuresPerRegion = new HashMap>>(); - for(int internalX = 0; internalX < CustomStructureCache.REGION_SIZE; internalX++) - { - for(int internalZ = 0; internalZ < CustomStructureCache.REGION_SIZE; internalZ++) - { - ChunkCoordinate worldChunkCoord = ChunkCoordinate.fromChunkCoords( - (cachedRegion.getKey().getChunkX() * CustomStructureCache.REGION_SIZE) + internalX, - (cachedRegion.getKey().getChunkZ() * CustomStructureCache.REGION_SIZE) + internalZ - ); - CustomStructure structureInChunk = cachedRegion.getValue().getStructure(internalX, internalZ); - if(structureInChunk != null) - { - // BO3's that add spawners/particles/moddata are saved as null structures - String startBoName = "NULL"; - if(structureInChunk.start != null) - { - startBoName = structureInChunk.start.bo3Name; - } + // Plotted chunks - HashMap> entryByStructureName = structuresPerRegion.get(startBoName); - ArrayList structureChunks = new ArrayList(); - if(entryByStructureName == null) - { - entryByStructureName = new HashMap>(); - entryByStructureName.put(structureInChunk, structureChunks); - structuresPerRegion.put(startBoName, entryByStructureName); - } else { - structureChunks = entryByStructureName.get(structureInChunk); - if(structureChunks == null) - { - structureChunks = new ArrayList(); - entryByStructureName.put(structureInChunk, structureChunks); - } - } - structureChunks.add(worldChunkCoord); - } - } - } - saveStructuresRegionFile(world, dimensionId, cachedRegion.getKey(), structuresPerRegion); - } - } - OTG.log(LogMarker.INFO, regionsSaved + " structure data regions saved."); - } + public static void savePlottedChunksData(LocalWorld world, Map populatedChunks) + { + AtomicInteger regionsSaved = new AtomicInteger(); - private static void saveStructuresRegionFile(LocalWorld world, int dimensionId, ChunkCoordinate regionCoord, HashMap>> structuresPerRegion) - { - File structuresRegionFile = new File( - world.getWorldSaveDir().getAbsolutePath() + File.separator + - PluginStandardValues.PLUGIN_NAME + File.separator + - (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + - WorldStandardValues.StructureDataFolderName + File.separator + - regionCoord.getChunkX() + "_" + - regionCoord.getChunkZ() + - WorldStandardValues.StructureDataFileExtension - ); - File structuresRegionBackupFile = new File( - world.getWorldSaveDir().getAbsolutePath() + File.separator + - PluginStandardValues.PLUGIN_NAME + File.separator + - (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + - WorldStandardValues.StructureDataFolderName + File.separator + - regionCoord.getChunkX() + "_" + - regionCoord.getChunkZ() + - WorldStandardValues.StructureDataBackupFileExtension - ); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - - try - { - int version = 1; - dos.writeInt(version); - dos.writeInt(structuresPerRegion.entrySet().size()); - for(Entry>> entry : structuresPerRegion.entrySet()) - { - StreamHelper.writeStringToStream(dos, entry.getKey()); - dos.writeInt(entry.getValue().entrySet().size()); - // Structures have been de-duplicated, should be only one entry per structure start - for(Entry> entry1 : entry.getValue().entrySet()) - { - CustomStructure structure = entry1.getKey(); + populatedChunks.forEach((k, v) -> { + if(!v.requiresSave()) + return; - // No need to write to file whether this is a CustomStructurePlaceHolder or not. - // If the structure start is outside the current region, it's a placeholder. - - // Write structure start data (if any) - // If name is "NULL", we'll know not to look for these when reading. - if(entry1.getKey().start != null) - { - dos.writeInt(structure.start.rotation.getRotationId()); - dos.writeInt(structure.start.getX()); - dos.writeInt(structure.start.getY()); - dos.writeInt(structure.start.getZ()); - } + write(world, WorldStandardValues.PlottedChunksDataFolderName + File.separator + toFileName(k), v::write); - // Write all chunks used for structure - dos.writeInt(entry1.getValue().size()); - for(ChunkCoordinate chunkCoord : entry1.getValue()) - { - // TODO: Use internal coords so we can use byte/short - dos.writeInt(chunkCoord.getChunkX()); - dos.writeInt(chunkCoord.getChunkZ()); - } + v.markSaved(); + regionsSaved.getAndIncrement(); + }); - if( - structure instanceof BO4CustomStructure && - ((BO4CustomStructure)structure).objectsToSpawn.entrySet().size() > 0 - ) - { - dos.writeBoolean(true); - - Map> objectsInRegion = new HashMap>(); - int size = 0; - for(Entry> objectToSpawn : ((BO4CustomStructure)structure).objectsToSpawn.entrySet()) - { - if(objectToSpawn.getKey().toRegionCoord().equals(regionCoord)) - { - objectsInRegion.put(objectToSpawn.getKey(), objectToSpawn.getValue()); - size++; - } - } - - dos.writeInt(size); - for(Entry> objectToSpawn : objectsInRegion.entrySet()) - { - ChunkCoordinate key = objectToSpawn.getKey(); - dos.writeInt(key.getChunkX()); - dos.writeInt(key.getChunkZ()); + OTG.log(LogMarker.INFO, regionsSaved + " plotted chunk regions saved."); + } - Stack coords = objectToSpawn.getValue(); - dos.writeInt(coords.size()); - for(CustomStructureCoordinate coord : coords) - { - StreamHelper.writeStringToStream(dos, coord.bo3Name); - dos.writeInt(coord.rotation.getRotationId()); - dos.writeInt(coord.getX()); - dos.writeInt(coord.getY()); - dos.writeInt(coord.getZ()); - } - } - } else { - dos.writeBoolean(false); - } - - if( - structure instanceof BO4CustomStructure && - ((BO4CustomStructure)structure).smoothingAreaManager.smoothingAreasToSpawn.entrySet().size() > 0 - ) - { - ArrayList coords2; - dos.writeBoolean(true); - - Map> smoothingAreasPerRegion = new HashMap>(); - int size = 0; - for(Entry> smoothingAreaToSpawn : ((BO4CustomStructure)structure).smoothingAreaManager.smoothingAreasToSpawn.entrySet()) - { - if(smoothingAreaToSpawn.getKey().toRegionCoord().equals(regionCoord)) - { - smoothingAreasPerRegion.put(smoothingAreaToSpawn.getKey(), smoothingAreaToSpawn.getValue()); - size++; - } - } - - dos.writeInt(size); - for(Entry> smoothingAreaToSpawn : smoothingAreasPerRegion.entrySet()) - { - ChunkCoordinate key = smoothingAreaToSpawn.getKey(); - dos.writeInt(key.getChunkX()); - dos.writeInt(key.getChunkZ()); - - coords2 = smoothingAreaToSpawn.getValue(); - dos.writeInt(coords2.size()); - for(SmoothingAreaLine coord : coords2) - { - // TODO: Should only need origin and destination? - dos.writeInt(coord.beginPointX); - dos.writeInt(coord.beginPointY); - dos.writeInt(coord.beginPointZ); - - dos.writeInt(coord.endPointX); - dos.writeInt(coord.endPointY); - dos.writeInt(coord.endPointZ); - - dos.writeInt(coord.originPointX); - dos.writeInt(coord.originPointY); - dos.writeInt(coord.originPointZ); - - dos.writeInt(coord.finalDestinationPointX); - dos.writeInt(coord.finalDestinationPointY); - dos.writeInt(coord.finalDestinationPointZ); - } - } - } else { - dos.writeBoolean(false); - } + public static void loadPlottedChunksData(LocalWorld world, Map populatedChunks) + { + populatedChunks.clear(); + + readAll(world, WorldStandardValues.PlottedChunksDataFolderName, (p, in) -> { + ChunkCoordinate key; + try + { + key = parseChunkCoordinate(p); + } + catch(IllegalArgumentException e) + { + return; + } + + populatedChunks.put(key, PlottedChunksRegion.read(in)); + }); + } - // Save moddata/particles/spawner data - // Bo3 objects/structures have start == null - // For Bo4's, only save for the start bo4, data will be reconstituted when the file is loaded. - - if(structure.modDataManager.modData.size() > 0) - { - dos.writeBoolean(true); - - HashSet> modDataPerRegion = new HashSet>(); - int size = 0; - for(ModDataFunction modData : structure.modDataManager.modData) - { - if(ChunkCoordinate.fromBlockCoords(modData.x, modData.z).toRegionCoord().equals(regionCoord)) - { - modDataPerRegion.add(modData); - size++; - } - } - - dos.writeInt(size); - for(ModDataFunction modData : modDataPerRegion) - { - dos.writeInt(modData.x); - dos.writeInt(modData.y); - dos.writeInt(modData.z); - StreamHelper.writeStringToStream(dos, modData.modId.replace(":", ":").replace(" ", " ")); - StreamHelper.writeStringToStream(dos, modData.modData.replace(":", ":").replace(" ", " ")); - } - } else { - dos.writeBoolean(false); - } + // Structure cache + + // TODO: Since we're using regions, use short/byte for (internal) coords? + public static void saveStructureData(Map worldInfoChunks, LocalWorld world) + { + // Collect all structure start points (and chunks that have bo3's with spawners/moddata/particles in them) + // and group them by BO name (or "NULL" for bo3's with spawners/moddata/particles). + // Structure starts are saved per region, if a BO4 structure has chunk data in multiple regions, each region gets + // its own BO4CustomStructure containing only the chunk data for that region. When loading, structures that have + // their structure start in a different region are loaded as CustomStructurePlaceHolder instead of BO4CustomStructure. + // When loading regions, we'll reconstitute/update worldInfoChunks by replacing any CustomStructurePlaceHolders with + // BO4CustomStructures as soon as they're loaded from disk. Fully spawned chunks that are part of structures are saved + // to disk inside their structure start/placeholder, but are only cached/kept in memory in worldInfoChunks. + // (BO4CustomStructures only cache data for unspawned structure parts and spawners/moddata/particles, worldInfoChunks + // caches data about fully spawned structure chunks, plottedChunks caches/persists info about plotted chunks etc). + AtomicInteger regionsSaved = new AtomicInteger(); + worldInfoChunks.forEach((k, v) -> { + if(!v.requiresSave()) + return; + + saveStructuresRegionFile(world, k, v.getStructures().collect(Collectors.groupingBy(e -> e.getValue().start != null ? e.getValue().start.bo3Name : "NULL", Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList()))))); + + v.markSaved(); + regionsSaved.getAndIncrement(); + }); + + OTG.log(LogMarker.INFO, regionsSaved + " structure data regions saved."); + } - if(structure.spawnerManager.spawnerData.size() > 0) - { - dos.writeBoolean(true); - - HashSet> spawnerDataPerRegion = new HashSet>(); - int size = 0; - for(SpawnerFunction spawnerData : structure.spawnerManager.spawnerData) - { - if(ChunkCoordinate.fromBlockCoords(spawnerData.x, spawnerData.z).toRegionCoord().equals(regionCoord)) - { - spawnerDataPerRegion.add(spawnerData); - size++; - } - } - - dos.writeInt(size); - for(SpawnerFunction spawnerData : spawnerDataPerRegion) - { - dos.writeInt(spawnerData.x); - dos.writeInt(spawnerData.y); - dos.writeInt(spawnerData.z); - StreamHelper.writeStringToStream(dos, spawnerData.mobName.replace(":", ":").replace(" ", " ")); - StreamHelper.writeStringToStream(dos, spawnerData.originalnbtFileName.replace(":", ":").replace(" ", " ")); - StreamHelper.writeStringToStream(dos, spawnerData.nbtFileName.replace(":", ":").replace(" ", " ")); - dos.writeInt(spawnerData.groupSize); - dos.writeInt(spawnerData.interval); - dos.writeInt(spawnerData.spawnChance); - dos.writeInt(spawnerData.maxCount); - dos.writeInt(spawnerData.despawnTime); - dos.writeDouble(spawnerData.velocityX); - dos.writeDouble(spawnerData.velocityY); - dos.writeDouble(spawnerData.velocityZ); - dos.writeBoolean(spawnerData.velocityXSet); - dos.writeBoolean(spawnerData.velocityYSet); - dos.writeBoolean(spawnerData.velocityZSet); - dos.writeFloat(spawnerData.yaw); - dos.writeFloat(spawnerData.pitch); - } - } else { - dos.writeBoolean(false); - } + private static void saveStructuresRegionFile(LocalWorld world, ChunkCoordinate regionCoord, Map>> structuresPerRegion) + { + write(world, WorldStandardValues.StructureDataFolderName + File.separator + toFileName(regionCoord), out -> { + out.writeInt(1); // version - if(structure.particlesManager.particleData.size() > 0) - { - dos.writeBoolean(true); - - HashSet> particleDataPerRegion = new HashSet>(); - int size = 0; - for(ParticleFunction particleData : structure.particlesManager.particleData) - { - if(ChunkCoordinate.fromBlockCoords(particleData.x, particleData.z).toRegionCoord().equals(regionCoord)) - { - particleDataPerRegion.add(particleData); - size++; - } - } - - dos.writeInt(size); - for(ParticleFunction particleData : particleDataPerRegion) - { - dos.writeInt(particleData.x); - dos.writeInt(particleData.y); - dos.writeInt(particleData.z); - StreamHelper.writeStringToStream(dos, particleData.particleName.replace(":", ":").replace(" ", " ")); - dos.writeDouble(particleData.interval); - dos.writeDouble(particleData.velocityX); - dos.writeDouble(particleData.velocityY); - dos.writeDouble(particleData.velocityZ); - dos.writeBoolean(particleData.velocityXSet); - dos.writeBoolean(particleData.velocityYSet); - dos.writeBoolean(particleData.velocityZSet); - } - } else { - dos.writeBoolean(false); - } - } - } - } catch (IOException e1) { - e1.printStackTrace(); - return; - } - - DataOutputStream dos2 = null; - FileOutputStream fos = null; - try { - if(!structuresRegionFile.exists()) - { - structuresRegionFile.getParentFile().mkdirs(); - } else { - Files.move(structuresRegionFile.toPath(), structuresRegionBackupFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - byte[] compressedBytes = com.pg85.otg.util.CompressionUtils.compress(bos.toByteArray()); - dos.close(); - fos = new FileOutputStream(structuresRegionFile); - dos2 = new DataOutputStream(fos); - dos2.write(compressedBytes, 0, compressedBytes.length); - } - catch (IOException e) - { - e.printStackTrace(); - OTG.log(LogMarker.INFO, "OTG encountered an error writing " + structuresRegionFile.getAbsolutePath() + ", skipping."); - } finally { - try { - if(dos != null) - { - dos.close(); - } - } catch (Exception e) { } - try { - if(dos2 != null) - { - dos2.close(); - } - } catch (Exception e) { } - try { - if(fos != null) + // Structures have been de-duplicated, should be only one entry per structure start + DataUtil.writeMap(structuresPerRegion, out, StreamHelper::writeStringToStream, DataUtil.mapWriter((out1, k, v) -> { + // No need to write to file whether this is a CustomStructurePlaceHolder or not. + // If the structure start is outside the current region, it's a placeholder. + + // Write structure start data (if any) + // If name is "NULL", we'll know not to look for these when reading. + if(k.start != null) { - fos.close(); + out1.writeInt(k.start.rotation.getRotationId()); + out1.writeInt(k.start.getX()); + out1.writeInt(k.start.getY()); + out1.writeInt(k.start.getZ()); } - } catch (Exception e) { } - } - } - - // TODO: Load one region file at a time, on-demand, rather than loading all region files at once. - // Almost everything should be set up for it, auto-replacing CustomStructurePlaceHolders take care of most things? - public static HashMap> loadStructureData(LocalWorld world) - { - int dimensionId = world.getDimensionId(); - HashMap> output = new HashMap>(); - - File structureDataFolder = new File( - world.getWorldSaveDir().getAbsolutePath() + File.separator + - PluginStandardValues.PLUGIN_NAME + File.separator + - (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + - WorldStandardValues.StructureDataFolderName + File.separator - ); - - HashMap saveFiles = new HashMap(); - ArrayList mainFiles = new ArrayList(); - ArrayList backupFiles = new ArrayList(); - if(structureDataFolder.exists()) - { - for(File file : structureDataFolder.listFiles()) - { - if( - file.getPath().endsWith(WorldStandardValues.StructureDataFileExtension) && - file.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_").length == 2 && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_")[0]) && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_")[1]) - ) { - mainFiles.add(file); - } - else if( - file.getPath().endsWith(WorldStandardValues.StructureDataBackupFileExtension) && - file.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_").length == 2 && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_")[0]) && - MathHelper.tryParseInt(file.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_")[1]) - ) - { - backupFiles.add(file); - } - } - for(File file : mainFiles) - { - boolean bFound = false; - for(File backupFile : backupFiles) - { - if(file.getPath().replace(WorldStandardValues.StructureDataFileExtension, "").equals(backupFile.getPath().replace(WorldStandardValues.StructureDataBackupFileExtension, ""))) - { - saveFiles.put(file, backupFile); - bFound = true; - break; - } - } - if(!bFound) - { - saveFiles.put(file, null); - } - } - } - - for(Entry saveFile : saveFiles.entrySet()) - { - ChunkCoordinate regionCoord = null; - boolean bSuccess = false; - File structureDataFile = saveFile.getKey(); - File structureDataBackupFile = saveFile.getValue(); - - if( - (structureDataFile == null || !structureDataFile.exists()) && - (structureDataBackupFile == null || !structureDataBackupFile.exists()) - ) - { - continue; - } + // Write all chunks used for structure + // TODO: Use internal coords so we can use byte/short + DataUtil.writeCollection(v, out1, ChunkCoordinate::write); - if(structureDataFile != null && structureDataFile.exists()) - { - FileInputStream fis = null; - HashMap> result = null; - try { - - int regionX = Integer.parseInt(structureDataFile.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_")[0]); - int regionZ = Integer.parseInt(structureDataFile.getName().replace(WorldStandardValues.StructureDataFileExtension, "").split("_")[1]); - regionCoord = ChunkCoordinate.fromChunkCoords(regionX, regionZ); - - fis = new FileInputStream(structureDataFile); - ByteBuffer buffer = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - buffer.get(compressedBytes); - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - buffer = ByteBuffer.wrap(decompressedBytes); - - result = parseStructuresFileFromStream(buffer, regionCoord, world); - } - catch (Exception ex) - { - ex.printStackTrace(); - OTG.log(LogMarker.INFO, "Failed to load " + structureDataFile.getAbsolutePath() + ", trying to load backup."); - } finally { - if(fis != null) - { - try { - fis.getChannel().close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - if(result != null) - { - bSuccess = true; - mergeRegionData(world, result, output); - } - } - - if(!bSuccess && structureDataBackupFile != null && structureDataBackupFile.exists()) - { - FileInputStream fis = null; - HashMap> result = null; - try { - int regionX = Integer.parseInt(structureDataBackupFile.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_")[0]); - int regionZ = Integer.parseInt(structureDataBackupFile.getName().replace(WorldStandardValues.BackupFileSuffix, "").split("_")[1]); - regionCoord = ChunkCoordinate.fromChunkCoords(regionX, regionZ); - - fis = new FileInputStream(structureDataBackupFile); - ByteBuffer buffer = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - buffer.get(compressedBytes); - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - buffer = ByteBuffer.wrap(decompressedBytes); - - result = parseStructuresFileFromStream(buffer, regionCoord, world); - } - catch (Exception ex) - { - ex.printStackTrace(); - } finally { - if(fis != null) - { - try { - fis.getChannel().close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - if(result != null) - { - bSuccess = true; - mergeRegionData(world, result, output); - } - } - if(!bSuccess) - { - OTG.log(LogMarker.INFO, - "OTG encountered an error loading " + structureDataFile.getAbsolutePath() + " and could not load a backup, ignoring. " - + "This may result in areas with missing BO4's, smoothing areas, /otg structure info and spawners/particles/moddata." - ); - } - } - - return output.size() > 0 ? output : null; - } - - private static void mergeRegionData(LocalWorld world, HashMap> result, HashMap> output) - { - // When parsing structures per region, merge all placeholder structures - // into their real structure starts as soon as their regions are loaded. - // TODO: Load on-demand, not all regions at once. - for(Entry> entryResult : result.entrySet()) - { - if(output.containsKey(entryResult.getKey())) - { - for(Entry> entryOutput : new HashSet>>(output.entrySet())) - { - // Returns true if structure starts are equal - if(entryResult.getKey().equals(entryOutput.getKey())) - { - if(entryResult.getKey() instanceof CustomStructurePlaceHolder) - { - ((CustomStructurePlaceHolder)entryResult.getKey()).mergeWithCustomStructure(world, (BO4CustomStructure)entryOutput.getKey()); - ArrayList coords = entryOutput.getValue(); - coords.addAll(entryResult.getValue()); - } - else if(entryOutput.getKey() instanceof CustomStructurePlaceHolder) - { - ((CustomStructurePlaceHolder)entryOutput.getKey()).mergeWithCustomStructure(world, (BO4CustomStructure)entryResult.getKey()); - ArrayList coords = entryResult.getValue(); - coords.addAll(entryOutput.getValue()); - - // Be sure to remove before putting, or only the value gets replaced. - output.remove(entryResult.getKey()); - output.put(entryResult.getKey(), entryResult.getValue()); - } - break; - } - } - } else { - output.put(entryResult.getKey(), entryResult.getValue()); - } - } - } + DataUtil.writeOptional(k, BO4CustomStructure.class, BO4CustomStructure::getObjectsToSpawn, DataUtil::nonEmpty, out1, DataUtil.mappingWriter(out1, DataUtil.filterByKey(regionCoord::regionContainsChunk), DataUtil.mapWriter(ChunkCoordinate::write, DataUtil.collectionWriter(CustomStructureCoordinate::write)))); - // TODO: Since we're using regions now, can use byte/short for internal coords instead of int. - // TODO: Dev versions of v9 used region size 100, not 250, this may cause problems. - private static HashMap> parseStructuresFileFromStream(ByteBuffer buffer, ChunkCoordinate regionCoord, LocalWorld world) throws IOException - { - int version = buffer.getInt(); - HashMap> structuresFile = new HashMap>(); - int structureNamesSize = buffer.getInt(); - for(int i = 0; i < structureNamesSize; i++) - { - String structureName = StreamHelper.readStringFromBuffer(buffer); - Rotation startRotationId; - int startX; - int startY; - int startZ; - int structuresSize = buffer.getInt(); - for(int j = 0; j < structuresSize; j++) - { - CustomStructureCoordinate structureStart = null; + DataUtil.writeOptional(k, BO4CustomStructure.class, BO4CustomStructure::getSmoothingAreasToSpawn, DataUtil::nonEmpty, out1, DataUtil.mappingWriter(out1, DataUtil.filterByKey(regionCoord::regionContainsChunk), DataUtil.mapWriter(ChunkCoordinate::write, DataUtil.collectionWriter(SmoothingAreaLine::write)))); - // Check if this is a structure start - if(!structureName.equals("NULL")) - { - startRotationId = Rotation.getRotation(buffer.getInt()); - startX = buffer.getInt(); - startY = buffer.getInt(); - startZ = buffer.getInt(); - - if(world.isBo4Enabled()) - { - structureStart = new BO4CustomStructureCoordinate(world, null, structureName, startRotationId, startX, (short)startY, startZ, 0, false, false, null); - } else { - structureStart = new BO3CustomStructureCoordinate(world, null, structureName, startRotationId, startX, (short)startY, startZ); - } - } + // Save moddata/particles/spawner data + // Bo3 objects/structures have start == null + // For Bo4's, only save for the start bo4, data will be reconstituted when the file is loaded. - // Get all chunks used for structure - int chunksSize = buffer.getInt(); - ArrayList chunkCoords = new ArrayList(); - for(int k = 0; k < chunksSize; k++) - { - int chunkX = buffer.getInt(); - int chunkZ = buffer.getInt(); - chunkCoords.add(ChunkCoordinate.fromChunkCoords(chunkX, chunkZ)); - } + DataUtil.writeOptional(k, CustomStructure::getModData, DataUtil::nonEmpty, out1, DataUtil.mappingWriter(out1, DataUtil.filter(regionCoord::regionContains), DataUtil.collectionWriter(ModDataFunction::write))); - Map> objectsToSpawn = new HashMap>(); - if(buffer.get() != 0) - { - int objectsToSpawnSize = buffer.getInt(); - for(int l = 0; l < objectsToSpawnSize; l++) - { - ChunkCoordinate chunkCoord = ChunkCoordinate.fromChunkCoords(buffer.getInt(), buffer.getInt()); - Stack coords = new Stack(); - int coordsSize = buffer.getInt(); - for(int m = 0; m < coordsSize; m++) - { - String bo3Name = StreamHelper.readStringFromBuffer(buffer); - Rotation coordRotation = Rotation.getRotation(buffer.getInt()); - int coordX = buffer.getInt(); - int coordY = buffer.getInt(); - int coordZ = buffer.getInt(); - coords.add(new BO4CustomStructureCoordinate(world, null, bo3Name, coordRotation, coordX, (short)coordY, coordZ, 0, false, false, null)); - } - objectsToSpawn.put(chunkCoord, coords); - } - } - - Map> smoothingAreasToSpawn = new HashMap>(); - if(buffer.get() != 0) - { - int smoothingAreasToSpawnSize = buffer.getInt(); - for(int l = 0; l < smoothingAreasToSpawnSize; l++) - { - ChunkCoordinate chunkCoord = ChunkCoordinate.fromChunkCoords(buffer.getInt(), buffer.getInt()); - int coordsSize = buffer.getInt(); - ArrayList smoothingAreaLines = new ArrayList(); - for(int m = 0; m < coordsSize; m++) - { - SmoothingAreaLine smoothingAreaLine; - - int beginPointX = buffer.getInt(); - int beginPointY = buffer.getInt(); - int beginPointZ = buffer.getInt(); + DataUtil.writeOptional(k, CustomStructure::getSpawnerData, DataUtil::nonEmpty, out1, DataUtil.mappingWriter(out1, DataUtil.filter(regionCoord::regionContains), DataUtil.collectionWriter(SpawnerFunction::write))); - int endPointX = buffer.getInt(); - int endPointY = buffer.getInt(); - int endPointZ = buffer.getInt(); + DataUtil.writeOptional(k, CustomStructure::getParticleData, DataUtil::nonEmpty, out1, DataUtil.mappingWriter(out1, DataUtil.filter(regionCoord::regionContains), DataUtil.collectionWriter(ParticleFunction::write))); + })); + }); + } - int originPointX = buffer.getInt(); - int originPointY = buffer.getInt(); - int originPointZ = buffer.getInt(); + // TODO: Load one region file at a time, on-demand, rather than loading all region files at once. + // Almost everything should be set up for it, auto-replacing CustomStructurePlaceHolders take care of most things? + public static Map> loadStructureData(LocalWorld world) + { + Map>> output = new HashMap<>(); + + readAll(world, WorldStandardValues.StructureDataFolderName, (p, in) -> { + ChunkCoordinate key = parseChunkCoordinate(p); + if(key == null) + { + return; + } + + // When parsing structures per region, merge all placeholder structures + // into their real structure starts as soon as their regions are loaded. + parseStructuresFileFromStream(in, key, world).values().stream().map(Map::entrySet).flatMap(Set::stream).forEach(e -> { + output.merge(e.getKey(), e, (oldEntry, newEntry) -> { + if(newEntry.getKey() instanceof CustomStructurePlaceHolder) + { + ((CustomStructurePlaceHolder) newEntry.getKey()).mergeWithCustomStructure(world, (BO4CustomStructure) oldEntry.getKey()); + oldEntry.getValue().addAll(newEntry.getValue()); + return oldEntry; + } + if(oldEntry.getKey() instanceof CustomStructurePlaceHolder) + { + ((CustomStructurePlaceHolder) oldEntry.getKey()).mergeWithCustomStructure(world, (BO4CustomStructure) newEntry.getKey()); + newEntry.getValue().addAll(oldEntry.getValue()); + return new SimpleEntry<>(newEntry); + } + return oldEntry; + }); + }); + }); + + if(output.isEmpty()) + { + return null; + } + return output.values().stream().collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + } - int finalDestinationPointX = buffer.getInt(); - int finalDestinationPointY = buffer.getInt(); - int finalDestinationPointZ = buffer.getInt(); - - smoothingAreaLine = new SmoothingAreaLine(beginPointX, (short)beginPointY, beginPointZ, endPointX, (short)endPointY, endPointZ, originPointX, (short)originPointY, originPointZ, finalDestinationPointX, (short)finalDestinationPointY, finalDestinationPointZ); - smoothingAreaLines.add(smoothingAreaLine); - } - smoothingAreasToSpawn.put(chunkCoord, smoothingAreaLines); - } - } - - // Save moddata/particles/spawner data - // Bo3 objects/structures have start == null - // For Bo4's, only save for the start bo4, data will be reconstituted when the file is loaded. - - HashSet> modData = new HashSet>(); - if(buffer.get() != 0) - { - int modDataSize = buffer.getInt(); - for(int l = 0; l < modDataSize; l++) - { - ModDataFunction modDataFunction; - if(world.isBo4Enabled()) - { - modDataFunction = new BO4ModDataFunction(); - } else { - modDataFunction = new BO3ModDataFunction(); - } - - modDataFunction.x = buffer.getInt(); - modDataFunction.y = buffer.getInt(); - modDataFunction.z = buffer.getInt(); - modDataFunction.modId = StreamHelper.readStringFromBuffer(buffer); - modDataFunction.modData = StreamHelper.readStringFromBuffer(buffer); - modData.add(modDataFunction); - } - } + // TODO: Since we're using regions now, can use byte/short for internal coords instead of int. + // TODO: Dev versions of v9 used region size 100, not 250, this may cause problems. + private static Map>> parseStructuresFileFromStream(DataInputStream in, ChunkCoordinate regionCoord, LocalWorld world) throws IOException + { + in.readInt(); // version - HashSet> spawnerData = new HashSet>(); - if(buffer.get() != 0) - { - int spawnerDataSize = buffer.getInt(); - for(int l = 0; l < spawnerDataSize; l++) - { - SpawnerFunction spawnerFunction; - if(world.isBo4Enabled()) - { - spawnerFunction = new BO4SpawnerFunction(); - } else { - spawnerFunction = new BO3SpawnerFunction(); - } - - spawnerFunction.x = buffer.getInt(); - spawnerFunction.y = buffer.getInt(); - spawnerFunction.z = buffer.getInt(); - spawnerFunction.mobName = StreamHelper.readStringFromBuffer(buffer); - spawnerFunction.originalnbtFileName = StreamHelper.readStringFromBuffer(buffer); - spawnerFunction.nbtFileName = StreamHelper.readStringFromBuffer(buffer); - spawnerFunction.groupSize = buffer.getInt(); - spawnerFunction.interval = buffer.getInt(); - spawnerFunction.spawnChance = buffer.getInt(); - spawnerFunction.maxCount = buffer.getInt(); - spawnerFunction.despawnTime = buffer.getInt(); - spawnerFunction.velocityX = buffer.getDouble(); - spawnerFunction.velocityY = buffer.getDouble(); - spawnerFunction.velocityZ = buffer.getDouble(); - spawnerFunction.velocityXSet = buffer.get() != 0; - spawnerFunction.velocityYSet = buffer.get() != 0; - spawnerFunction.velocityZSet = buffer.get() != 0; - spawnerFunction.yaw = buffer.getFloat(); - spawnerFunction.pitch = buffer.getFloat(); - spawnerData.add(spawnerFunction); - } - } + return DataUtil.readMap(new HashMap<>(), in, in1 -> { + String structureName = StreamHelper.readStringFromStream(in1); + return new SimpleEntry<>(structureName, DataUtil.readMap(new HashMap<>(), in1, in2 -> { - HashSet> particleData = new HashSet>(); - if(buffer.get() != 0) - { - int particleDataSize = buffer.getInt(); - for(int l = 0; l < particleDataSize; l++) - { - ParticleFunction particleFunction; - if(world.isBo4Enabled()) - { - particleFunction = new BO4ParticleFunction(); - } else { - particleFunction = new BO3ParticleFunction(); - } - - particleFunction.x = buffer.getInt(); - particleFunction.y = buffer.getInt(); - particleFunction.z = buffer.getInt(); - particleFunction.particleName = StreamHelper.readStringFromBuffer(buffer); - particleFunction.interval = buffer.getDouble(); - particleFunction.velocityX = buffer.getDouble(); - particleFunction.velocityY = buffer.getDouble(); - particleFunction.velocityZ = buffer.getDouble(); - particleFunction.velocityXSet = buffer.get() != 0; - particleFunction.velocityYSet = buffer.get() != 0; - particleFunction.velocityZSet = buffer.get() != 0; - - particleData.add(particleFunction); - } - } - - CustomStructure structure; - if(world.isBo4Enabled()) - { - // If the structure start is outside the current region, it's a placeholder. - // We'll replace the placeholder in worldInfoChunks as soon as the region data - // containing the "real" structure start is loaded. - ChunkCoordinate startChunkCoord = ChunkCoordinate.fromChunkCoords(structureStart.getChunkX(), structureStart.getChunkZ()); - if(!startChunkCoord.toRegionCoord().equals(regionCoord)) - { - structure = new CustomStructurePlaceHolder(world, (BO4CustomStructureCoordinate)structureStart, objectsToSpawn, smoothingAreasToSpawn, 0); - } else { - structure = new BO4CustomStructure(world, (BO4CustomStructureCoordinate)structureStart, objectsToSpawn, smoothingAreasToSpawn, 0); - } - ((BO4CustomStructure)structure).startChunkBlockChecksDone = true; - } else { - structure = new BO3CustomStructure((BO3CustomStructureCoordinate)structureStart); - } - structure.modDataManager.modData = modData; - structure.spawnerManager.spawnerData = spawnerData; - structure.particlesManager.particleData = particleData; - structuresFile.put(structure, chunkCoords); - } - } + CustomStructureCoordinate structureStart = null; - return structuresFile; - } + // Check if this is a structure start + if(!structureName.equals("NULL")) + { + Rotation startRotationId = Rotation.getRotation(in2.readInt()); + int startX = in2.readInt(); + int startY = in2.readInt(); + int startZ = in2.readInt(); + + if(world.isBo4Enabled()) + { + structureStart = new BO4CustomStructureCoordinate(world, null, structureName, startRotationId, startX, (short) startY, startZ, 0, false, false, null); + } + else + { + structureStart = new BO3CustomStructureCoordinate(world, null, structureName, startRotationId, startX, (short) startY, startZ); + } + } - public static void saveChunksMapFile(LocalWorld world, HashMap> spawnedStructuresByName, HashMap> spawnedStructuresByGroup) - { - int dimensionId = world.getDimensionId(); - File occupiedChunksFile = new File(world.getWorldSaveDir().getAbsolutePath() + File.separator + PluginStandardValues.PLUGIN_NAME + File.separator + (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + WorldStandardValues.SpawnedStructuresFileName); - File occupiedChunksBackupFile = new File(world.getWorldSaveDir().getAbsolutePath() + File.separator + PluginStandardValues.PLUGIN_NAME + File.separator + (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + WorldStandardValues.SpawnedStructuresBackupFileName); + List chunkCoords = DataUtil.readCollection(new ArrayList<>(), in, ChunkCoordinate::read); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - - if(spawnedStructuresByName.size() > 0) - { - try { - int version = 1; - dos.writeInt(version); + Map> objectsToSpawn = DataUtil.readOptional(new HashMap<>(), in2, DataUtil.mapReader(HashMap::new, ChunkCoordinate::read, DataUtil.collectionReader(Stack::new, BO4CustomStructureCoordinate.reader(world)))); - dos.writeInt(spawnedStructuresByName.entrySet().size()); - for(Map.Entry> entry : spawnedStructuresByName.entrySet()) - { - StreamHelper.writeStringToStream(dos, entry.getKey()); - dos.writeInt(entry.getValue().size()); - for(ChunkCoordinate chunkCoord : entry.getValue()) - { - dos.writeInt(chunkCoord.getChunkX()); - dos.writeInt(chunkCoord.getChunkZ()); - } - } - - dos.writeInt(spawnedStructuresByGroup.entrySet().size()); - for(Entry> entry : spawnedStructuresByGroup.entrySet()) - { - StreamHelper.writeStringToStream(dos, entry.getKey()); - dos.writeInt(entry.getValue().entrySet().size()); - for(Entry valueEntry : entry.getValue().entrySet()) - { - dos.writeInt(valueEntry.getKey().getChunkX()); - dos.writeInt(valueEntry.getKey().getChunkZ()); - dos.writeInt(valueEntry.getValue().intValue()); - } - } - } catch (IOException e1) { - e1.printStackTrace(); - return; - } - - DataOutputStream dos2 = null; - FileOutputStream fos = null; - try - { - if(!occupiedChunksFile.exists()) - { - occupiedChunksFile.getParentFile().mkdirs(); - } else { - Files.move(occupiedChunksFile.toPath(), occupiedChunksBackupFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - byte[] compressedBytes = com.pg85.otg.util.CompressionUtils.compress(bos.toByteArray()); - fos = new FileOutputStream(occupiedChunksFile); - dos2 = new DataOutputStream(fos); - dos2.write(compressedBytes, 0, compressedBytes.length); - } catch (IOException e) { - e.printStackTrace(); - OTG.log(LogMarker.INFO, "OTG encountered an error writing " + occupiedChunksFile.getAbsolutePath() + ", skipping."); - } finally { - try { - if(dos != null) - { - dos.close(); - } - } catch (Exception e) { } - try { - if(dos2 != null) - { - dos2.close(); - } - } catch (Exception e) { } - try { - if(fos != null) - { - fos.close(); - } - } catch (Exception e) { } - } - } - } + Map> smoothingAreasToSpawn = DataUtil.readOptional(new HashMap<>(), in2, DataUtil.mapReader(HashMap::new, ChunkCoordinate::read, DataUtil.collectionReader(ArrayList::new, SmoothingAreaLine::read))); - public static void loadChunksMapFile(LocalWorld world, HashMap> spawnedStructuresByName, HashMap> spawnedStructuresByGroup) - { - int dimensionId = world.getDimensionId(); - File occupiedChunksFile = new File(world.getWorldSaveDir().getAbsolutePath() + File.separator + PluginStandardValues.PLUGIN_NAME + File.separator + (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + WorldStandardValues.SpawnedStructuresFileName); - File occupiedChunksBackupFile = new File(world.getWorldSaveDir().getAbsolutePath() + File.separator + PluginStandardValues.PLUGIN_NAME + File.separator + (dimensionId != 0 ? "DIM-" + dimensionId + File.separator : "") + WorldStandardValues.SpawnedStructuresBackupFileName); + HashSet> modData = DataUtil.readOptional(new HashSet<>(), in2, DataUtil.collectionReader(HashSet::new, world.isBo4Enabled() ? BO4ModDataFunction::read : BO3ModDataFunction::read)); + HashSet> spawnerData = DataUtil.readOptional(new HashSet<>(), in2, DataUtil.collectionReader(HashSet::new, world.isBo4Enabled() ? BO4SpawnerFunction::read : BO3SpawnerFunction::read)); + HashSet> particleData = DataUtil.readOptional(new HashSet<>(), in2, DataUtil.collectionReader(HashSet::new, world.isBo4Enabled() ? BO4ParticleFunction::read : BO3ParticleFunction::read)); - if(!occupiedChunksFile.exists() && !occupiedChunksBackupFile.exists()) - { - return; - } - - if(occupiedChunksFile.exists()) - { - FileInputStream fis = null; - try { - fis = new FileInputStream(occupiedChunksFile); - ByteBuffer buffer = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - buffer.get(compressedBytes); - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - buffer = ByteBuffer.wrap(decompressedBytes); - parseChunksMapFileFromStream(buffer, world, spawnedStructuresByName, spawnedStructuresByGroup); - return; - } - catch (Exception ex) - { - ex.printStackTrace(); - OTG.log(LogMarker.INFO, "Failed to load " + occupiedChunksFile.getAbsolutePath() + ", trying to load backup."); - } finally { - if(fis != null) - { - try { - fis.getChannel().close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - if(occupiedChunksBackupFile.exists()) - { - FileInputStream fis = null; - try { - fis = new FileInputStream(occupiedChunksBackupFile); - ByteBuffer buffer = fis.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fis.getChannel().size()); - - byte[] compressedBytes = new byte[(int) fis.getChannel().size()]; - buffer.get(compressedBytes); - byte[] decompressedBytes = com.pg85.otg.util.CompressionUtils.decompress(compressedBytes); - buffer = ByteBuffer.wrap(decompressedBytes); - parseChunksMapFileFromStream(buffer, world, spawnedStructuresByName, spawnedStructuresByGroup); - return; - } - catch (Exception ex) - { - ex.printStackTrace(); - } finally { - if(fis != null) - { - try { - fis.getChannel().close(); - } catch (IOException e) { - e.printStackTrace(); - } - try { - fis.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - OTG.log(LogMarker.INFO, "OTG encountered an error loading " + occupiedChunksFile.getAbsolutePath() + " and could not load a backup, skipping. "); - } + CustomStructure structure; + if(world.isBo4Enabled()) + { + // If the structure start is outside the current region, it's a placeholder. + // We'll replace the placeholder in worldInfoChunks as soon as the region data + // containing the "real" structure start is loaded. + ChunkCoordinate startChunkCoord = ChunkCoordinate.fromChunkCoords(structureStart.getChunkX(), structureStart.getChunkZ()); + if(!startChunkCoord.toRegionCoord().equals(regionCoord)) + { + structure = new CustomStructurePlaceHolder(world, (BO4CustomStructureCoordinate) structureStart, objectsToSpawn, smoothingAreasToSpawn, 0); + } + else + { + structure = new BO4CustomStructure(world, (BO4CustomStructureCoordinate) structureStart, objectsToSpawn, smoothingAreasToSpawn, 0); + } + ((BO4CustomStructure) structure).startChunkBlockChecksDone = true; + } + else + { + structure = new BO3CustomStructure((BO3CustomStructureCoordinate) structureStart); + } + structure.modDataManager.modData = modData; + structure.spawnerManager.spawnerData = spawnerData; + structure.particlesManager.particleData = particleData; + return new SimpleEntry<>(structure, chunkCoords); + })); + }); + } + + public static void saveChunksMapFile(LocalWorld world, Map> spawnedStructuresByName, Map> spawnedStructuresByGroup) + { + write(world, WorldStandardValues.SpawnedStructuresFileName, out -> { + out.writeInt(1); // version + + DataUtil.writeMap(spawnedStructuresByName, out, StreamHelper::writeStringToStream, DataUtil.collectionWriter(ChunkCoordinate::write)); + DataUtil.writeMap(spawnedStructuresByGroup, out, StreamHelper::writeStringToStream, DataUtil.mapWriter(ChunkCoordinate::write, DataOutput::writeInt)); + }); + } + + public static void loadChunksMapFile(LocalWorld world, Map> spawnedStructuresByName, Map> spawnedStructuresByGroup) + { + spawnedStructuresByName.clear(); + spawnedStructuresByGroup.clear(); + + read(world, WorldStandardValues.SpawnedStructuresFileName, in -> { + Map> chunksByName = new HashMap<>(); + Map> chunksByGroup = new HashMap<>(); - private static void parseChunksMapFileFromStream(ByteBuffer buffer, LocalWorld world, HashMap> spawnedStructuresByName, HashMap> spawnedStructuresByGroup) throws IOException - { - HashMap> chunksByName = new HashMap>(); - HashMap> chunksByGroup = new HashMap>(); + in.readInt(); // version - int version = buffer.getInt(); - - int spawnedStructuresByNameSize = buffer.getInt(); - for(int i = 0; i < spawnedStructuresByNameSize; i++) - { - String name = StreamHelper.readStringFromBuffer(buffer); - int coordsSize = buffer.getInt(); - ArrayList coords = new ArrayList(); - for(int j = 0; j < coordsSize; j++) - { - coords.add(ChunkCoordinate.fromChunkCoords(buffer.getInt(), buffer.getInt())); - } - chunksByName.put(name, coords); - } - - int spawnedStructuresByGroupSize = buffer.getInt(); - for(int k = 0; k < spawnedStructuresByGroupSize; k++) - { - String name = StreamHelper.readStringFromBuffer(buffer); - HashMap coords = new HashMap(); - int coordsSize = buffer.getInt(); - for(int l = 0; l < coordsSize; l++) - { - coords.put(ChunkCoordinate.fromChunkCoords(buffer.getInt(), buffer.getInt()), Integer.valueOf(buffer.getInt())); - } - chunksByGroup.put(name, coords); - } - - spawnedStructuresByName.clear(); - spawnedStructuresByName.putAll(chunksByName); - - spawnedStructuresByGroup.clear(); - spawnedStructuresByGroup.putAll(chunksByGroup); - } + DataUtil.readMap(chunksByName, in, StreamHelper::readStringFromStream, DataUtil.collectionReader(ArrayList::new, ChunkCoordinate::read)); + DataUtil.readMap(chunksByGroup, in, StreamHelper::readStringFromStream, DataUtil.mapReader(HashMap::new, ChunkCoordinate::read, DataInput::readInt)); + + spawnedStructuresByName.putAll(chunksByName); + spawnedStructuresByGroup.putAll(chunksByGroup); + }); + } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/EntitiesManager.java b/common/src/main/java/com/pg85/otg/customobjects/structures/EntitiesManager.java index 8e22940d0..55f7e73d1 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/EntitiesManager.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/EntitiesManager.java @@ -33,28 +33,28 @@ else if(coordObject.getRotation() == Rotation.EAST) // Apply rotation if(rotations == 0) { - newEntityData.x = entityDataInObject[i].x; - newEntityData.z = entityDataInObject[i].z; + newEntityData.x(entityDataInObject[i].x()); + newEntityData.z(entityDataInObject[i].z()); } if(rotations == 1) { - newEntityData.x = entityDataInObject[i].z; - newEntityData.z = -entityDataInObject[i].x + 15; + newEntityData.x(entityDataInObject[i].z()); + newEntityData.z(-entityDataInObject[i].x() + 15); } if(rotations == 2) { - newEntityData.x = -entityDataInObject[i].x + 15; - newEntityData.z = -entityDataInObject[i].z + 15; + newEntityData.x(-entityDataInObject[i].x() + 15); + newEntityData.z(-entityDataInObject[i].z() + 15); } if(rotations == 3) { - newEntityData.x = -entityDataInObject[i].z + 15; - newEntityData.z = entityDataInObject[i].x; + newEntityData.x(-entityDataInObject[i].z() + 15); + newEntityData.z(entityDataInObject[i].x()); } - newEntityData.y = coordObject.getY() + entityDataInObject[i].y; + newEntityData.y(coordObject.getY() + entityDataInObject[i].y()); - newEntityData.x = coordObject.getX() + newEntityData.x; - newEntityData.z = coordObject.getZ() + newEntityData.z; + newEntityData.x(coordObject.getX() + newEntityData.x()); + newEntityData.z(coordObject.getZ() + newEntityData.z()); newEntityData.name = entityDataInObject[i].name; newEntityData.resourceLocation = entityDataInObject[i].resourceLocation; @@ -66,10 +66,10 @@ else if(coordObject.getRotation() == Rotation.EAST) world.spawnEntity(newEntityData, chunkCoordinate); } else { - newEntityData.y = coordObject.getY() + entityDataInObject[i].y; + newEntityData.y(coordObject.getY() + entityDataInObject[i].y()); - newEntityData.x = coordObject.getX() + entityDataInObject[i].x; - newEntityData.z = coordObject.getZ() + entityDataInObject[i].z; + newEntityData.x(coordObject.getX() + entityDataInObject[i].x()); + newEntityData.z(coordObject.getZ() + entityDataInObject[i].z()); newEntityData.name = entityDataInObject[i].name; newEntityData.resourceLocation = entityDataInObject[i].resourceLocation; diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/ModDataManager.java b/common/src/main/java/com/pg85/otg/customobjects/structures/ModDataManager.java index bc4c621be..6aeb32a96 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/ModDataManager.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/ModDataManager.java @@ -36,51 +36,51 @@ else if(coordObject.getRotation() == Rotation.EAST) // Apply rotation if(rotations == 0) { - newModData.x = blockDataInObject[i].x; - newModData.z = blockDataInObject[i].z; + newModData.x(blockDataInObject[i].x()); + newModData.z(blockDataInObject[i].z()); } if(rotations == 1) { - newModData.x = blockDataInObject[i].z; - newModData.z = -blockDataInObject[i].x + 15; + newModData.x(blockDataInObject[i].z()); + newModData.z(-blockDataInObject[i].x() + 15); } if(rotations == 2) { - newModData.x = -blockDataInObject[i].x + 15; - newModData.z = -blockDataInObject[i].z + 15; + newModData.x(-blockDataInObject[i].x() + 15); + newModData.z(-blockDataInObject[i].z() + 15); } if(rotations == 3) { - newModData.x = -blockDataInObject[i].z + 15; - newModData.z = blockDataInObject[i].x; + newModData.x(-blockDataInObject[i].z() + 15); + newModData.z(blockDataInObject[i].x()); } - newModData.y = coordObject.getY() + blockDataInObject[i].y; + newModData.y(coordObject.getY() + blockDataInObject[i].y()); - newModData.x = coordObject.getX() + newModData.x; - newModData.z = coordObject.getZ() + newModData.z; + newModData.x(coordObject.getX() + newModData.x()); + newModData.z(coordObject.getZ() + newModData.z()); newModData.modData = blockDataInObject[i].modData; newModData.modId = blockDataInObject[i].modId; modData.add(newModData); - if(!ChunkCoordinate.fromBlockCoords(newModData.x, newModData.z).equals(chunkCoordinate)) + if(!ChunkCoordinate.fromBlockCoords(newModData.x(), newModData.z()).equals(chunkCoordinate)) { throw new RuntimeException(); // TODO: Remove this after testing } } else { - newModData.y = coordObject.getY() + blockDataInObject[i].y; + newModData.y(coordObject.getY() + blockDataInObject[i].y()); - newModData.x = coordObject.getX() + blockDataInObject[i].x; - newModData.z = coordObject.getZ() + blockDataInObject[i].z; + newModData.x(coordObject.getX() + blockDataInObject[i].x()); + newModData.z(coordObject.getZ() + blockDataInObject[i].z()); newModData.modData = blockDataInObject[i].modData; newModData.modId = blockDataInObject[i].modId; modData.add(newModData); - if(!ChunkCoordinate.fromBlockCoords(newModData.x, newModData.z).equals(chunkCoordinate)) + if(!ChunkCoordinate.fromBlockCoords(newModData.x(), newModData.z()).equals(chunkCoordinate)) { throw new RuntimeException(); // TODO: Remove this after testing } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/ParticlesManager.java b/common/src/main/java/com/pg85/otg/customobjects/structures/ParticlesManager.java index 1beced6ff..938884d09 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/ParticlesManager.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/ParticlesManager.java @@ -36,44 +36,44 @@ else if(coordObject.getRotation() == Rotation.EAST) // Apply rotation if(rotations == 0) { - newParticleData.x = particleDataInObject[i].x; + newParticleData.x(particleDataInObject[i].x()); newParticleData.velocityX = particleDataInObject[i].velocityX; - newParticleData.z = particleDataInObject[i].z; + newParticleData.z(particleDataInObject[i].z()); newParticleData.velocityZ = particleDataInObject[i].velocityZ; newParticleData.velocityXSet = particleDataInObject[i].velocityXSet; newParticleData.velocityZSet = particleDataInObject[i].velocityZSet; } if(rotations == 1) { - newParticleData.x = particleDataInObject[i].z; + newParticleData.x(particleDataInObject[i].z()); newParticleData.velocityX = particleDataInObject[i].velocityZ; - newParticleData.z = -particleDataInObject[i].x + 15; + newParticleData.z(-particleDataInObject[i].x() + 15); newParticleData.velocityZ = -particleDataInObject[i].velocityX; newParticleData.velocityXSet = particleDataInObject[i].velocityZSet; newParticleData.velocityZSet = particleDataInObject[i].velocityXSet; } if(rotations == 2) { - newParticleData.x = -particleDataInObject[i].x + 15; + newParticleData.x(-particleDataInObject[i].x() + 15); newParticleData.velocityX = -particleDataInObject[i].velocityX; - newParticleData.z = -particleDataInObject[i].z + 15; + newParticleData.z(-particleDataInObject[i].z() + 15); newParticleData.velocityZ = -particleDataInObject[i].velocityZ; newParticleData.velocityXSet = particleDataInObject[i].velocityXSet; newParticleData.velocityZSet = particleDataInObject[i].velocityZSet; } if(rotations == 3) { - newParticleData.x = -particleDataInObject[i].z + 15; + newParticleData.x(-particleDataInObject[i].z() + 15); newParticleData.velocityX = -particleDataInObject[i].velocityZ; - newParticleData.z = particleDataInObject[i].x; + newParticleData.z(particleDataInObject[i].x()); newParticleData.velocityZ = particleDataInObject[i].velocityX; newParticleData.velocityXSet = particleDataInObject[i].velocityZSet; newParticleData.velocityZSet = particleDataInObject[i].velocityXSet; } - newParticleData.y = coordObject.getY() + particleDataInObject[i].y; + newParticleData.y(coordObject.getY() + particleDataInObject[i].y()); - newParticleData.x = coordObject.getX() + newParticleData.x; - newParticleData.z = coordObject.getZ() + newParticleData.z; + newParticleData.x(coordObject.getX() + newParticleData.x()); + newParticleData.z(coordObject.getZ() + newParticleData.z()); newParticleData.particleName = particleDataInObject[i].particleName; @@ -84,16 +84,16 @@ else if(coordObject.getRotation() == Rotation.EAST) particleData.add(newParticleData); - if(!ChunkCoordinate.fromBlockCoords(newParticleData.x, newParticleData.z).equals(chunkCoordinate)) + if(!ChunkCoordinate.fromBlockCoords(newParticleData.x(), newParticleData.z()).equals(chunkCoordinate)) { throw new RuntimeException(); // TODO: Remove after testing } } else { - newParticleData.y = coordObject.getY() + particleDataInObject[i].y; + newParticleData.y(coordObject.getY() + particleDataInObject[i].y()); - newParticleData.x = coordObject.getX() + particleDataInObject[i].x; - newParticleData.z = coordObject.getZ() + particleDataInObject[i].z; + newParticleData.x(coordObject.getX() + particleDataInObject[i].x()); + newParticleData.z(coordObject.getZ() + particleDataInObject[i].z()); newParticleData.particleName = particleDataInObject[i].particleName; @@ -109,7 +109,7 @@ else if(coordObject.getRotation() == Rotation.EAST) particleData.add(newParticleData); - if(!ChunkCoordinate.fromBlockCoords(newParticleData.x, newParticleData.z).equals(chunkCoordinate)) + if(!ChunkCoordinate.fromBlockCoords(newParticleData.x(), newParticleData.z()).equals(chunkCoordinate)) { throw new RuntimeException(); // TODO: Remove after testing } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/PlottedChunksRegion.java b/common/src/main/java/com/pg85/otg/customobjects/structures/PlottedChunksRegion.java index 3d9425463..6fb22114f 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/PlottedChunksRegion.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/PlottedChunksRegion.java @@ -1,52 +1,73 @@ package com.pg85.otg.customobjects.structures; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; import java.util.Arrays; +import com.pg85.otg.OTG; +import com.pg85.otg.logging.LogMarker; + public class PlottedChunksRegion { - private boolean requiresSave = false; - private boolean[][] plottedChunks = new boolean[CustomStructureCache.REGION_SIZE][CustomStructureCache.REGION_SIZE]; - - public PlottedChunksRegion() { } - - public PlottedChunksRegion(boolean[][] plottedChunks) - { - this.plottedChunks = plottedChunks; - } - - public boolean requiresSave() - { - return this.requiresSave; - } - - public void markSaved() - { - this.requiresSave = false; - } - - public boolean getChunk(int internalX, int internalZ) - { - return this.plottedChunks[internalX][internalZ]; - } - - public void setChunk(int internalX, int internalZ) - { - this.plottedChunks[internalX][internalZ] = true; - this.requiresSave = true; - } - - public boolean[][] getArray() - { - return this.plottedChunks; - } - - public static PlottedChunksRegion getFilledRegion() - { - boolean[][] plottedChunks = new boolean[CustomStructureCache.REGION_SIZE][CustomStructureCache.REGION_SIZE]; - for(int i = 0; i < CustomStructureCache.REGION_SIZE; i++) - { - Arrays.fill(plottedChunks[i], true); - } - return new PlottedChunksRegion(plottedChunks); - } + private boolean requiresSave = false; + private final boolean[] plottedChunks = new boolean[CustomStructureCache.REGION_SIZE * CustomStructureCache.REGION_SIZE]; + + public boolean requiresSave() + { + return this.requiresSave; + } + + public void markSaved() + { + this.requiresSave = false; + } + + public boolean getChunk(int internalX, int internalZ) + { + return this.plottedChunks[internalX * CustomStructureCache.REGION_SIZE + internalZ]; + } + + public void setChunk(int internalX, int internalZ) + { + if(!this.plottedChunks[internalX * CustomStructureCache.REGION_SIZE + internalZ]) + { + this.plottedChunks[internalX * CustomStructureCache.REGION_SIZE + internalZ] = true; + this.requiresSave = true; + } + } + + public void write(DataOutput out) throws IOException + { + write(out, this); + } + + public static void write(DataOutput out, PlottedChunksRegion region) throws IOException + { + out.writeInt(1); // version + out.writeInt(CustomStructureCache.REGION_SIZE); + for(boolean plotted : region.plottedChunks) + { + out.writeBoolean(plotted); + } + } + + public static PlottedChunksRegion read(DataInput in) throws IOException + { + in.readInt(); // version + PlottedChunksRegion region = new PlottedChunksRegion(); + if(in.readInt() != CustomStructureCache.REGION_SIZE) + { + OTG.log(LogMarker.INFO, "PlottedChunks region files were corrupted or exported with an incompatible version of OTG, ignoring."); + Arrays.fill(region.plottedChunks, true); + } + else + { + for(int i = 0; i < region.plottedChunks.length; i++) + { + region.plottedChunks[i] = in.readBoolean(); + } + } + return region; + } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/SpawnerManager.java b/common/src/main/java/com/pg85/otg/customobjects/structures/SpawnerManager.java index ae9c30452..a06be3bab 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/SpawnerManager.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/SpawnerManager.java @@ -36,44 +36,44 @@ else if(coordObject.getRotation() == Rotation.EAST) // Apply rotation if(rotations == 0) { - newSpawnerData.x = spawnerDataInObject[i].x; + newSpawnerData.x(spawnerDataInObject[i].x()); newSpawnerData.velocityX = spawnerDataInObject[i].velocityX; - newSpawnerData.z = spawnerDataInObject[i].z; + newSpawnerData.z(spawnerDataInObject[i].z()); newSpawnerData.velocityZ = spawnerDataInObject[i].velocityZ; newSpawnerData.velocityXSet = spawnerDataInObject[i].velocityXSet; newSpawnerData.velocityZSet = spawnerDataInObject[i].velocityZSet; } if(rotations == 1) { - newSpawnerData.x = spawnerDataInObject[i].z; + newSpawnerData.x(spawnerDataInObject[i].z()); newSpawnerData.velocityX = spawnerDataInObject[i].velocityZ; - newSpawnerData.z = -spawnerDataInObject[i].x + 15; + newSpawnerData.z(-spawnerDataInObject[i].x() + 15); newSpawnerData.velocityZ = -spawnerDataInObject[i].velocityX; newSpawnerData.velocityXSet = spawnerDataInObject[i].velocityZSet; newSpawnerData.velocityZSet = spawnerDataInObject[i].velocityXSet; } if(rotations == 2) { - newSpawnerData.x = -spawnerDataInObject[i].x + 15; + newSpawnerData.x(-spawnerDataInObject[i].x() + 15); newSpawnerData.velocityX = -spawnerDataInObject[i].velocityX; - newSpawnerData.z = -spawnerDataInObject[i].z + 15; + newSpawnerData.z(-spawnerDataInObject[i].z() + 15); newSpawnerData.velocityZ = -spawnerDataInObject[i].velocityZ; newSpawnerData.velocityXSet = spawnerDataInObject[i].velocityXSet; newSpawnerData.velocityZSet = spawnerDataInObject[i].velocityZSet; } if(rotations == 3) { - newSpawnerData.x = -spawnerDataInObject[i].z + 15; + newSpawnerData.x(-spawnerDataInObject[i].z() + 15); newSpawnerData.velocityX = -spawnerDataInObject[i].velocityZ; - newSpawnerData.z = spawnerDataInObject[i].x; + newSpawnerData.z(spawnerDataInObject[i].x()); newSpawnerData.velocityZ = spawnerDataInObject[i].velocityX; newSpawnerData.velocityXSet = spawnerDataInObject[i].velocityZSet; newSpawnerData.velocityZSet = spawnerDataInObject[i].velocityXSet; } - newSpawnerData.y = coordObject.getY() + spawnerDataInObject[i].y; + newSpawnerData.y(coordObject.getY() + spawnerDataInObject[i].y()); - newSpawnerData.x = coordObject.getX() + newSpawnerData.x; - newSpawnerData.z = coordObject.getZ() + newSpawnerData.z; + newSpawnerData.x(coordObject.getX() + newSpawnerData.x()); + newSpawnerData.z(coordObject.getZ() + newSpawnerData.z()); newSpawnerData.mobName = spawnerDataInObject[i].mobName; newSpawnerData.originalnbtFileName = spawnerDataInObject[i].originalnbtFileName; @@ -93,16 +93,16 @@ else if(coordObject.getRotation() == Rotation.EAST) spawnerData.add(newSpawnerData); - if(!ChunkCoordinate.fromBlockCoords(newSpawnerData.x, newSpawnerData.z).equals(chunkCoordinate)) + if(!ChunkCoordinate.fromBlockCoords(newSpawnerData.x(), newSpawnerData.z()).equals(chunkCoordinate)) { throw new RuntimeException(); // TODO: Remove after testing } } else { - newSpawnerData.y = coordObject.getY() + spawnerDataInObject[i].y; + newSpawnerData.y(coordObject.getY() + spawnerDataInObject[i].y()); - newSpawnerData.x = coordObject.getX() + spawnerDataInObject[i].x; - newSpawnerData.z = coordObject.getZ() + spawnerDataInObject[i].z; + newSpawnerData.x(coordObject.getX() + spawnerDataInObject[i].x()); + newSpawnerData.z(coordObject.getZ() + spawnerDataInObject[i].z()); newSpawnerData.mobName = spawnerDataInObject[i].mobName; newSpawnerData.originalnbtFileName = spawnerDataInObject[i].originalnbtFileName; @@ -127,7 +127,7 @@ else if(coordObject.getRotation() == Rotation.EAST) spawnerData.add(newSpawnerData); - if(!ChunkCoordinate.fromBlockCoords(newSpawnerData.x, newSpawnerData.z).equals(chunkCoordinate)) + if(!ChunkCoordinate.fromBlockCoords(newSpawnerData.x(), newSpawnerData.z()).equals(chunkCoordinate)) { throw new RuntimeException(); // TODO: Remove after testing } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/StructureDataRegion.java b/common/src/main/java/com/pg85/otg/customobjects/structures/StructureDataRegion.java index 6f3889189..8f059d745 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/StructureDataRegion.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/StructureDataRegion.java @@ -1,33 +1,47 @@ package com.pg85.otg.customobjects.structures; +import static com.pg85.otg.customobjects.structures.CustomStructureCache.REGION_SIZE; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Map; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import com.pg85.otg.util.ChunkCoordinate; + public class StructureDataRegion { - private boolean requiresSave = false; - private CustomStructure[][] structures = new CustomStructure[CustomStructureCache.REGION_SIZE][CustomStructureCache.REGION_SIZE]; - - public boolean requiresSave() - { - return this.requiresSave; - } - - public void markSaved() - { - this.requiresSave = false; - } - - public void markSaveRequired() - { - this.requiresSave = true; - } - - public CustomStructure getStructure(int internalX, int internalZ) - { - return this.structures[internalX][internalZ]; - } - - public void setStructure(int internalX, int internalZ, CustomStructure structure, boolean requiresSave) - { - this.structures[internalX][internalZ] = structure; - this.requiresSave = this.requiresSave || requiresSave; - } + private boolean requiresSave = false; + private CustomStructure[] structures = new CustomStructure[REGION_SIZE * REGION_SIZE]; + + public boolean requiresSave() + { + return this.requiresSave; + } + + public void markSaved() + { + this.requiresSave = false; + } + + public void markSaveRequired() + { + this.requiresSave = true; + } + + public CustomStructure getStructure(int internalX, int internalZ) + { + return this.structures[internalX * REGION_SIZE + internalZ]; + } + + public void setStructure(int internalX, int internalZ, CustomStructure structure, boolean requiresSave) + { + this.structures[internalX * REGION_SIZE + internalZ] = structure; + this.requiresSave = this.requiresSave || requiresSave; + } + + public Stream> getStructures() + { + return IntStream.range(0, REGION_SIZE * REGION_SIZE).filter(i -> structures[i] != null).mapToObj(i -> new SimpleEntry<>(ChunkCoordinate.fromChunkCoords(i / REGION_SIZE, i % REGION_SIZE), structures[i])); + } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructure.java b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructure.java index 94cfa2023..8a068284f 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructure.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructure.java @@ -2258,4 +2258,14 @@ public void spawnInChunk(ChunkCoordinate chunkCoordinate, LocalWorld world, Chun world.getStructureCache().markRegionForSaving(ChunkCoordinate.fromChunkCoords(this.start.getChunkX(), this.start.getChunkZ()).toRegionCoord()); world.getStructureCache().markRegionForSaving(chunkCoordinate.toRegionCoord()); } + + public Map> getObjectsToSpawn() + { + return objectsToSpawn; + } + + public Map> getSmoothingAreasToSpawn() + { + return smoothingAreaManager.smoothingAreasToSpawn; + } } diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructureCoordinate.java b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructureCoordinate.java index 3c45933f5..e546db336 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructureCoordinate.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/BO4CustomStructureCoordinate.java @@ -1,12 +1,18 @@ package com.pg85.otg.customobjects.structures.bo4; +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.IOException; + import com.pg85.otg.OTG; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.logging.LogMarker; -import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.customobjects.CustomObject; import com.pg85.otg.customobjects.structures.CustomStructureCoordinate; import com.pg85.otg.customobjects.structures.StructuredCustomObject; +import com.pg85.otg.logging.LogMarker; +import com.pg85.otg.util.DataUtil.IOFunction; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.helpers.StreamHelper; /** * Represents an object along with its location in the world. @@ -19,7 +25,7 @@ public class BO4CustomStructureCoordinate extends CustomStructureCoordinate boolean isWeightedBranch; String branchGroup; - public BO4CustomStructureCoordinate(LocalWorld world, StructuredCustomObject object, String customObjectName, Rotation rotation, int x, short y, int z, int branchDepth, boolean isRequiredBranch, boolean isWeightedBranch, String branchGroup) + public BO4CustomStructureCoordinate(LocalWorld world, StructuredCustomObject object, String customObjectName, Rotation rotation, int x, int y, int z, int branchDepth, boolean isRequiredBranch, boolean isWeightedBranch, String branchGroup) { this.worldName = world != null ? world.getName() : null; this.bo3Name = object != null ? object.getName() : customObjectName != null && customObjectName.length() > 0 ? customObjectName : null; @@ -33,7 +39,22 @@ public BO4CustomStructureCoordinate(LocalWorld world, StructuredCustomObject obj this.isWeightedBranch = isWeightedBranch; this.branchGroup = branchGroup; } - + + public static IOFunction reader(LocalWorld world) throws IOException + { + return in -> read(world, in); + } + + public static BO4CustomStructureCoordinate read(LocalWorld world, DataInput in) throws IOException + { + String bo3Name = StreamHelper.readStringFromStream(in); + Rotation coordRotation = Rotation.getRotation(in.readInt()); + int coordX = in.readInt(); + int coordY = in.readInt(); + int coordZ = in.readInt(); + return new BO4CustomStructureCoordinate(world, null, bo3Name, coordRotation, coordX, (short) coordY, coordZ, 0, false, false, null); + } + /** * Returns the object of this coordinate. * @@ -73,13 +94,7 @@ public StructuredCustomObject getStructuredObject() { return (StructuredCustomObject)getObject(); } - - @Override - public int hashCode() - { - return (x >> 13) ^ (y >> 7) ^ z ^ object.getName().hashCode() ^ rotation.toString().hashCode(); - } - + @Override public boolean equals(Object otherObject) { @@ -209,12 +224,12 @@ public static BO4CustomStructureCoordinate getRotatedBO3Coords(int x, int y, int } // TODO: Why is this necessary for smoothing areas? - public static BO4CustomStructureCoordinate getRotatedSmoothingCoords(int x, short y, int z, Rotation newRotation) + public static BO4CustomStructureCoordinate getRotatedSmoothingCoords(int x, int y, int z, Rotation newRotation) { // Assuming initial rotation is always north int newX = 0; - short newY = 0; + int newY = 0; int newZ = 0; int rotations = 0; diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/CustomStructurePlotter.java b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/CustomStructurePlotter.java index d14925924..1264801c4 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/CustomStructurePlotter.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/CustomStructurePlotter.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; @@ -16,12 +17,11 @@ import com.pg85.otg.customobjects.structures.CustomStructureFileManager; import com.pg85.otg.customobjects.structures.PlottedChunksRegion; import com.pg85.otg.customobjects.structures.StructuredCustomObject; -import com.pg85.otg.customobjects.structures.bo4.BO4CustomStructure; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.generator.resource.CustomStructureGen; import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; import com.pg85.otg.util.bo3.Rotation; public class CustomStructurePlotter @@ -39,8 +39,8 @@ public class CustomStructurePlotter // Used to find distance between structures and structure groups, only stores 1 chunk per structure in the // calculated center of the structure. Does not clean itself when used with the pre-generator and will become // slower as it fills up, use as little as possible! (can't clean itself because max radius for BO4 groups cannot be known) - private final HashMap> spawnedStructuresByName; // structure name -> start chunk coords. Saved to disk. - private final HashMap> spawnedStructuresByGroup; // group name -> Map. Saved to disk. + private final Map> spawnedStructuresByName; // structure name -> start chunk coords. Saved to disk. + private final Map> spawnedStructuresByGroup; // group name -> Map. Saved to disk. // Locking objects to ensure plotting code can // never run multiple times asynchronously, or recursively. @@ -51,18 +51,18 @@ public class CustomStructurePlotter private boolean structurePlottedAtSpawn; // Used by ObjectSpawner to make sure the structureatspawn is plotted first. // Non-persistent caches (optimisations) - private final FifoMap> structureNamesPerChunk; - private final FifoMap plottedChunksFastCache; // TODO: Technically we don't need a map, we need a FIFO list with unique entries. + private final LRUCache> structureNamesPerChunk; + private final LRUCache plottedChunksFastCache; // TODO: Technically we don't need a map, we need a FIFO list with unique entries. public CustomStructurePlotter() { // Non-persistent caches - this.structureNamesPerChunk = new FifoMap>(2048); - this.plottedChunksFastCache = new FifoMap(2048); + this.structureNamesPerChunk = new LRUCache>(2048); + this.plottedChunksFastCache = new LRUCache(2048); // Persistent caches - this.spawnedStructuresByName = new HashMap>(); - this.spawnedStructuresByGroup = new HashMap>(); + this.spawnedStructuresByName = new HashMap<>(); + this.spawnedStructuresByGroup = new HashMap<>(); this.bo4StructureCache = new HashMap(); this.plottedChunks = new HashMap(); } @@ -970,7 +970,7 @@ else if(structureBBInsideAreaZ + structureWidthRotated < chunkCoord.getChunkZ()) String bO3Name = ((BO4)currentStructureSpawning[0]).getName(); ChunkCoordinate bo4SpawnCoord = ChunkCoordinate.fromChunkCoords(spawnCoordX, spawnCoordZ); - ArrayList chunkCoords = this.spawnedStructuresByName.get(bO3Name); + List chunkCoords = this.spawnedStructuresByName.get(bO3Name); if(chunkCoords == null) { chunkCoords = new ArrayList(); @@ -990,7 +990,7 @@ else if(structureBBInsideAreaZ + structureWidthRotated < chunkCoord.getChunkZ()) int bo4GroupFrequency = entry.getValue().intValue(); if(bo4GroupFrequency > 0) { - HashMap spawnedStructures = this.spawnedStructuresByGroup.get(bo4GroupName); + Map spawnedStructures = this.spawnedStructuresByGroup.get(bo4GroupName); if(spawnedStructures == null) { spawnedStructures = new HashMap(); @@ -1057,7 +1057,7 @@ private boolean isBO4AllowedToSpawnAtByFrequency(ChunkCoordinate chunkCoord, BO4 { float distanceBetweenStructures = 0; - ArrayList chunkCoords = spawnedStructuresByName.get(bO3Name); + List chunkCoords = spawnedStructuresByName.get(bO3Name); if(chunkCoords != null) { // Check BO3 frequency @@ -1082,7 +1082,7 @@ private boolean isBO4AllowedToSpawnAtByFrequency(ChunkCoordinate chunkCoord, BO4 ChunkCoordinate cachedChunk = null; for(Entry entry : BO3ToSpawn.getConfig().bo4Groups.entrySet()) { - HashMap spawnedStructure = spawnedStructuresByGroup.get(entry.getKey()); + Map spawnedStructure = spawnedStructuresByGroup.get(entry.getKey()); if(spawnedStructure != null) { for(Entry cachedChunkEntry : spawnedStructure.entrySet()) @@ -1114,8 +1114,7 @@ public void savePlottedChunks(LocalWorld world) public void loadPlottedChunks(LocalWorld world) { - this.plottedChunks.clear(); - this.plottedChunks.putAll(CustomStructureFileManager.loadPlottedChunksData(world)); + CustomStructureFileManager.loadPlottedChunksData(world, this.plottedChunks); } public void saveSpawnedStructures(LocalWorld world) @@ -1137,7 +1136,7 @@ public void saveStructureCache(LocalWorld world) } } - public void loadStructureCache(LocalWorld world, Map> loadedStructures) + public void loadStructureCache(LocalWorld world, Map> loadedStructures) { this.bo4StructureCache.clear(); @@ -1145,7 +1144,7 @@ public void loadStructureCache(LocalWorld world, Map> loadedStructure : loadedStructures.entrySet()) + for(Entry> loadedStructure : loadedStructures.entrySet()) { if(loadedStructure == null) { diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/BlockCoordsAndNeighbours.java b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/BlockCoordsAndNeighbours.java index c408d41e6..d15a5d1bb 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/BlockCoordsAndNeighbours.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/BlockCoordsAndNeighbours.java @@ -6,14 +6,14 @@ class BlockCoordsAndNeighbours { public BO4CustomStructureCoordinate bO3; public int blockX; - public short blockY; + public int blockY; public int blockZ; public boolean smoothInDirection1; public boolean smoothInDirection2; public boolean smoothInDirection3; public boolean smoothInDirection4; - public BlockCoordsAndNeighbours(BO4CustomStructureCoordinate bO3, int blockX, short blockY, int blockZ, boolean smoothInDirection1, boolean smoothInDirection2, boolean smoothInDirection3, boolean smoothInDirection4) + public BlockCoordsAndNeighbours(BO4CustomStructureCoordinate bO3, int blockX, int blockY, int blockZ, boolean smoothInDirection1, boolean smoothInDirection2, boolean smoothInDirection3, boolean smoothInDirection4) { this.bO3 = bO3; this.blockX = blockX; diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaGenerator.java b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaGenerator.java index bb9c084ab..7f0ea3757 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaGenerator.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaGenerator.java @@ -7,6 +7,7 @@ import java.util.Stack; import java.util.Map.Entry; +import com.pg85.otg.common.BlockContainer; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; import com.pg85.otg.customobjects.bo4.BO4; @@ -117,19 +118,19 @@ public void calculateSmoothingAreas(Map= 0 && heightMap[block.x - 1][block.z] != null) + if(block.x() - 1 >= 0 && heightMap[block.x() - 1][block.z()] != null) { bFoundNeighbour1 = true; } - if(block.x + 1 <= 15 && heightMap[block.x + 1][block.z] != null) + if(block.x() + 1 <= 15 && heightMap[block.x() + 1][block.z()] != null) { bFoundNeighbour2 = true; } - if(block.z - 1 >= 0 && heightMap[block.x][block.z - 1] != null) + if(block.z() - 1 >= 0 && heightMap[block.x()][block.z() - 1] != null) { bFoundNeighbour3 = true; } - if(block.z + 1 <= 15 && heightMap[block.x][block.z + 1] != null) + if(block.z() + 1 <= 15 && heightMap[block.x()][block.z() + 1] != null) { bFoundNeighbour4 = true; } @@ -138,44 +139,44 @@ public void calculateSmoothingAreas(Map 15) + if(!bFoundNeighbour2 && block.x() + 1 > 15) { // Check if the BO4 contains a block at the location of the neighbouring block //Normalize the coordinates of the neigbouring block taking into consideration rotation - neighbouringBlockCoords = BO4CustomStructureCoordinate.getRotatedSmoothingCoords(block.x + 1, block.y, block.z, objectInChunk.getRotation()); + neighbouringBlockCoords = BO4CustomStructureCoordinate.getRotatedSmoothingCoords(block.x() + 1, block.y(), block.z(), objectInChunk.getRotation()); normalizedNeigbouringBlockX = neighbouringBlockCoords.getX() + (objectInChunk.getX()); normalizedNeigbouringBlockY = neighbouringBlockCoords.getY() + objectInChunk.getY(); normalizedNeigbouringBlockZ = neighbouringBlockCoords.getZ() + (objectInChunk.getZ()); bFoundNeighbour2 = findNeighbouringBlock(SmoothStartTop, normalizedNeigbouringBlockX, normalizedNeigbouringBlockY, normalizedNeigbouringBlockZ, objectsToSpawn, objectInChunk, start); } - if(!bFoundNeighbour3 && block.z - 1 < 0) + if(!bFoundNeighbour3 && block.z() - 1 < 0) { // Check if the BO4 contains a block at the location of the neighbouring block //Normalize the coordinates of the neigbouring block taking into consideration rotation - neighbouringBlockCoords = BO4CustomStructureCoordinate.getRotatedSmoothingCoords(block.x, block.y, block.z - 1, objectInChunk.getRotation()); + neighbouringBlockCoords = BO4CustomStructureCoordinate.getRotatedSmoothingCoords(block.x(), block.y(), block.z() - 1, objectInChunk.getRotation()); normalizedNeigbouringBlockX = neighbouringBlockCoords.getX() + (objectInChunk.getX()); normalizedNeigbouringBlockY = neighbouringBlockCoords.getY() + objectInChunk.getY(); normalizedNeigbouringBlockZ = neighbouringBlockCoords.getZ() + (objectInChunk.getZ()); bFoundNeighbour3 = findNeighbouringBlock(SmoothStartTop, normalizedNeigbouringBlockX, normalizedNeigbouringBlockY, normalizedNeigbouringBlockZ, objectsToSpawn, objectInChunk, start); } - if(!bFoundNeighbour4 && block.z + 1 > 15) + if(!bFoundNeighbour4 && block.z() + 1 > 15) { // Check if the BO4 contains a block at the location of the neighbouring block // Normalize the coordinates of the neighbouring block taking into consideration rotation - neighbouringBlockCoords = BO4CustomStructureCoordinate.getRotatedSmoothingCoords(block.x, (short)block.y, block.z + 1, objectInChunk.getRotation()); + neighbouringBlockCoords = BO4CustomStructureCoordinate.getRotatedSmoothingCoords(block.x(), (short)block.y(), block.z() + 1, objectInChunk.getRotation()); normalizedNeigbouringBlockX = neighbouringBlockCoords.getX() + (objectInChunk.getX()); normalizedNeigbouringBlockY = neighbouringBlockCoords.getY() + objectInChunk.getY(); normalizedNeigbouringBlockZ = neighbouringBlockCoords.getZ() + (objectInChunk.getZ()); @@ -207,21 +208,21 @@ public void calculateSmoothingAreas(Map> smoothingAreasToSpawn, BO4CustomStructureCoordinate bO4, int direction) + void plotStraightLine(LocalWorld world, int blockX, int blockY, int blockZ, int smoothRadius, Map> smoothingAreasToSpawn, BO4CustomStructureCoordinate bO4, int direction) { int normalizedSmoothStartPointBlockX = 0; int normalizedSmoothStartPointBlockZ = 0; @@ -506,7 +508,7 @@ void plotStraightLine(LocalWorld world, int blockX, short blockY, int blockZ, in boolean bFound; ArrayList smoothingAreasToSpawnForThisBlock = new ArrayList(); int beginPointX; - short beginPointY; + int beginPointY; int beginPointZ; int endPointX; int endPointZ; diff --git a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaLine.java b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaLine.java index 9b7c1bec5..ff905f355 100644 --- a/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaLine.java +++ b/common/src/main/java/com/pg85/otg/customobjects/structures/bo4/smoothing/SmoothingAreaLine.java @@ -1,26 +1,30 @@ package com.pg85.otg.customobjects.structures.bo4.smoothing; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + public class SmoothingAreaLine { public int beginPointX; - public short beginPointY = -1; + public int beginPointY = -1; public int beginPointZ; public int endPointX; - public short endPointY = -1; + public int endPointY = -1; public int endPointZ; public int originPointX; - public short originPointY = -1; + public int originPointY = -1; public int originPointZ; public int finalDestinationPointX; - public short finalDestinationPointY = -1; + public int finalDestinationPointY = -1; public int finalDestinationPointZ; public SmoothingAreaLine() {} - public SmoothingAreaLine(int beginPointX, short beginPointY, int beginPointZ, int endPointX, short endPointY, int endPointZ, int originPointX, short originPointY, int originPointZ, int finalDestinationPointX, short finalDestinationPointY, int finalDestinationPointZ) + public SmoothingAreaLine(int beginPointX, int beginPointY, int beginPointZ, int endPointX, int endPointY, int endPointZ, int originPointX, int originPointY, int originPointZ, int finalDestinationPointX, int finalDestinationPointY, int finalDestinationPointZ) { this(beginPointX, beginPointZ, endPointX, endPointZ, originPointX, originPointY, originPointZ, finalDestinationPointX, finalDestinationPointZ); this.beginPointY = beginPointY; @@ -28,7 +32,7 @@ public SmoothingAreaLine(int beginPointX, short beginPointY, int beginPointZ, in this.finalDestinationPointY = finalDestinationPointY; } - public SmoothingAreaLine(int beginPointX, int beginPointZ, int endPointX, int endPointZ, int originPointX, short originPointY, int originPointZ, int finalDestinationPointX, int finalDestinationPointZ) + public SmoothingAreaLine(int beginPointX, int beginPointZ, int endPointX, int endPointZ, int originPointX, int originPointY, int originPointZ, int finalDestinationPointX, int finalDestinationPointZ) { this.beginPointX = beginPointX; this.beginPointZ = beginPointZ; @@ -43,4 +47,45 @@ public SmoothingAreaLine(int beginPointX, int beginPointZ, int endPointX, int en this.finalDestinationPointX = finalDestinationPointX; this.finalDestinationPointZ = finalDestinationPointZ; } + + public static void write(DataOutput out, SmoothingAreaLine line) throws IOException + { + // TODO: Should only need origin and destination? + out.writeInt(line.beginPointX); + out.writeInt(line.beginPointY); + out.writeInt(line.beginPointZ); + + out.writeInt(line.endPointX); + out.writeInt(line.endPointY); + out.writeInt(line.endPointZ); + + out.writeInt(line.originPointX); + out.writeInt(line.originPointY); + out.writeInt(line.originPointZ); + + out.writeInt(line.finalDestinationPointX); + out.writeInt(line.finalDestinationPointY); + out.writeInt(line.finalDestinationPointZ); + } + + public static SmoothingAreaLine read(DataInput in) throws IOException + { + int beginPointX = in.readInt(); + int beginPointY = in.readInt(); + int beginPointZ = in.readInt(); + + int endPointX = in.readInt(); + int endPointY = in.readInt(); + int endPointZ = in.readInt(); + + int originPointX = in.readInt(); + int originPointY = in.readInt(); + int originPointZ = in.readInt(); + + int finalDestinationPointX = in.readInt(); + int finalDestinationPointY = in.readInt(); + int finalDestinationPointZ = in.readInt(); + + return new SmoothingAreaLine(beginPointX, (short) beginPointY, beginPointZ, endPointX, (short) endPointY, endPointZ, originPointX, (short) originPointY, originPointZ, finalDestinationPointX, (short) finalDestinationPointY, finalDestinationPointZ); + } } \ No newline at end of file diff --git a/common/src/main/java/com/pg85/otg/exception/InvalidConfigException.java b/common/src/main/java/com/pg85/otg/exception/InvalidConfigException.java index 8bfa6b066..6ea601948 100644 --- a/common/src/main/java/com/pg85/otg/exception/InvalidConfigException.java +++ b/common/src/main/java/com/pg85/otg/exception/InvalidConfigException.java @@ -3,10 +3,13 @@ @SuppressWarnings("serial") // No need to serialize this public class InvalidConfigException extends Exception { - - public InvalidConfigException(String string) + public InvalidConfigException(String message) { - super(string); + super(message); } + public InvalidConfigException(String message, Throwable cause) + { + super(message, cause); + } } diff --git a/common/src/main/java/com/pg85/otg/generator/ObjectSpawner.java b/common/src/main/java/com/pg85/otg/generator/ObjectSpawner.java index 3026c226d..60a5196f6 100644 --- a/common/src/main/java/com/pg85/otg/generator/ObjectSpawner.java +++ b/common/src/main/java/com/pg85/otg/generator/ObjectSpawner.java @@ -42,16 +42,15 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Random; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; public class ObjectSpawner { // Locking objects / checks to prevent populate running on multiple threads, // or when the world is waiting for an opportunity to save. - // TODO: Make this prettier - public Object lockingObject = new Object(); - public boolean populating; + public final Lock lock = new ReentrantLock(); public boolean processing = false; - public boolean saving; public boolean saveRequired; // @@ -68,71 +67,36 @@ public ObjectSpawner(ConfigProvider configProvider, LocalWorld localWorld) public void populate(ChunkCoordinate chunkCoord) { - boolean unlockWhenDone = false; - // Wait for another thread running SaveToDisk, then place a lock. - boolean firstLog = false; - while(true) - { - //OTG.log(LogMarker.INFO, "Populate waiting on SaveToDisk."); - synchronized(this.lockingObject) - { - if(!this.saving) - { - // If populating then this method is being called recursively (indicating cascading chunk-gen). - // This method can be called recursively, but should never be called by two threads at once. - // TODO: Make sure that's the case. - if(!this.populating) - { - this.populating = true; - unlockWhenDone = true; - } - break; - } else { - if(firstLog) - { - OTG.log(LogMarker.WARN, "Populate waiting on SaveToDisk. Although other mods could be causing this and there may not be any problem, this can potentially cause an endless loop!"); - firstLog = false; - } - } - } - } - synchronized(this.lockingObject) - { - this.saveRequired = true; - } - - if (!this.processing) - { - this.processing = true; - - // Cache all biomes in the are being populated (2x2 chunks) - this.world.cacheBiomesForPopulation(chunkCoord); - doPopulate(chunkCoord); - - this.processing = false; - } else { - - // Don't use the population chunk biome cache during cascading chunk generation - this.world.invalidatePopulationBiomeCache(); - doPopulate(chunkCoord); - - OTG.log(LogMarker.INFO, "Cascading chunk generation detected."); - if(OTG.getPluginConfig().developerMode) - { - OTG.log(LogMarker.INFO, Arrays.toString(Thread.currentThread().getStackTrace())); - } - } - - // Release the lock - synchronized(this.lockingObject) - { - // This assumes that this method can only be called alone or recursively, never by 2 threads at once. - // TODO: Make sure that's the case. - if(unlockWhenDone) - { - this.populating = false; - } - } + lock.lock(); + try + { + if(!this.processing) + { + this.processing = true; + + // Cache all biomes in the are being populated (2x2 chunks) + this.world.cacheBiomesForPopulation(chunkCoord); + doPopulate(chunkCoord); + + this.processing = false; + } + else + { + // Don't use the population chunk biome cache during cascading chunk generation + this.world.invalidatePopulationBiomeCache(); + doPopulate(chunkCoord); + + OTG.log(LogMarker.INFO, "Cascading chunk generation detected."); + if(OTG.getPluginConfig().developerMode) + { + OTG.log(LogMarker.INFO, Arrays.toString(Thread.currentThread().getStackTrace())); + } + } + } + finally + { + lock.unlock(); + } // Resource spawning may have changed terrain dramatically, update // the spawnpoint so players don't spawn mid-air or underground diff --git a/common/src/main/java/com/pg85/otg/generator/biome/CachedBiomeGenerator.java b/common/src/main/java/com/pg85/otg/generator/biome/CachedBiomeGenerator.java index 21b59633f..5fe55f158 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/CachedBiomeGenerator.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/CachedBiomeGenerator.java @@ -2,7 +2,7 @@ import com.pg85.otg.common.LocalWorld; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; /** * Wraps uncached biome generators. @@ -44,7 +44,7 @@ int getCalculatedBiomeId(int blockX, int blockZ) /** * The map of cached BiomeCacheBlocks. */ - private FifoMap cacheMap = new FifoMap(4096); // TODO: This gets slow at large sizes, test/profile to find the best size. + private LRUCache cacheMap = new LRUCache(4096); // TODO: This gets slow at large sizes, test/profile to find the best size. /** * The uncached biome generator. */ diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/Layer.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/Layer.java index 278748b78..24ccab963 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/Layer.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/Layer.java @@ -1,7 +1,10 @@ package com.pg85.otg.generator.biome.layers; +import com.pg85.otg.OTG; +import com.pg85.otg.common.LocalBiome; import com.pg85.otg.common.LocalWorld; import com.pg85.otg.generator.biome.ArraysCache; +import com.pg85.otg.logging.LogMarker; /** * Layer is the abstract base class for the entire layering system. @@ -37,32 +40,7 @@ */ public abstract class Layer { - - /** - * The base seed set during layer construction, all other seeds are based - * upon this one. - */ - private long baseSeed; - - /** - * A general seed kept for use in world generation - * @see #initWorldGenSeed(long) - */ - protected long scrambledWorldSeed; - - /** - * This seed is used for general random number generation within the Layers - * system. It is based off of both the scrambledWorldSeed and baseSeed. - * @see #initWorldGenSeed(long) - * @see #initChunkSeed(long, long) - */ - private long scrambledChunkSeed; - - /** - * This seed is used for generating random numbers for biome groups - * @see #initGroupSeed(long, long) - */ - private long scrambledGroupSeed; + private final LayerRNG rng; /** * The layer to process before this one. getInts() should call @@ -70,11 +48,6 @@ public abstract class Layer */ protected Layer child; - /** - * This helps our random numbers be a little more random - */ - protected static final int Entropy = 10000; - /* * LayerIsland - chance to big land * LayerLandRandom - a(3) - chance to increase big land @@ -120,40 +93,29 @@ public abstract class Layer protected static final int RiverBitOne = (1 << RiverShift); //>> 22st Bit, 1048576 protected static final int RiverBitTwo = (1 << (RiverShift + 1)); //>> 23nd Bit, 2097152 - private static long getScrambledBaseSeed(long baseSeed) - { - long scrambledBaseSeed = baseSeed; - scrambledBaseSeed *= (scrambledBaseSeed * 6364136223846793005L + 1442695040888963407L); - scrambledBaseSeed += baseSeed; - scrambledBaseSeed *= (scrambledBaseSeed * 6364136223846793005L + 1442695040888963407L); - scrambledBaseSeed += baseSeed; - scrambledBaseSeed *= (scrambledBaseSeed * 6364136223846793005L + 1442695040888963407L); - scrambledBaseSeed += baseSeed; - return scrambledBaseSeed; - } + protected final int defaultOceanId; - protected static long getScrambledWorldSeed(long baseSeed, long worldSeed) + protected Layer(long seed, LocalWorld world) { - long scrambledBaseSeed = getScrambledBaseSeed(baseSeed); - long scrambledWorldSeed = worldSeed; - scrambledWorldSeed *= (scrambledWorldSeed * 6364136223846793005L + 1442695040888963407L); - scrambledWorldSeed += scrambledBaseSeed; - scrambledWorldSeed *= (scrambledWorldSeed * 6364136223846793005L + 1442695040888963407L); - scrambledWorldSeed += scrambledBaseSeed; - scrambledWorldSeed *= (scrambledWorldSeed * 6364136223846793005L + 1442695040888963407L); - scrambledWorldSeed += scrambledBaseSeed; - return scrambledWorldSeed; + this.rng = LayerRNG.create(world.getConfigs().getWorldSaveData()); + this.rng.setLayerSeed(seed); + this.defaultOceanId = getBiomeId(world, world.getConfigs().getWorldConfig().defaultOceanBiome, "DefaultOceanBiome"); } - protected int defaultOceanId; - protected Layer(long seed, int defaultOceanId) + protected static int getBiomeId(LocalWorld world, String id, String name) { - this.baseSeed = seed; - this.defaultOceanId = defaultOceanId; - } + LocalBiome biome = world.getBiomeByNameOrNull(id); + if(biome == null) + { + biome = world.getFirstBiomeOrNull(); + if(biome == null) + { + throw new RuntimeException(String.format("Could not find '%s' \"%s\", aborting.", name, id)); + } + OTG.log(LogMarker.WARN, "Could not find '%s' \"%s\", substituting \"%s\".", name, id, biome.getName()); + } - public Layer() - { + return biome.getIds().getOTGBiomeId(); } public void initWorldGenSeed(long worldSeed) @@ -161,74 +123,98 @@ public void initWorldGenSeed(long worldSeed) if (this.child != null) this.child.initWorldGenSeed(worldSeed); - this.scrambledWorldSeed = getScrambledWorldSeed(this.baseSeed, worldSeed); + this.rng.setWorldSeed(worldSeed); } + protected void initChunkSeed(int x, int z) + { + this.rng.initChunkSeed(x, z); + } + + @Deprecated protected void initChunkSeed(long x, long z) { - this.scrambledChunkSeed = this.scrambledWorldSeed; - this.scrambledChunkSeed *= (this.scrambledChunkSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledChunkSeed += x; - this.scrambledChunkSeed *= (this.scrambledChunkSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledChunkSeed += z; - this.scrambledChunkSeed *= (this.scrambledChunkSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledChunkSeed += x; - this.scrambledChunkSeed *= (this.scrambledChunkSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledChunkSeed += z; + this.rng.initChunkSeed(x, z); } + @Deprecated protected void initGroupSeed(long x, long z) { - this.scrambledGroupSeed = this.scrambledChunkSeed; - this.scrambledGroupSeed *= (this.scrambledGroupSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledGroupSeed += x; - this.scrambledGroupSeed *= (this.scrambledGroupSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledGroupSeed += z; - this.scrambledGroupSeed *= (this.scrambledGroupSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledGroupSeed += x; - this.scrambledGroupSeed *= (this.scrambledGroupSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledGroupSeed += z; + this.rng.initGroupSeed(x, z); } protected int nextInt(int x) { - int i = (int) ((this.scrambledChunkSeed >> 24) % x); - if (i < 0) - { - i += x; - } - this.scrambledChunkSeed *= (this.scrambledChunkSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledChunkSeed += this.scrambledWorldSeed; - return i; + return this.rng.nextChunkInt(x); } + @Deprecated protected int nextGroupInt(int x) { - int i = (int) ((this.scrambledGroupSeed >> 24) % x); - if (i < 0) - { - i += x; - } - this.scrambledGroupSeed *= (this.scrambledGroupSeed * 6364136223846793005L + 1442695040888963407L); - this.scrambledGroupSeed += this.scrambledChunkSeed; - return i; + return this.rng.nextGroupInt(x); + } + + @Deprecated + protected int nextGroupIntEntropy(int x) + { + return this.rng.nextGroupIntEntropy(x); } public abstract int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSize, int zSize); - protected int getRandomOf4(int a, int b, int c, int d) + protected int mostCommonOrRandom(int a, int b, int c, int d) { - if (b == c && c == d) return b; // b = c = d, a different - if (a == b && a == c) return a; // a = b = c, d different - if (a == b && a == d) return a; // a = b = d, c different - if (a == c && a == d) return a; // a = c = d, b different - - if (a == b && c != d) return a; - if (a == c && b != d) return a; - if (a == d && b != c) return a; - if (b == c && a != d) return b; - if (b == d && a != c) return b; - if (c == d && a != b) return c; + if(a == b) + { + // x x ? ? + if(a == c || c != d) + { + // x x x x + // x x x y + // x x y x + // x x y z + return a; + } + // x x y y + } + else if(a == c) + { + // x y x ? + if(b != d) + { + // x y x x + // x y x z + return a; + } + // x y x y + } + else if(a == d) + { + // x !x !x x + if(b != c) + { + // x y z x + return a; + } + // x y y x + } + else + { + // x !x !x !x + if(b == c || b == d) + { + // x y y y + // x y y z + // x y z y + return b; + } + if(c == d) + { + // x y z z + return c; + } + // x y z w + } switch (this.nextInt(4)){ case 0: return a; @@ -245,9 +231,6 @@ protected int getRandomOf4(int a, int b, int c, int d) */ protected int getBiomeFromLayer(int selection) { - return - (selection & LandBit) != 0 && (selection & BiomeBitsAreSetBit) != 0 ? - (selection & BiomeBits) : - this.defaultOceanId; + return (selection & (LandBit | BiomeBitsAreSetBit)) == (LandBit | BiomeBitsAreSetBit) ? (selection & BiomeBits) : this.defaultOceanId; } } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiome.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiome.java index 4621c70bb..16df0c0b3 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiome.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiome.java @@ -5,9 +5,7 @@ import com.pg85.otg.configuration.biome.BiomeGroup; import com.pg85.otg.configuration.biome.BiomeGroupManager; import com.pg85.otg.generator.biome.ArraysCache; - -import java.util.Map.Entry; -import java.util.SortedMap; +import com.pg85.otg.util.WeightedList; public class LayerBiome extends Layer { @@ -15,13 +13,13 @@ public class LayerBiome extends Layer private int depth; private double freezeTemp; - LayerBiome(long seed, int defaultOceanId, Layer childLayer, BiomeGroupManager groupManager, int depth, double freezeTemp) + LayerBiome(long seed, LocalWorld world, Layer childLayer, BiomeGroupManager groupManager, int depth) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; this.manager = groupManager; this.depth = depth; - this.freezeTemp = freezeTemp; + this.freezeTemp = world.getConfigs().getWorldConfig().frozenOceanTemperature; } @Override @@ -30,10 +28,7 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz int[] childInts = this.child.getInts(world, cache, x, z, xSize, zSize); int[] thisInts = cache.getArray(xSize * zSize); - SortedMap possibleBiomes; - BiomeGroup group; int currentPiece; - int newBiomeRarity; for (int i = 0; i < zSize; i++) { @@ -44,28 +39,18 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz if ((currentPiece & BiomeGroupBits) != 0 && ((currentPiece & BiomeBitsAreSetBit) == 0 || (currentPiece & BiomeBits) == this.defaultOceanId)) // has biomegroup bits but not biome bits { - group = manager.getGroupById((currentPiece & BiomeGroupBits) >> BiomeGroupShift); - possibleBiomes = group.getDepthMapOrHigher(depth); - // Get Max Rarity - if (!possibleBiomes.isEmpty()) + BiomeGroup group = manager.getGroupById((currentPiece & BiomeGroupBits) >> BiomeGroupShift); + WeightedList weightedBiomes = group.getDepthMapOrHigher(depth); + if(!weightedBiomes.isEmpty()) { - newBiomeRarity = nextInt(BiomeGroupManager.getMaxRarityFromPossibles(possibleBiomes)); - // Spawn the biome based on the rarity spectrum - for (Entry biome : possibleBiomes.entrySet()) - { - if (newBiomeRarity < biome.getKey()) - { - if (biome.getValue() != null && biome.getValue().getBiomeConfig().biomeSize == this.depth) - { - currentPiece |= biome.getValue().getIds().getOTGBiomeId() | - // Set IceBit based on Biome Temperature - (biome.getValue().getBiomeConfig().biomeTemperature <= freezeTemp ? IceBit : 0) | - // Set BiomeBitsAreSetBit - BiomeBitsAreSetBit - ; - } - break; - } + LocalBiome biome = weightedBiomes.getRandom(this::nextInt); + if (biome != null) { + currentPiece |= biome.getIds().getOTGBiomeId() | + // Set IceBit based on Biome Temperature + (biome.getBiomeConfig().biomeTemperature <= freezeTemp ? IceBit : 0) | + // Set BiomeBitsAreSetBit + BiomeBitsAreSetBit + ; } } } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBeforeGroups.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBeforeGroups.java index c37f7e101..7e590cc7e 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBeforeGroups.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBeforeGroups.java @@ -15,9 +15,9 @@ public class LayerBiomeBeforeGroups extends Layer private LocalBiome[] biomes; private LocalBiome[] ice_biomes; - LayerBiomeBeforeGroups(long seed, int defaultOceanId, Layer childLayer, LocalBiome[] biomes, LocalBiome[] ice_biomes) + LayerBiomeBeforeGroups(long seed, LocalWorld world, Layer childLayer, LocalBiome[] biomes, LocalBiome[] ice_biomes) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; this.biomes = biomes; this.ice_biomes = ice_biomes; diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBorder.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBorder.java index 0e0507a71..43e7803ad 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBorder.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeBorder.java @@ -9,9 +9,9 @@ public class LayerBiomeBorder extends Layer private boolean[][] bordersFrom; private int[] bordersTo; - LayerBiomeBorder(long seed, LocalWorld world, int defaultOceanId) + LayerBiomeBorder(long seed, LocalWorld world) { - super(seed, defaultOceanId); + super(seed, world); this.bordersFrom = new boolean[world.getMaxBiomesCount()][]; this.bordersTo = new int[world.getMaxBiomesCount()]; } @@ -31,63 +31,114 @@ void addBiome(LocalBiome replaceTo, int replaceFrom, LocalWorld world) @Override public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSize, int zSize) { - int[] childInts = this.child.getInts(world, cache, x - 1, z - 1, xSize + 2, zSize + 2); - int[] thisInts = cache.getArray(xSize * zSize); - - int selection; - int northCheck; - int southCheck; - int eastCheck; - int westCheck; - boolean[] biomeFrom; - int biomeId; - boolean improvedBiomeBorders = world.getConfigs().getWorldConfig().improvedBiomeBorders; - for (int zi = 0; zi < zSize; zi++) - { - for (int xi = 0; xi < xSize; xi++) - { - initChunkSeed(xi + x, zi + z); - selection = childInts[(xi + 1 + (zi + 1) * (xSize + 2))]; + int[] childBiomeData = this.child.getInts(world, cache, x - 1, z - 1, xSize + 2, zSize + 2); + int[] biomeData = cache.getArray(xSize * zSize); - biomeId = getBiomeFromLayer(selection); - if (bordersFrom[biomeId] != null) + if(world.getConfigs().getWorldConfig().improvedBiomeBorders) + { + for(int z1 = 0; z1 < zSize; z1++) + { + int biomeDataNC = childBiomeData[(z1 + 0) * (xSize + 2) + 0]; + int biomeDataNE = childBiomeData[(z1 + 0) * (xSize + 2) + 1]; + int biomeDataCC = childBiomeData[(z1 + 1) * (xSize + 2) + 0]; + int biomeDataCE = childBiomeData[(z1 + 1) * (xSize + 2) + 1]; + int biomeDataSC = childBiomeData[(z1 + 2) * (xSize + 2) + 0]; + int biomeDataSE = childBiomeData[(z1 + 2) * (xSize + 2) + 1]; + int biomeNW = 0; + int biomeNC = getBiomeFromLayer(biomeDataNC); + int biomeNE = getBiomeFromLayer(biomeDataNE); + int biomeCW = 0; + int biomeCC = getBiomeFromLayer(biomeDataCC); + int biomeCE = getBiomeFromLayer(biomeDataCE); + int biomeSW = 0; + int biomeSC = getBiomeFromLayer(biomeDataSC); + int biomeSE = getBiomeFromLayer(biomeDataSE); + for(int x1 = 0; x1 < xSize; x1++) { - // check in a plus formation to see if the tile is suitable for an edge biome - northCheck = getBiomeFromLayer(childInts[(xi + 1 + (zi) * (xSize + 2))]); - southCheck = getBiomeFromLayer(childInts[(xi + 1 + (zi + 2) * (xSize + 2))]); - eastCheck = getBiomeFromLayer(childInts[(xi + 2 + (zi + 1) * (xSize + 2))]); - westCheck = getBiomeFromLayer(childInts[(xi + (zi + 1) * (xSize + 2))]); - - biomeFrom = bordersFrom[biomeId]; - if (biomeFrom[northCheck] && biomeFrom[eastCheck] && biomeFrom[westCheck] && biomeFrom[southCheck]) + biomeDataNC = biomeDataNE; + biomeDataNE = childBiomeData[(z1 + 0) * (xSize + 2) + (x1 + 2)]; + biomeDataCC = biomeDataCE; + biomeDataCE = childBiomeData[(z1 + 1) * (xSize + 2) + (x1 + 2)]; + biomeDataSC = biomeDataSE; + biomeDataSE = childBiomeData[(z1 + 2) * (xSize + 2) + (x1 + 2)]; + biomeNW = biomeNC; + biomeNC = biomeNE; + biomeNE = getBiomeFromLayer(biomeDataNE); + biomeCW = biomeCC; + biomeCC = biomeCE; + biomeCE = getBiomeFromLayer(biomeDataCE); + biomeSW = biomeSC; + biomeSC = biomeSE; + biomeSE = getBiomeFromLayer(biomeDataSE); + + int biomeDataAt = biomeDataCC; + boolean[] biomeFrom; + if((biomeFrom = bordersFrom[biomeCC]) != null) { - if ((northCheck != biomeId) || (eastCheck != biomeId) || (westCheck != biomeId) || (southCheck != biomeId)) - { - // if it is suitable, set the edge biome - selection = (selection & (IslandBit | RiverBits | IceBit)) | LandBit | bordersTo[biomeId] | BiomeBitsAreSetBit; - } - else if(improvedBiomeBorders) + if(biomeFrom[biomeCW] && biomeFrom[biomeCE] && biomeFrom[biomeNC] && biomeFrom[biomeSC]) { - // if it's not suitable, try again but sample in an X formation to make sure we didn't miss any potential edge - int nwCheck = getBiomeFromLayer(childInts[(xi + 0 + (zi) * (xSize + 2))]); - int neCheck = getBiomeFromLayer(childInts[(xi + 2 + (zi) * (xSize + 2))]); - int swCheck = getBiomeFromLayer(childInts[(xi + 0 + (zi + 2) * (xSize + 2))]); - int seCheck = getBiomeFromLayer(childInts[(xi + 2 + (zi + 2) * (xSize + 2))]); - if (biomeFrom[nwCheck] && biomeFrom[neCheck] && biomeFrom[swCheck] && biomeFrom[seCheck]) + if(biomeCW != biomeCC || biomeCE != biomeCC || biomeNC != biomeCC || biomeSC != biomeCC) { - if ((nwCheck != biomeId) || (neCheck != biomeId) || (swCheck != biomeId) || (seCheck != biomeId)) + biomeDataAt = (biomeDataAt & (IslandBit | RiverBits | IceBit)) | LandBit | bordersTo[biomeCC] | BiomeBitsAreSetBit; + } + else + { + if(biomeFrom[biomeNW] && biomeFrom[biomeNE] && biomeFrom[biomeSW] && biomeFrom[biomeSE]) { - // if the second test is suitable, set the edge biome - selection = (selection & (IslandBit | RiverBits | IceBit)) | LandBit | bordersTo[biomeId] | BiomeBitsAreSetBit; + if(biomeNW != biomeCC || biomeNE != biomeCC || biomeSW != biomeCC || biomeSE != biomeCC) + { + biomeDataAt = (biomeDataAt & (IslandBit | RiverBits | IceBit)) | LandBit | bordersTo[biomeCC] | BiomeBitsAreSetBit; + } } - // if the selection isn't suitable at this point, we can be almost certain that it's not an edge } - } + } } + biomeData[z1 * zSize + x1] = biomeDataAt; } - thisInts[(xi + zi * xSize)] = selection; } } - return thisInts; + else + { + for(int z1 = 0; z1 < zSize; z1++) + { + int biomeDataN = 0; + int biomeDataC = childBiomeData[(z1 + 1) * (xSize + 2) + 0]; + int biomeDataE = childBiomeData[(z1 + 1) * (xSize + 2) + 1]; + int biomeDataS = 0; + int biomeN = 0; + int biomeW = 0; + int biomeC = getBiomeFromLayer(biomeDataC); + int biomeE = getBiomeFromLayer(biomeDataE); + int biomeS = 0; + for(int x1 = 0; x1 < xSize; x1++) + { + biomeDataN = childBiomeData[(z1 + 0) * (xSize + 2) + (x1 + 1)]; + biomeDataC = biomeDataE; + biomeDataE = childBiomeData[(z1 + 1) * (xSize + 2) + (x1 + 2)]; + biomeDataS = childBiomeData[(z1 + 2) * (xSize + 2) + (x1 + 1)]; + biomeN = getBiomeFromLayer(biomeDataN); + biomeW = biomeC; + biomeC = biomeE; + biomeE = getBiomeFromLayer(biomeDataE); + biomeS = getBiomeFromLayer(biomeDataS); + + int biomeDataAt = biomeDataC; + boolean[] biomeFrom; + if((biomeFrom = bordersFrom[biomeC]) != null) + { + if(biomeFrom[biomeW] && biomeFrom[biomeE] && biomeFrom[biomeN] && biomeFrom[biomeS]) + { + if(biomeW != biomeC || biomeE != biomeC || biomeN != biomeC || biomeS != biomeC) + { + biomeDataAt = (biomeDataAt & (IslandBit | RiverBits | IceBit)) | LandBit | bordersTo[biomeC] | BiomeBitsAreSetBit; + } + } + } + biomeData[z1 * zSize + x1] = biomeDataAt; + } + } + } + + return biomeData; } } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeGroups.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeGroups.java index 3faffd8e9..17544b2e9 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeGroups.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeGroups.java @@ -4,9 +4,9 @@ import com.pg85.otg.configuration.biome.BiomeGroup; import com.pg85.otg.configuration.biome.BiomeGroupManager; import com.pg85.otg.generator.biome.ArraysCache; +import com.pg85.otg.util.WeightedList; -import java.util.Map.Entry; -import java.util.SortedMap; +import java.util.function.IntUnaryOperator; public class LayerBiomeGroups extends Layer { @@ -15,12 +15,13 @@ public class LayerBiomeGroups extends Layer private int depth; private boolean freezeGroups; - LayerBiomeGroups(Layer paramGenLayer, BiomeGroupManager biomeGroups, int depth, boolean freezeGroups) + LayerBiomeGroups(long seed, LocalWorld world, Layer paramGenLayer, BiomeGroupManager biomeGroups, int depth) { + super(seed, world); this.child = paramGenLayer; this.biomeGroupManager = biomeGroups; this.depth = depth; - this.freezeGroups = freezeGroups; + this.freezeGroups = world.getConfigs().getWorldConfig().freezeAllColdGroupBiomes; } @Override @@ -30,9 +31,9 @@ public int[] getInts(LocalWorld world, ArraysCache arraysCache, int x, int z, in int[] thisInts = arraysCache.getArray(x_size * z_size); int currentPiece; - SortedMap possibleGroups; - int newGroupRarity; boolean improvedBiomeGroups = world.getConfigs().getWorldConfig().improvedBiomeGroups; + IntUnaryOperator rng = improvedBiomeGroups ? this::nextGroupIntEntropy : this::nextGroupInt; + for (int i = 0; i < z_size; i++) { for (int j = 0; j < x_size; j++) @@ -49,28 +50,15 @@ public int[] getInts(LocalWorld world, ArraysCache arraysCache, int x, int z, in { // TODO: even with rarity 1 this always spawns the biome - possibleGroups = biomeGroupManager.getGroupDepthMap(depth); - if(improvedBiomeGroups) - { - newGroupRarity = nextGroupInt(BiomeGroupManager.getMaxRarityFromPossibles(possibleGroups)); - } else { - newGroupRarity = nextGroupInt(BiomeGroupManager.getMaxRarityFromPossibles(possibleGroups)*Entropy); - } - //>> Spawn the biome based on the rarity spectrum - for (Entry group : possibleGroups.entrySet()) + WeightedList weightedGroups = biomeGroupManager.getGroupDepthMap(depth); + if(!weightedGroups.isEmpty()) { - if ( - (!improvedBiomeGroups && newGroupRarity/Entropy < group.getKey()) || - (improvedBiomeGroups && (newGroupRarity < group.getKey())) - ) + BiomeGroup group = weightedGroups.getRandom(rng); + if(group != null) { - if (group.getValue() != null) - { - currentPiece |= (group.getValue().getGroupId() << BiomeGroupShift) | - //>> If the average temp of the group is cold - ((group.getValue().isColdGroup() && freezeGroups) ? IceBit : 0); - } - break; + currentPiece |= (group.getGroupId() << BiomeGroupShift) | + //>> If the average temp of the group is cold + ((group.isColdGroup() && freezeGroups) ? IceBit : 0); } } } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeInBiome.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeInBiome.java index a8ce2acc4..ce42eee2d 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeInBiome.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerBiomeInBiome.java @@ -4,6 +4,7 @@ import com.pg85.otg.common.LocalWorld; import com.pg85.otg.generator.biome.ArraysCache; import com.pg85.otg.util.minecraft.defaults.DefaultBiome; +import com.pg85.otg.worldsave.WorldSaveData; import java.util.ArrayList; import java.util.List; @@ -13,19 +14,21 @@ public class LayerBiomeInBiome extends Layer private static class Isle { short biomeId; - int chance = 10; - boolean[] canSpawnIn = new boolean[2048]; // Changed to 2048 - long scrambledWorldSeed; - boolean inOcean = false; + int chance; + boolean[] canSpawnIn; + boolean inOcean; + LayerRNG rng; } + private final WorldSaveData worldSaveData; private final long worldSeed; private List isles = new ArrayList(); - LayerBiomeInBiome(Layer childLayer, long worldSeed, int defaultOceanId) + LayerBiomeInBiome(long seed, LocalWorld world, Layer childLayer) { - this.defaultOceanId = defaultOceanId; - this.worldSeed = worldSeed; + super(seed, world); + this.worldSaveData = world.getConfigs().getWorldSaveData(); + this.worldSeed = world.getSeed(); this.child = childLayer; } @@ -57,7 +60,9 @@ void addIsle(LocalBiome biome, int chance, boolean[] biomeCanSpawnIn, boolean in rngSeed = (short) biome.getIds().getSavedId(); } - isle.scrambledWorldSeed = getScrambledWorldSeed(4000 + rngSeed, this.worldSeed); + isle.rng = LayerRNG.create(this.worldSaveData); + isle.rng.setLayerSeed(4000 + rngSeed); + isle.rng.setWorldSeed(this.worldSeed); this.isles.add(isle); } @@ -90,8 +95,7 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz { // Make the scrambled world seed unique for each isle // (each island used to have its own layer) - this.scrambledWorldSeed = isle.scrambledWorldSeed; - initChunkSeed(xi + x, zi + z); + isle.rng.initChunkSeed(xi + x, zi + z); alreadySpawned = false; if (isle.inOcean) { @@ -100,7 +104,7 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz swCheck = childInts[(xi + 0 + (zi + 2) * xSize0)] & LandBit; seCheck = childInts[(xi + 2 + (zi + 2) * xSize0)] & LandBit; - if (((selection & LandBit) == 0) && (nwCheck == 0) && (neCheck == 0) && (swCheck == 0) && (seCheck == 0) && nextInt(isle.chance) == 0) + if (((selection & LandBit) == 0) && (nwCheck == 0) && (neCheck == 0) && (swCheck == 0) && (seCheck == 0) && isle.rng.nextChunkInt(isle.chance) == 0) { selection = (selection & IceBit) | (selection & RiverBits) | LandBit | isle.biomeId | IslandBit | BiomeBitsAreSetBit; alreadySpawned = true; @@ -119,7 +123,7 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz isle.canSpawnIn[neCheck] && isle.canSpawnIn[swCheck] && isle.canSpawnIn[seCheck] && - nextInt(isle.chance) == 0 + isle.rng.nextChunkInt(isle.chance) == 0 ) { selection = (selection & LandBit) | (selection & IceBit) | (selection & RiverBits) | isle.biomeId | IslandBit | BiomeBitsAreSetBit; diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerEmpty.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerEmpty.java index 370935a16..4233bdd5c 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerEmpty.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerEmpty.java @@ -5,9 +5,9 @@ public class LayerEmpty extends Layer { - LayerEmpty(long seed, int defaultOceanId) + LayerEmpty(long seed, LocalWorld world) { - super(seed, defaultOceanId); + super(seed, world); } @Override diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFactory.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFactory.java index d00552056..0526117dc 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFactory.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFactory.java @@ -26,22 +26,6 @@ private LayerFactory() // No instances } - private static int getDefaultOceanBiomeId(LocalWorld world) - { - LocalBiome defaultOceanBiome = world.getBiomeByNameOrNull(world.getConfigs().getWorldConfig().defaultOceanBiome); - if(defaultOceanBiome == null) - { - defaultOceanBiome = world.getFirstBiomeOrNull(); - if(defaultOceanBiome == null) - { - throw new RuntimeException("Could not find DefaultOceanBiome \"" + world.getConfigs().getWorldConfig().defaultOceanBiome + "\", aborting."); - } - OTG.log(LogMarker.WARN, "Could not find DefaultOceanBiome \"" + world.getConfigs().getWorldConfig().defaultOceanBiome + "\", substituting \"" + defaultOceanBiome.getName() + "\"."); - } - - return defaultOceanBiome.getIds().getOTGBiomeId(); - } - /** * Creates a pair of layers for use with the normal biome mode. * @param world World to create layers for. @@ -51,7 +35,7 @@ public static Layer[] createNormal(LocalWorld world) { Layer mainLayer = initMainLayer(world); - Layer zoomedLayer = new LayerZoomVoronoi(10L, getDefaultOceanBiomeId(world), mainLayer); + Layer zoomedLayer = new LayerZoomVoronoi(10L, world, mainLayer); zoomedLayer.initWorldGenSeed(world.getSeed()); return new Layer[] {mainLayer, zoomedLayer}; @@ -68,12 +52,12 @@ public static Layer[] createFromImage(LocalWorld world) Layer mainLayer = initMainLayer(world); if (worldConfig.imageMode == WorldConfig.ImageMode.ContinueNormal) { - mainLayer = new LayerFromImage(1L, getDefaultOceanBiomeId(world), mainLayer, worldConfig, world); + mainLayer = new LayerFromImage(1L, world, mainLayer); } else { - mainLayer = new LayerFromImage(1L, getDefaultOceanBiomeId(world), null, worldConfig, world); + mainLayer = new LayerFromImage(1L, world, null); } - Layer zoomedLayer = new LayerZoomVoronoi(10L, getDefaultOceanBiomeId(world), mainLayer); + Layer zoomedLayer = new LayerZoomVoronoi(10L, world, mainLayer); zoomedLayer.initWorldGenSeed(world.getSeed()); @@ -94,31 +78,6 @@ public static Layer[] createBeforeGroups(LocalWorld world) ConfigProvider configs = world.getConfigs(); WorldConfig worldConfig = configs.getWorldConfig(); - LocalBiome defaultOceanBiome = world.getBiomeByNameOrNull(worldConfig.defaultOceanBiome); - if(defaultOceanBiome == null) - { - defaultOceanBiome = world.getFirstBiomeOrNull(); - if(defaultOceanBiome == null) - { - throw new RuntimeException("Could not find DefaultOceanBiome \"" + worldConfig.defaultOceanBiome + "\", aborting."); - } - OTG.log(LogMarker.WARN, "Could not find DefaultOceanBiome \"" + worldConfig.defaultOceanBiome + "\", substituting \"" + defaultOceanBiome.getName() + "\"."); - } - - LocalBiome defaultFrozenOceanBiome = world.getBiomeByNameOrNull(worldConfig.defaultFrozenOceanBiome); - if(defaultFrozenOceanBiome == null) - { - defaultFrozenOceanBiome = world.getFirstBiomeOrNull(); - if(defaultFrozenOceanBiome == null) - { - throw new RuntimeException("Could not find DefaultFrozenOceanBiome \"" + worldConfig.defaultFrozenOceanBiome + "\", aborting."); - } - OTG.log(LogMarker.WARN, "Could not find DefaultFrozenOceanBiome \"" + worldConfig.defaultFrozenOceanBiome + "\", substituting \"" + defaultOceanBiome.getName() + "\"."); - } - - int defaultOceanId = defaultOceanBiome.getIds().getOTGBiomeId(); - int defaultFrozenOceanId = defaultFrozenOceanBiome.getIds().getOTGBiomeId(); - BiomeGroupManager worldGroupManager = worldConfig.biomeGroupManager; BiomeGroup normalGroup = worldGroupManager.getGroupByName(WorldStandardValues.NORMAL_BIOMES.getName()); @@ -197,9 +156,9 @@ public static Layer[] createBeforeGroups(LocalWorld world) } - Layer mainLayer = new LayerEmpty(1L, defaultOceanId); + Layer mainLayer = new LayerEmpty(1L, world); - Layer RiverLayer = new LayerEmpty(1L, defaultOceanId); + Layer RiverLayer = new LayerEmpty(1L, world); boolean riversStarted = false; LayerBiomeBorder layerBiomeBorder; @@ -217,56 +176,56 @@ public static Layer[] createBeforeGroups(LocalWorld world) for (int depth = 0; depth <= worldConfig.generationDepth; depth++) { - mainLayer = new LayerZoom(2001 + depth, defaultOceanId, mainLayer); + mainLayer = new LayerZoom(2001 + depth, world, mainLayer); if (worldConfig.randomRivers && riversStarted) { - RiverLayer = new LayerZoom(2001 + depth, defaultOceanId, RiverLayer); + RiverLayer = new LayerZoom(2001 + depth, world, RiverLayer); } if (worldConfig.landSize == depth) { - mainLayer = new LayerLand(1L, defaultOceanId, mainLayer, worldConfig.landRarity); - mainLayer = new LayerZoomFuzzy(2000L, defaultOceanId, mainLayer); + mainLayer = new LayerLand(1L, world, mainLayer, worldConfig.landRarity); + mainLayer = new LayerZoomFuzzy(2000L, world, mainLayer); } if (depth < (worldConfig.landSize + worldConfig.landFuzzy)) { - mainLayer = new LayerLandRandom(depth, defaultOceanId, mainLayer); + mainLayer = new LayerLandRandom(depth, world, mainLayer); } if (normalBiomeMap[depth].length != 0 || iceBiomeMap[depth].length != 0) { - mainLayer = new LayerBiomeBeforeGroups(200, defaultOceanId, mainLayer, normalBiomeMap[depth], iceBiomeMap[depth]); + mainLayer = new LayerBiomeBeforeGroups(200, world, mainLayer, normalBiomeMap[depth], iceBiomeMap[depth]); } if (iceGroup.getGenerationDepth() == depth) { - mainLayer = new LayerIce(depth, defaultOceanId, mainLayer, iceGroup.getGroupRarity()); + mainLayer = new LayerIce(depth, world, mainLayer, iceGroup.getGroupRarity()); } if (worldConfig.riverRarity == depth) { if (worldConfig.randomRivers) { - RiverLayer = new LayerRiverInit(155, defaultOceanId, RiverLayer); + RiverLayer = new LayerRiverInit(155, world, RiverLayer); riversStarted = true; } else { - mainLayer = new LayerRiverInit(155, defaultOceanId, mainLayer); + mainLayer = new LayerRiverInit(155, world, mainLayer); } } if ((worldConfig.generationDepth - worldConfig.riverSize) == depth) { if (worldConfig.randomRivers) { - RiverLayer = new LayerRiver(5 + depth, defaultOceanId, RiverLayer); + RiverLayer = new LayerRiver(5 + depth, world, RiverLayer); } else { - mainLayer = new LayerRiver(5 + depth, defaultOceanId, mainLayer); + mainLayer = new LayerRiver(5 + depth, world, mainLayer); } } - layerBiomeBorder = new LayerBiomeBorder(3000 + depth, world, defaultOceanId); - layerBiomeIsle = new LayerBiomeInBiome(mainLayer, world.getSeed(), defaultOceanId); + layerBiomeBorder = new LayerBiomeBorder(3000 + depth, world); + layerBiomeIsle = new LayerBiomeInBiome(1, world, mainLayer); haveBorder = false; haveIsle = false; @@ -339,24 +298,24 @@ public static Layer[] createBeforeGroups(LocalWorld world) if (worldConfig.randomRivers) { - mainLayer = new LayerMixWithRiver(1L, mainLayer, RiverLayer, configs, world, defaultOceanId, defaultFrozenOceanId); + mainLayer = new LayerMixWithRiver(1L, world, mainLayer, RiverLayer); } else { - mainLayer = new LayerMix(1L, mainLayer, configs, world, defaultOceanId, defaultFrozenOceanId); + mainLayer = new LayerMix(1L, world, mainLayer); } - mainLayer = new LayerSmooth(400L, defaultOceanId, mainLayer); + mainLayer = new LayerSmooth(400L, world, mainLayer); if (worldConfig.biomeMode == OTG.getBiomeModeManager().FROM_IMAGE) { if (worldConfig.imageMode == WorldConfig.ImageMode.ContinueNormal) { - mainLayer = new LayerFromImage(1L, defaultOceanId, mainLayer, worldConfig, world); + mainLayer = new LayerFromImage(1L, world, mainLayer); } else { - mainLayer = new LayerFromImage(1L, defaultOceanId, null, worldConfig, world); + mainLayer = new LayerFromImage(1L, world, null); } } - Layer zoomedLayer = new LayerZoomVoronoi(10L, defaultOceanId, mainLayer); + Layer zoomedLayer = new LayerZoomVoronoi(10L, world, mainLayer); zoomedLayer.initWorldGenSeed(world.getSeed()); @@ -368,37 +327,10 @@ private static Layer initMainLayer(LocalWorld world) ConfigProvider configs = world.getConfigs(); WorldConfig worldConfig = configs.getWorldConfig(); - LocalBiome defaultOceanBiome = world.getBiomeByNameOrNull(worldConfig.defaultOceanBiome); - if(defaultOceanBiome == null) - { - defaultOceanBiome = world.getFirstBiomeOrNull(); - if(defaultOceanBiome == null) - { - OTG.log(LogMarker.FATAL, "Could not find DefaultOceanBiome \"" + worldConfig.defaultOceanBiome + "\", aborting."); - throw new RuntimeException("Could not find DefaultOceanBiome \"" + worldConfig.defaultOceanBiome + "\", aborting."); - } - OTG.log(LogMarker.WARN, "Could not find DefaultOceanBiome \"" + worldConfig.defaultOceanBiome + "\", substituting \"" + defaultOceanBiome.getName() + "\"."); - } - - LocalBiome defaultFrozenOceanBiome = world.getBiomeByNameOrNull(worldConfig.defaultFrozenOceanBiome); - if(defaultFrozenOceanBiome == null) - { - defaultFrozenOceanBiome = world.getFirstBiomeOrNull(); - if(defaultFrozenOceanBiome == null) - { - OTG.log(LogMarker.FATAL, "Could not find DefaultFrozenOceanBiome \"" + worldConfig.defaultFrozenOceanBiome + "\", aborting."); - throw new RuntimeException("Could not find DefaultFrozenOceanBiome \"" + worldConfig.defaultFrozenOceanBiome + "\", aborting."); - } - OTG.log(LogMarker.WARN, "Could not find DefaultFrozenOceanBiome \"" + worldConfig.defaultFrozenOceanBiome + "\", substituting \"" + defaultOceanBiome.getName() + "\"."); - } - - int defaultOceanId = defaultOceanBiome.getIds().getOTGBiomeId(); - int defaultFrozenOceanId = defaultFrozenOceanBiome.getIds().getOTGBiomeId(); - BiomeGroupManager groupManager = worldConfig.biomeGroupManager; - Layer mainLayer = new LayerEmpty(1L, defaultOceanId); - Layer RiverLayer = new LayerEmpty(1L, defaultOceanId); + Layer mainLayer = new LayerEmpty(1L, world); + Layer RiverLayer = new LayerEmpty(1L, world); boolean riversStarted = false; LayerBiomeBorder layerBiomeBorder; @@ -417,47 +349,47 @@ private static Layer initMainLayer(LocalWorld world) for (int depth = 0; depth <= worldConfig.generationDepth; depth++) { - mainLayer = new LayerZoom(2001 + depth, defaultOceanId, mainLayer); + mainLayer = new LayerZoom(2001 + depth, world, mainLayer); if (worldConfig.randomRivers && riversStarted) { - RiverLayer = new LayerZoom(2001 + depth, defaultOceanId, RiverLayer); + RiverLayer = new LayerZoom(2001 + depth, world, RiverLayer); } if (worldConfig.landSize == depth) { - mainLayer = new LayerLand(1L, defaultOceanId, mainLayer, worldConfig.landRarity); - mainLayer = new LayerZoomFuzzy(2000L, defaultOceanId, mainLayer); + mainLayer = new LayerLand(1L, world, mainLayer, worldConfig.landRarity); + mainLayer = new LayerZoomFuzzy(2000L, world, mainLayer); } if (depth < (worldConfig.landSize + worldConfig.landFuzzy)) { - mainLayer = new LayerLandRandom(depth, defaultOceanId, mainLayer); + mainLayer = new LayerLandRandom(depth, world, mainLayer); } if (!groupManager.isGroupDepthMapEmpty(depth)) { - mainLayer = new LayerBiomeGroups(mainLayer, groupManager, depth, worldConfig.freezeAllColdGroupBiomes); + mainLayer = new LayerBiomeGroups(1, world, mainLayer, groupManager, depth); } if (!groupManager.isBiomeDepthMapEmpty(depth)) { - mainLayer = new LayerBiome(200, defaultOceanId, mainLayer, groupManager, depth, worldConfig.frozenOceanTemperature); + mainLayer = new LayerBiome(200, world, mainLayer, groupManager, depth); } if (depth == 3) { - mainLayer = new LayerIce(depth, defaultOceanId, mainLayer); + mainLayer = new LayerIce(depth, world, mainLayer); } if (worldConfig.riverRarity == depth) { if (worldConfig.randomRivers) { - RiverLayer = new LayerRiverInit(155, defaultOceanId, RiverLayer); + RiverLayer = new LayerRiverInit(155, world, RiverLayer); riversStarted = true; } else { - mainLayer = new LayerRiverInit(155, defaultOceanId, mainLayer); + mainLayer = new LayerRiverInit(155, world, mainLayer); } } @@ -465,14 +397,14 @@ private static Layer initMainLayer(LocalWorld world) { if (worldConfig.randomRivers) { - RiverLayer = new LayerRiver(5 + depth, defaultOceanId, RiverLayer); + RiverLayer = new LayerRiver(5 + depth, world, RiverLayer); } else { - mainLayer = new LayerRiver(5 + depth, defaultOceanId, mainLayer); + mainLayer = new LayerRiver(5 + depth, world, mainLayer); } } - layerBiomeBorder = new LayerBiomeBorder(3000 + depth, world, defaultOceanId); - layerBiomeIsle = new LayerBiomeInBiome(mainLayer, world.getSeed(), defaultOceanId); + layerBiomeBorder = new LayerBiomeBorder(3000 + depth, world); + layerBiomeIsle = new LayerBiomeInBiome(1, world, mainLayer); haveBorder = false; haveIsle = false; @@ -553,12 +485,12 @@ private static Layer initMainLayer(LocalWorld world) if (worldConfig.randomRivers) { - mainLayer = new LayerMixWithRiver(1L, mainLayer, RiverLayer, configs, world, defaultOceanId, defaultFrozenOceanId); + mainLayer = new LayerMixWithRiver(1L, world, mainLayer, RiverLayer); } else { - mainLayer = new LayerMix(1L, mainLayer, configs, world, defaultOceanId, defaultFrozenOceanId); + mainLayer = new LayerMix(1L, world, mainLayer); } - mainLayer = new LayerSmooth(400L, defaultOceanId, mainLayer); + mainLayer = new LayerSmooth(400L, world, mainLayer); return mainLayer; } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFromImage.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFromImage.java index 1f2ee6098..1ffbaa57b 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFromImage.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerFromImage.java @@ -1,241 +1,350 @@ package com.pg85.otg.generator.biome.layers; -import com.pg85.otg.OTG; -import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.configuration.world.WorldConfig; -import com.pg85.otg.generator.biome.ArraysCache; -import com.pg85.otg.logging.LogMarker; - import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; import javax.imageio.ImageIO; +import javax.xml.bind.DatatypeConverter; + +import com.pg85.otg.OTG; +import com.pg85.otg.common.LocalWorld; +import com.pg85.otg.configuration.world.WorldConfig; +import com.pg85.otg.configuration.world.WorldConfig.ImageMode; +import com.pg85.otg.generator.biome.ArraysCache; +import com.pg85.otg.logging.LogMarker; +import com.pg85.otg.util.helpers.MathHelper; + +import it.unimi.dsi.fastutil.ints.Int2IntMap; public class LayerFromImage extends Layer { - private int[] biomeMap; - private int mapHeight; - private int mapWidth; - private int fillBiome = 0; - private int xOffset; - private int zOffset; - private WorldConfig.ImageMode imageMode; - - LayerFromImage(long seed, int defaultOceanId, Layer childLayer, WorldConfig config, LocalWorld world) + private final Image image; + private final ImageMode imageMode; + private final int xOffset; + private final int zOffset; + private final int fillBiome; + + LayerFromImage(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; - xOffset = config.imageXOffset; - zOffset = config.imageZOffset; - this.imageMode = config.imageMode; - - this.fillBiome = world.getBiomeByNameOrNull(config.imageFillBiome).getIds().getOTGBiomeId(); - - // Read from file + WorldConfig config = world.getConfigs().getWorldConfig(); try { - final File image = new File(config.settingsDir, config.imageFile); - final BufferedImage map = ImageIO.read(image); - - this.mapWidth = map.getWidth(null); - this.mapHeight = map.getHeight(null); - int[] colorMap = new int[this.mapHeight * this.mapWidth]; - - map.getRGB(0, 0, this.mapWidth, this.mapHeight, colorMap, 0, this.mapWidth); - - // Rotate RGBs if need - switch (config.imageOrientation) - { - case North: - // Default behavior - nothing to rotate - break; - case South: - // Rotate picture 180 degrees - int[] colorMap180 = new int[colorMap.length]; - for (int y = 0; y < this.mapHeight; y++) - { - for (int x = 0; x < this.mapWidth; x++) - { - colorMap180[(this.mapHeight - 1 - y) * this.mapWidth + this.mapWidth - 1 - x] = colorMap[y * this.mapWidth + x]; - } - } - colorMap = colorMap180; - break; - case West: - // Rotate picture CW - int[] colorMapCW = new int[colorMap.length]; - for (int y = 0; y < this.mapHeight; y++) - { - for (int x = 0; x < this.mapWidth; x++) - { - colorMapCW[x * this.mapHeight + this.mapHeight - 1 - y] = colorMap[y * this.mapWidth + x]; - } - } - colorMap = colorMapCW; - this.mapWidth = map.getHeight(null); - this.mapHeight = map.getWidth(null); - break; - case East: - // Rotate picture CCW - int[] colorMapCCW = new int[colorMap.length]; - for (int y = 0; y < this.mapHeight; y++) - { - for (int x = 0; x < this.mapWidth; x++) - { - colorMapCCW[(this.mapWidth - 1 - x) * this.mapHeight + y] = colorMap[y * this.mapWidth + x]; - } - } - colorMap = colorMapCCW; - this.mapWidth = map.getHeight(null); - this.mapHeight = map.getWidth(null); - break; - } - - this.biomeMap = new int[colorMap.length]; - - for (int nColor = 0; nColor < colorMap.length; nColor++) - { - int color = colorMap[nColor] & 0x00FFFFFF; - - if (config.biomeColorMap.containsKey(color)) - { - this.biomeMap[nColor] = config.biomeColorMap.get(color); - } else { - // ContinueNormal interprets a -1 as "Use the childLayer" - if (this.imageMode == WorldConfig.ImageMode.ContinueNormal) - { - this.biomeMap[nColor] = -1; - } else { - this.biomeMap[nColor] = fillBiome; - } - } - } + this.image = new Image(config); + } + catch(IOException e) + { + throw new UncheckedIOException("Failed initializing image layer", e); } - catch (IOException ioexception) + this.xOffset = config.imageXOffset; + this.zOffset = config.imageZOffset; + if(config.imageMode == ImageMode.ContinueNormal && child == null) { - OTG.log(LogMarker.FATAL, Arrays.toString(ioexception.getStackTrace())); + OTG.log(LogMarker.ERROR, "Can't use image mode " + ImageMode.ContinueNormal + " without a child layer. Falling back to " + ImageMode.FillEmpty); + this.imageMode = ImageMode.FillEmpty; } + else + { + this.imageMode = config.imageMode; + } + this.fillBiome = world.getBiomeByNameOrNull(config.imageFillBiome).getIds().getOTGBiomeId(); } @Override public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSize, int zSize) { - int[] resultBiomes = cache.getArray(xSize * zSize); - int Buffer_x; - int Buffer_z; - int Buffer_xq; - int Buffer_zq; - switch (this.imageMode) + int[] biomes = cache.getArray(xSize * zSize); + + image.update(); + switch(imageMode) { case Repeat: - for (int zi = 0; zi < zSize; zi++) + for(int z0 = 0; z0 < zSize; z0++) { - for (int xi = 0; xi < xSize; xi++) + int z1 = MathHelper.floorMod(z + z0 - zOffset, image.height); + for(int x0 = 0; x0 < xSize; x0++) { - Buffer_x = (x + xi - xOffset) % this.mapWidth; - Buffer_z = (z + zi - zOffset) % this.mapHeight; + int x1 = MathHelper.floorMod(x + x0 - xOffset, image.width); - // Take care of negatives - if (Buffer_x < 0) + int biome = image.get(x1, z1); + if(biome == -1) { - Buffer_x += this.mapWidth; + biome = fillBiome; } - if (Buffer_z < 0) - { - Buffer_z += this.mapHeight; - } - resultBiomes[(xi + zi * xSize)] = this.biomeMap[Buffer_x + Buffer_z * this.mapWidth]; + biomes[z0 * xSize + x0] = biome; } } - return resultBiomes; + break; case Mirror: - // Improved repeat mode - for (int zi = 0; zi < zSize; zi++) + for(int z0 = 0; z0 < zSize; z0++) { - for (int xi = 0; xi < xSize; xi++) + int z1 = MathHelper.transformMirror(z + z0 - zOffset, image.height); + for(int x0 = 0; x0 < xSize; x0++) { - Buffer_xq = (x + xi - xOffset) % (2 * this.mapWidth); - Buffer_zq = (z + zi - zOffset) % (2 * this.mapHeight); - if (Buffer_xq < 0) - { - Buffer_xq += 2 * this.mapWidth; - } - if (Buffer_zq < 0) - { - Buffer_zq += 2 * this.mapHeight; - } - Buffer_x = Buffer_xq % this.mapWidth; - Buffer_z = Buffer_zq % this.mapHeight; - if (Buffer_xq >= this.mapWidth) - { - Buffer_x = this.mapWidth - 1 - Buffer_x; - } - if (Buffer_zq >= this.mapHeight) + int x1 = MathHelper.transformMirror(x + x0 - xOffset, image.width); + + int biome = image.get(x1, z1); + if(biome == -1) { - Buffer_z = this.mapHeight - 1 - Buffer_z; + biome = fillBiome; } - resultBiomes[(xi + zi * xSize)] = this.biomeMap[Buffer_x + Buffer_z * this.mapWidth]; + biomes[z0 * xSize + x0] = biome; } } - return resultBiomes; + break; case ContinueNormal: int[] childBiomes = null; - if (this.child != null) - { - childBiomes = this.child.getInts(world, cache, x, z, xSize, zSize); - } - for (int zi = 0; zi < zSize; zi++) + for(int z0 = 0; z0 < zSize; z0++) { - for (int xi = 0; xi < xSize; xi++) + int z1 = z + z0 - zOffset; + for(int x0 = 0; x0 < xSize; x0++) { - Buffer_x = x + xi - xOffset; - Buffer_z = z + zi - zOffset; - // if X or Z is outside map bounds - if (Buffer_x < 0 || Buffer_x >= this.mapWidth || Buffer_z < 0 || Buffer_z >= this.mapHeight) + int x1 = x + x0 - xOffset; + + int biome = -1; + if(x1 >= 0 && x1 < image.width && z1 >= 0 && z1 < image.height) + { + biome = image.get(x1, z1); + } + if(biome == -1) + { + if(childBiomes == null) + childBiomes = child.getInts(world, cache, x, z, xSize, zSize); + biome = childBiomes[z0 * xSize + x0]; + } + if(biome == -1) { - if (childBiomes != null) - { - resultBiomes[(xi + zi * xSize)] = childBiomes[(xi + zi * xSize)]; - } else { - resultBiomes[(xi + zi * xSize)] = this.fillBiome; - } - } else { - int biome_id_buffer = this.biomeMap[Buffer_x + Buffer_z * this.mapWidth]; - // If set to -1 in the constructor above, uses the childlayer instead of the fillbiome if it exists. - if (biome_id_buffer == -1) - { - if (childBiomes != null) - { - biome_id_buffer = childBiomes[(xi + zi * xSize)]; - } else { - biome_id_buffer = this.fillBiome; - } - } - resultBiomes[(xi + zi * xSize)] = biome_id_buffer; + biome = fillBiome; } + biomes[z0 * xSize + x0] = biome; } } break; case FillEmpty: - // Some fastened version - for (int zi = 0; zi < zSize; zi++) + for(int z0 = 0; z0 < zSize; z0++) { - for (int xi = 0; xi < xSize; xi++) + int z1 = z + z0 - zOffset; + for(int x0 = 0; x0 < xSize; x0++) { - Buffer_x = x + xi - xOffset; - Buffer_z = z + zi - zOffset; - if (Buffer_x < 0 || Buffer_x >= this.mapWidth || Buffer_z < 0 || Buffer_z >= this.mapHeight) + int x1 = x + x0 - xOffset; + + int biome = -1; + if(x1 >= 0 && x1 < image.width && z1 >= 0 && z1 < image.height) + { + biome = image.get(x1, z1); + } + if(biome == -1) { - resultBiomes[(xi + zi * xSize)] = this.fillBiome; - } else { - resultBiomes[(xi + zi * xSize)] = this.biomeMap[Buffer_x + Buffer_z * this.mapWidth]; + biome = fillBiome; } + biomes[z0 * xSize + x0] = biome; } } break; + default: + throw new IllegalStateException(); } - return resultBiomes; + + return biomes; } + + private static class Image { + + final Path cacheDir; + final int width; + final int height; + final Chunk[] chunks; + + Image(WorldConfig config) throws IOException { + Path imageFile = new File(config.settingsDir, config.imageFile).toPath(); + + if (!Files.exists(imageFile)) { + throw new IllegalArgumentException("Image '" + config.imageFile + "' for config '" + config.getName() + "' does not exist"); + } + + cacheDir = Paths.get(".").resolve(".otg").resolve("images").resolve(config.getName()); + Path infoFile = cacheDir.resolve("info"); + String hash = sha256(config.biomeColorMap, imageFile); + + if (Files.exists(infoFile)) { + List lines = Files.readAllLines(infoFile); + if (lines.size() >= 3 && lines.get(0).equals(hash)) { + width = Integer.parseInt(lines.get(1)); + height = Integer.parseInt(lines.get(2)); + chunks = new Chunk[(width + Chunk.MASK >> Chunk.SHIFT) * (height + Chunk.MASK >> Chunk.SHIFT)]; + return; + } + } + + BufferedImage image = ImageIO.read(imageFile.toFile()); + width = image.getWidth(); + height = image.getHeight(); + chunks = new Chunk[(width + Chunk.MASK >> Chunk.SHIFT) * (height + Chunk.MASK >> Chunk.SHIFT)]; + int[] biomes = image.getRGB(0, 0, width, height, null, 0, width); + switch (config.imageOrientation) { + case North: + break; + case East: + int[] r90 = new int[biomes.length]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + r90[x * height + (height - 1 - y)] = biomes[y * width + x]; + } + } + biomes = r90; + break; + case South: + int[] r180 = new int[biomes.length]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + r180[(height - 1 - y) * width + (width - 1 - x)] = biomes[y * width + x]; + } + } + biomes = r180; + break; + case West: + int[] r270 = new int[biomes.length]; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + r270[(width - 1 - x) * height + y] = biomes[y * width + x]; + } + } + biomes = r270; + break; + default: + break; + } + for (int i = 0; i < biomes.length; i++) { + biomes[i] = config.biomeColorMap.get(biomes[i] & 0xFFFFFF); + } + + Files.createDirectories(cacheDir); + for (int x = 0; x < width + Chunk.MASK >> Chunk.SHIFT; x++) { + for (int z = 0; z < height + Chunk.MASK >> Chunk.SHIFT; z++) { + int[] chunkData = new int[Chunk.SIZE * Chunk.SIZE]; + for (int z0 = 0; z0 < Math.min(Chunk.SIZE, height - (z << Chunk.SHIFT)); z0++) { + for (int x0 = 0; x0 < Math.min(Chunk.SIZE, width - (x << Chunk.SHIFT)); x0++) { + chunkData[(z0 << Chunk.SHIFT) | x0] = biomes[((z << Chunk.SHIFT) | z0) * width + ((x << Chunk.SHIFT) | x0)]; + } + } + writeChunkData(chunkData, cacheDir.resolve(Integer.toString(z * (width + Chunk.MASK >> Chunk.SHIFT) + x))); + } + } + Files.write(infoFile, Arrays.asList(hash, Integer.toString(width), Integer.toString(height))); + } + + static String sha256(Int2IntMap colorBiomeMap, Path imageFile) throws IOException { + MessageDigest digest; + try { + digest = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException(e); + } + colorBiomeMap.int2IntEntrySet().stream().sorted(Comparator.comparingInt(Int2IntMap.Entry::getIntKey)).forEach(e -> { + digest.update((byte) (e.getIntKey() >>> 24)); + digest.update((byte) (e.getIntKey() >>> 16)); + digest.update((byte) (e.getIntKey() >>> 8)); + digest.update((byte) (e.getIntKey() >>> 0)); + digest.update((byte) (e.getIntValue() >>> 24)); + digest.update((byte) (e.getIntValue() >>> 16)); + digest.update((byte) (e.getIntValue() >>> 8)); + digest.update((byte) (e.getIntValue() >>> 0)); + }); + try (InputStream in = new BufferedInputStream(Files.newInputStream(imageFile))) { + byte[] buf = new byte[8192]; + int n; + while ((n = in.read(buf)) >= 0) { + digest.update(buf, 0, n); + } + } + return DatatypeConverter.printHexBinary(digest.digest()).toLowerCase(); + } + + static void writeChunkData(int[] data, Path file) throws IOException { + try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(Files.newOutputStream(file))))) { + for (int i = 0; i < Chunk.SIZE * Chunk.SIZE; i++) { + out.writeShort(data[i]); + } + } + } + + static short[] readChunkData(Path file) throws IOException { + short[] data = new short[Chunk.SIZE * Chunk.SIZE]; + try (DataInputStream in = new DataInputStream(new BufferedInputStream(new GZIPInputStream(Files.newInputStream(file))))) { + for (int i = 0; i < Chunk.SIZE * Chunk.SIZE; i++) { + data[i] = in.readShort(); + } + } + return data; + } + + int get(int x, int z) { + assert x >= 0 && x < width; + assert z >= 0 && z < height; + return getChunk(x >> Chunk.SHIFT, z >> Chunk.SHIFT).get(x & Chunk.MASK, z & Chunk.MASK); + } + + Chunk getChunk(int x, int z) { + int i = z * (width + Chunk.MASK >> Chunk.SHIFT) + x; + Chunk chunk = chunks[i]; + if (chunk == null) { + try { + chunks[i] = chunk = new Chunk(readChunkData(cacheDir.resolve(Integer.toString(i)))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + return chunk; + } + + void update() { + if (-Chunk.time + (Chunk.time = System.currentTimeMillis()) > 1000) { + for (int j = 0; j < chunks.length; j++) { + Chunk c = chunks[j]; + if (c != null && Chunk.time - c.lastAccess > 30_000) + chunks[j] = null; + } + } + } + + static class Chunk { + static final int SHIFT = 8; + static final int SIZE = 1 << SHIFT; + static final int MASK = SIZE - 1; + final short[] data; + long lastAccess = System.currentTimeMillis(); + static long time = System.currentTimeMillis(); + + Chunk(short[] data) { + this.data = data; + } + + int get(int x, int z) { + assert x >= 0 && x < SIZE; + assert z >= 0 && z < SIZE; + lastAccess = time; + return data[(z << SHIFT) | x]; + } + } + + } + } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerIce.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerIce.java index 75ed89da6..c7f5e7120 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerIce.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerIce.java @@ -8,15 +8,15 @@ public class LayerIce extends Layer private int rarity = 10; - LayerIce(long seed, int defaultOceanId, Layer childLayer) + LayerIce(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; } - LayerIce(long seed, int defaultOceanId, Layer childLayer, int _rarity) + LayerIce(long seed, LocalWorld world, Layer childLayer, int _rarity) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; this.rarity = 101 - _rarity; } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLand.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLand.java index adef287fb..9af89b17b 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLand.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLand.java @@ -8,9 +8,9 @@ public class LayerLand extends Layer private int rarity = 5; - LayerLand(long seed, int defaultOceanId, Layer childLayer, int _rarity) + LayerLand(long seed, LocalWorld world, Layer childLayer, int _rarity) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; this.rarity = 101 - _rarity; } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLandRandom.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLandRandom.java index 6109bce79..821e64cfa 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLandRandom.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerLandRandom.java @@ -5,9 +5,9 @@ public class LayerLandRandom extends Layer { - LayerLandRandom(long seed, int defaultOceanId, Layer childLayer) + LayerLandRandom(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; } @@ -36,7 +36,6 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz seCheck = childInts[(xi + 2 + (zi + 2) * xSize0)] & LandBit; centerCheck = childInts[(xi + 1 + (zi + 1) * xSize0)] & LandBit; initChunkSeed(xi + x, zi + z); - initGroupSeed(xi + x, zi + z); thisInts[(xi + zi * xSize)] = childInts[(xi + 1 + (zi + 1) * xSize0)] | LandBit; // Chances to reset LandBit diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMix.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMix.java index fdcbf6698..3eb28117b 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMix.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMix.java @@ -15,12 +15,12 @@ public class LayerMix extends Layer private int[] riverBiomes; private int defaultFrozenOceanId; - LayerMix(long seed, Layer childLayer, ConfigProvider configs, LocalWorld world, int defaultOceanId, int defaultFrozenOceanId) + LayerMix(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); - this.defaultFrozenOceanId = defaultFrozenOceanId; + super(seed, world); + this.defaultFrozenOceanId = getBiomeId(world, world.getConfigs().getWorldConfig().defaultFrozenOceanBiome, "DefaultFrozenOcean"); this.child = childLayer; - this.configs = configs; + this.configs = world.getConfigs(); this.riverBiomes = new int[world.getMaxBiomesCount()]; LocalBiome biome; LocalBiome riverBiome; diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMixWithRiver.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMixWithRiver.java index a31978226..a5e01cd08 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMixWithRiver.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerMixWithRiver.java @@ -12,12 +12,12 @@ public class LayerMixWithRiver extends Layer { private int defaultFrozenOceanId; - LayerMixWithRiver(long seed, Layer childLayer, Layer riverLayer, ConfigProvider configs, LocalWorld world, int defaultOceanId, int defaultFrozenOceanId) + LayerMixWithRiver(long seed, LocalWorld world, Layer childLayer, Layer riverLayer) { - super(seed, defaultOceanId); - this.defaultFrozenOceanId = defaultFrozenOceanId; + super(seed, world); + this.defaultFrozenOceanId = getBiomeId(world, world.getConfigs().getWorldConfig().defaultFrozenOceanBiome, "DefaultFrozenOcean"); this.child = childLayer; - this.configs = configs; + this.configs = world.getConfigs(); this.riverLayer = riverLayer; this.riverBiomes = new int[world.getMaxBiomesCount()]; LocalBiome biome; diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRNG.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRNG.java new file mode 100644 index 000000000..d958a9d06 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRNG.java @@ -0,0 +1,175 @@ +package com.pg85.otg.generator.biome.layers; + +import com.pg85.otg.util.RNG; +import com.pg85.otg.worldsave.WorldSaveData; + +public interface LayerRNG +{ + void setLayerSeed(long seed); + + void setWorldSeed(long seed); + + void initChunkSeed(int x, int z); + + @Deprecated + default void initChunkSeed(long x, long z) + { + initChunkSeed((int) x, (int) z); + } + + @Deprecated + default void initGroupSeed(long x, long z) + { + initChunkSeed(x, z); + } + + int nextChunkInt(int bound); + + @Deprecated + default int nextGroupInt(int bound) + { + return nextChunkInt(bound); + } + + @Deprecated + default int nextGroupIntEntropy(int bound) + { + return nextGroupInt(bound); + } + + static LayerRNG create(WorldSaveData worldSaveData) + { + if(worldSaveData.version < 9) + { + return new Legacy(); + } + return new Default(); + } + + class Default implements LayerRNG + { + private final RNG rng = new RNG(); + private long scrambledLayerSeed; + private long scrambledWorldSeed; + + @Override + public void setLayerSeed(long seed) + { + this.scrambledLayerSeed = RNG.murmurHash3(seed); + } + + @Override + public void setWorldSeed(long seed) + { + this.scrambledWorldSeed = this.scrambledLayerSeed ^ RNG.murmurHash3(seed); + } + + @Override + public void initChunkSeed(int x, int z) + { + this.rng.setSeed(this.scrambledWorldSeed ^ (x & 0xFFFF_FFFFL | (z & 0xFFFF_FFFFL) << 32)); + } + + @Override + public int nextChunkInt(int bound) + { + return this.rng.nextInt(bound); + } + } + + @Deprecated + class Legacy implements LayerRNG + { + private static final long M = 6364136223846793005L; + private static final long A = 1442695040888963407L; + + private long scrambledLayerSeed; + private long scrambledWorldSeed; + private long scrambledChunkSeed; + private long scrambledGroupSeed; + + @Override + public void setLayerSeed(long seed) + { + this.scrambledLayerSeed = scramble3(seed, seed); + } + + @Override + public void setWorldSeed(long seed) + { + this.scrambledWorldSeed = scramble3(seed, this.scrambledLayerSeed); + } + + private static long scramble3(long base, long addend) + { + long z = base; + z = z * (z * M + A) + addend; + z = z * (z * M + A) + addend; + z = z * (z * M + A) + addend; + return z; + } + + @Override + public void initChunkSeed(int x, int z) + { + this.initChunkSeed((long) x, (long) z); + } + + @Override + public void initChunkSeed(long x, long z) + { + this.scrambledChunkSeed = scramble22(this.scrambledWorldSeed, x, z); + } + + @Override + public void initGroupSeed(long x, long z) + { + this.scrambledGroupSeed = scramble22(this.scrambledChunkSeed, x, z); + } + + private static long scramble22(long base, long addend1, long addend2) + { + long z = base; + z = z * (z * M + A) + addend1; + z = z * (z * M + A) + addend2; + z = z * (z * M + A) + addend1; + z = z * (z * M + A) + addend2; + return z; + } + + @Override + public int nextChunkInt(int bound) + { + int i = (int) ((this.scrambledChunkSeed >> 24) % bound); + if(i < 0) + { + i += bound; + } + this.scrambledChunkSeed = scramble1(this.scrambledChunkSeed, this.scrambledWorldSeed); + return i; + } + + @Override + public int nextGroupInt(int bound) + { + int i = (int) ((this.scrambledGroupSeed >> 24) % bound); + if(i < 0) + { + i += bound; + } + this.scrambledGroupSeed = scramble1(this.scrambledGroupSeed, this.scrambledChunkSeed); + return i; + } + + private static long scramble1(long base, long addend) + { + return base * (base * M + A) + addend; + } + + @Override + public int nextGroupIntEntropy(int bound) + { + return this.nextGroupInt(bound * 10000) / 10000; + } + } +} diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiver.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiver.java index 4db549566..b40e9fab3 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiver.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiver.java @@ -5,9 +5,9 @@ public class LayerRiver extends Layer { - LayerRiver(long seed, int defaultOceanId, Layer childLayer) + LayerRiver(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiverInit.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiverInit.java index 3cfbf919c..e752ca6ba 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiverInit.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerRiverInit.java @@ -6,9 +6,9 @@ public class LayerRiverInit extends Layer { - LayerRiverInit(long paramLong, int defaultOceanId, Layer paramGenLayer) + LayerRiverInit(long seed, LocalWorld world, Layer paramGenLayer) { - super(paramLong, defaultOceanId); + super(seed, world); this.child = paramGenLayer; } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerSmooth.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerSmooth.java index 9377415c6..f9d0c7166 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerSmooth.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerSmooth.java @@ -6,9 +6,9 @@ public class LayerSmooth extends Layer { - LayerSmooth(long seed, int defaultOceanId, Layer childLayer) + LayerSmooth(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; } diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoom.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoom.java index 170263478..b503d83cf 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoom.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoom.java @@ -6,9 +6,9 @@ public class LayerZoom extends Layer { - LayerZoom(long seed, int defaultOceanId, Layer childLayer) + LayerZoom(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; } @@ -44,7 +44,7 @@ public int[] getInts(LocalWorld world, ArraysCache cache, int x, int z, int xSiz thisInts[i3] = i4; thisInts[(i3++ + n)] = rndParam(i4, i5); thisInts[i3] = rndParam(i4, northCheck); - thisInts[(i3++ + n)] = getRandomOf4(i4, northCheck, i5, centerCheck); + thisInts[(i3++ + n)] = mostCommonOrRandom(i4, northCheck, i5, centerCheck); i4 = northCheck; i5 = centerCheck; diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomFuzzy.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomFuzzy.java index c9c98a912..df72b24bb 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomFuzzy.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomFuzzy.java @@ -1,15 +1,17 @@ package com.pg85.otg.generator.biome.layers; +import com.pg85.otg.common.LocalWorld; + public class LayerZoomFuzzy extends LayerZoom { - LayerZoomFuzzy(long seed, int defaultOceanId, Layer childLayer) + LayerZoomFuzzy(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId, childLayer); + super(seed, world, childLayer); } @Override - protected int getRandomOf4(int a, int b, int c, int d) + protected int mostCommonOrRandom(int a, int b, int c, int d) { switch (this.nextInt(4)){ case 0: return a; diff --git a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomVoronoi.java b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomVoronoi.java index cb38bed9c..8bd0d8cd3 100644 --- a/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomVoronoi.java +++ b/common/src/main/java/com/pg85/otg/generator/biome/layers/LayerZoomVoronoi.java @@ -7,9 +7,9 @@ public class LayerZoomVoronoi extends Layer { - LayerZoomVoronoi(long seed, int defaultOceanId, Layer childLayer) + LayerZoomVoronoi(long seed, LocalWorld world, Layer childLayer) { - super(seed, defaultOceanId); + super(seed, world); this.child = childLayer; } diff --git a/common/src/main/java/com/pg85/otg/generator/resource/AboveWaterGen.java b/common/src/main/java/com/pg85/otg/generator/resource/AboveWaterGen.java index df7de51e1..2acdc160c 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/AboveWaterGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/AboveWaterGen.java @@ -32,7 +32,7 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, return; } - parseMaterials(world, material, null); + material = material.parseForWorld(world); int j; int k; diff --git a/common/src/main/java/com/pg85/otg/generator/resource/BoulderGen.java b/common/src/main/java/com/pg85/otg/generator/resource/BoulderGen.java index af673ab96..34c07b2d4 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/BoulderGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/BoulderGen.java @@ -42,7 +42,8 @@ public void spawn(LocalWorld world, Random random, boolean villageInChunk, int x return; } - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); while (y > 3) { diff --git a/common/src/main/java/com/pg85/otg/generator/resource/CactusGen.java b/common/src/main/java/com/pg85/otg/generator/resource/CactusGen.java index 4a7861875..b4187a854 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/CactusGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/CactusGen.java @@ -40,7 +40,8 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, int y = RandomHelper.numberInRange(rand, minAltitude, maxAltitude); - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); LocalMaterialData worldMaterial; int cactusX; int cactusBaseY; diff --git a/common/src/main/java/com/pg85/otg/generator/resource/IceSpikeGen.java b/common/src/main/java/com/pg85/otg/generator/resource/IceSpikeGen.java index 8c1cd1cad..4a3d36cbc 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/IceSpikeGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/IceSpikeGen.java @@ -103,7 +103,8 @@ private void spawnBasement(LocalWorld world, Random random,int x, int z, ChunkCo y--; } - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); if ((worldMaterial = world.getMaterial(x, y, z, chunkBeingPopulated)) == null || !this.sourceBlocks.contains(worldMaterial)) { @@ -145,7 +146,8 @@ private void spawnSpike(LocalWorld world, Random random, int x, int z, boolean h --y; } - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); if ((worldMaterial = world.getMaterial(x, y, z, chunkBeingPopulated)) == null || !sourceBlocks.contains(worldMaterial)) { @@ -238,7 +240,7 @@ private void spawnSpike(LocalWorld world, Random random, int x, int z, boolean h ( worldMaterial.isAir() || sourceBlocks.contains(worldMaterial) || - worldMaterial.equals(this.material) + this.material.matches(worldMaterial) ) ) { diff --git a/common/src/main/java/com/pg85/otg/generator/resource/LiquidGen.java b/common/src/main/java/com/pg85/otg/generator/resource/LiquidGen.java index 50e3333af..00680635c 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/LiquidGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/LiquidGen.java @@ -81,7 +81,8 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, int y = RandomHelper.numberInRange(rand, minAltitude, maxAltitude); - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); LocalMaterialData worldMaterial = world.getMaterial(x, y + 1, z, chunkBeingPopulated); if (worldMaterial == null || !sourceBlocks.contains(worldMaterial)) diff --git a/common/src/main/java/com/pg85/otg/generator/resource/OreGen.java b/common/src/main/java/com/pg85/otg/generator/resource/OreGen.java index 3f3837dce..bd498141d 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/OreGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/OreGen.java @@ -95,7 +95,8 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, { // Make sure we stay within population bounds, anything outside won't be spawned (unless it's in an existing chunk). - parseMaterials(world, this.material, this.sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); if(world.getConfigs().getWorldConfig().disableOreGen) { diff --git a/common/src/main/java/com/pg85/otg/generator/resource/PlantType.java b/common/src/main/java/com/pg85/otg/generator/resource/PlantType.java index 38455846e..364410d1c 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/PlantType.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/PlantType.java @@ -1,8 +1,8 @@ package com.pg85.otg.generator.resource; import java.util.Collection; +import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; @@ -18,7 +18,7 @@ public class PlantType { // Builds lookup map - private static final Map LOOKUP_MAP = new TreeMap(String.CASE_INSENSITIVE_ORDER); + private static final Map LOOKUP_MAP = new HashMap<>(); public static final PlantType Allium = register(new PlantType("Allium", DefaultMaterial.RED_ROSE, 2)); public static final PlantType AzureBluet = register(new PlantType("AzureBluet", DefaultMaterial.RED_ROSE, 3)); diff --git a/common/src/main/java/com/pg85/otg/generator/resource/ReedGen.java b/common/src/main/java/com/pg85/otg/generator/resource/ReedGen.java index 7715611fb..45481ccb7 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/ReedGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/ReedGen.java @@ -97,7 +97,8 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, return; } - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); LocalMaterialData worldMaterial = world.getMaterial(x, y - 1, z, chunkBeingPopulated); if (worldMaterial == null || !this.sourceBlocks.contains(worldMaterial)) diff --git a/common/src/main/java/com/pg85/otg/generator/resource/Resource.java b/common/src/main/java/com/pg85/otg/generator/resource/Resource.java index 13d5056b5..21da71447 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/Resource.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/Resource.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; import com.pg85.otg.OTG; @@ -11,7 +12,6 @@ import com.pg85.otg.configuration.biome.BiomeConfig; import com.pg85.otg.exception.InvalidConfigException; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.materials.MaterialSet; /** * Represents a Resource: something that can generate in the world. @@ -98,16 +98,6 @@ public LocalMaterialData getMaterial() { return material; } - - protected void parseMaterials(LocalWorld world, LocalMaterialData material, MaterialSet sourceBlocks) - { - material.parseForWorld(world); - - if (sourceBlocks != null) - { - sourceBlocks.parseForWorld(world); - } - } public int getPriority() { @@ -118,9 +108,9 @@ public int getPriority() public int hashCode() { int hash = 5; - hash = 53 * hash + (this.material == null ? 0 : material.hashCode()); + hash = 53 * hash + Objects.hashCode(this.material); hash = 53 * hash + this.frequency; - hash = 53 * hash + (int) (Double.doubleToLongBits(this.rarity) ^ (Double.doubleToLongBits(this.rarity) >>> 32)); + hash = 53 * hash + Double.hashCode(this.rarity); return hash; } diff --git a/common/src/main/java/com/pg85/otg/generator/resource/SmallLakeGen.java b/common/src/main/java/com/pg85/otg/generator/resource/SmallLakeGen.java index c021c7134..85dea63c7 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/SmallLakeGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/SmallLakeGen.java @@ -103,7 +103,7 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, // y = floor y -= 4; - parseMaterials(world, material, null); + material = material.parseForWorld(world); // TODO: Why on earth would this be necessary or useful? synchronized (BooleanBuffer) @@ -171,7 +171,7 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, return; } localMaterialData2 = world.getMaterial(x + j, y + i2, z + i1, chunkBeingPopulated); - if ((i2 < 4) && (localMaterialData == null || !localMaterialData.isSolid()) && (localMaterialData2 == null || !localMaterialData2.equals(material))) + if ((i2 < 4) && (localMaterialData == null || !localMaterialData.isSolid()) && (localMaterialData2 == null || !material.matches(localMaterialData2))) { return; } diff --git a/common/src/main/java/com/pg85/otg/generator/resource/SurfacePatchGen.java b/common/src/main/java/com/pg85/otg/generator/resource/SurfacePatchGen.java index 56df6014e..60723ebfb 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/SurfacePatchGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/SurfacePatchGen.java @@ -120,7 +120,8 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, if (y < minAltitude || y > maxAltitude) return; - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); double yNoise = noiseGen.getYNoise(x * 0.25D, z * 0.25D); if (yNoise > 0.0D) diff --git a/common/src/main/java/com/pg85/otg/generator/resource/UnderWaterOreGen.java b/common/src/main/java/com/pg85/otg/generator/resource/UnderWaterOreGen.java index 2b6d3e6d2..7d20cab35 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/UnderWaterOreGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/UnderWaterOreGen.java @@ -77,7 +77,8 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, return; } - parseMaterials(world, this.material, this.sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); if(world.getConfigs().getWorldConfig().disableOreGen) { diff --git a/common/src/main/java/com/pg85/otg/generator/resource/UndergroundLakeGen.java b/common/src/main/java/com/pg85/otg/generator/resource/UndergroundLakeGen.java index 7740e8e79..4511a95aa 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/UndergroundLakeGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/UndergroundLakeGen.java @@ -119,7 +119,7 @@ public void spawn(LocalWorld world, Random rand, boolean villageInChunk, int x, for (int zLake = (int) (zAdjusted - horizontalSize / 2.0D); zLake <= (int) (zAdjusted + horizontalSize / 2.0D); zLake++) { LocalMaterialData material = world.getMaterial(xLake, yLake, zLake, chunkBeingPopulated); - if (material == null || material.isEmptyOrAir() || material.isMaterial(DefaultMaterial.BEDROCK)) + if (material == null || material.isAir() || material.isMaterial(DefaultMaterial.BEDROCK)) { // Don't replace air or bedrock continue; diff --git a/common/src/main/java/com/pg85/otg/generator/resource/Vein.java b/common/src/main/java/com/pg85/otg/generator/resource/Vein.java index 851695cb2..07b21cc61 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/Vein.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/Vein.java @@ -66,60 +66,59 @@ public void spawn(LocalWorld world, Random random, ChunkCoordinate chunkBeingPop } } - private void spawnOre(LocalWorld world, Random rand, int x, int y, int z, VeinGen gen, ChunkCoordinate chunkBeingPopulated) + private static void spawnOre(LocalWorld world, Random rand, int x, int y, int z, VeinGen gen, ChunkCoordinate chunkBeingPopulated) { int maxSize = gen.oreSize; LocalMaterialData material = gen.material; MaterialSet sourceBlocks = gen.sourceBlocks; - float f = rand.nextFloat() * 3.141593F; + float f = rand.nextFloat() * (float) Math.PI; + float sinf = MathHelper.sin(f) * maxSize / 8.0F; + float cosf = MathHelper.cos(f) * maxSize / 8.0F; - double d1 = x + 8 + MathHelper.sin(f) * maxSize / 8.0F; - double d2 = x + 8 - MathHelper.sin(f) * maxSize / 8.0F; - double d3 = z + 8 + MathHelper.cos(f) * maxSize / 8.0F; - double d4 = z + 8 - MathHelper.cos(f) * maxSize / 8.0F; + float maxX = x + 8 + sinf; + float minX = x + 8 - sinf; + float maxZ = z + 8 + cosf; + float minZ = z + 8 - cosf; - double d5 = y + rand.nextInt(3) - 2; - double d6 = y + rand.nextInt(3) - 2; + float maxY = y - 2 + rand.nextInt(3); + float minY = y - 2 + rand.nextInt(3); - for (int i = 0; i < maxSize; i++) + for(int i = 0; i < maxSize; i++) { float iFactor = (float) i / (float) maxSize; - double d7 = d1 + (d2 - d1) * iFactor; - double d8 = d5 + (d6 - d5) * iFactor; - double d9 = d3 + (d4 - d3) * iFactor; + float x1 = maxX + (minX - maxX) * iFactor; + float y1 = maxY + (minY - maxY) * iFactor; + float z1 = maxZ + (minZ - maxZ) * iFactor; - double d10 = rand.nextDouble() * maxSize / 16.0D; - double d11 = (MathHelper.sin((float) Math.PI * iFactor) + 1.0) * d10 + 1.0; - double d12 = (MathHelper.sin((float) Math.PI * iFactor) + 1.0) * d10 + 1.0; + float r = ((MathHelper.sin((float) Math.PI * iFactor) + 1.0F) * rand.nextFloat() * maxSize / 16.0F + 1.0F) * 0.5F; - int j = MathHelper.floor(d7 - d11 / 2.0D); - int k = MathHelper.floor(d8 - d12 / 2.0D); - int m = MathHelper.floor(d9 - d11 / 2.0D); + int minX1 = MathHelper.ceil(x1 - r - 0.5F); + int minY1 = MathHelper.ceil(y1 - r - 0.5F); + int minZ1 = MathHelper.ceil(z1 - r - 0.5F); - int n = MathHelper.floor(d7 + d11 / 2.0D); - int i1 = MathHelper.floor(d8 + d12 / 2.0D); - int i2 = MathHelper.floor(d9 + d11 / 2.0D); + int maxX1 = MathHelper.floor(x1 + r - 0.5F); + int maxY1 = MathHelper.floor(y1 + r - 0.5F); + int maxZ1 = MathHelper.floor(z1 + r - 0.5F); - for (int i3 = j; i3 <= n; i3++) + for(int x2 = minX1; x2 <= maxX1; x2++) { - double d13 = (i3 + 0.5D - d7) / (d11 / 2.0D); - if (d13 * d13 < 1.0D) + for(int y2 = minY1; y2 <= maxY1; y2++) { - for (int i4 = k; i4 <= i1; i4++) + for(int z2 = minZ1; z2 <= maxZ1; z2++) { - double d14 = (i4 + 0.5D - d8) / (d12 / 2.0D); - if (d13 * d13 + d14 * d14 < 1.0D) + float dx = x2 + 0.5F - x1; + float dy = y2 + 0.5F - y1; + float dz = z2 + 0.5F - z1; + if(dx * dx + dy * dy + dz * dz > r * r) { - for (int i5 = m; i5 <= i2; i5++) - { - double d15 = (i5 + 0.5D - d9) / (d11 / 2.0D); - if ((d13 * d13 + d14 * d14 + d15 * d15 < 1.0D) && sourceBlocks.contains(world.getMaterial(i3, i4, i5, chunkBeingPopulated))) - { - world.setBlock(i3, i4, i5, material, null, chunkBeingPopulated, true); - } - } + continue; } + if(!sourceBlocks.contains(world.getMaterial(x2, y2, z2, chunkBeingPopulated))) + { + continue; + } + world.setBlock(x2, y2, z2, material, null, chunkBeingPopulated, true); } } } diff --git a/common/src/main/java/com/pg85/otg/generator/resource/VeinGen.java b/common/src/main/java/com/pg85/otg/generator/resource/VeinGen.java index b945efaa5..b3f510f1f 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/VeinGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/VeinGen.java @@ -10,6 +10,7 @@ import com.pg85.otg.util.materials.MaterialSet; import java.util.List; +import java.util.Objects; import java.util.Random; public class VeinGen extends Resource @@ -103,7 +104,7 @@ public int hashCode() { int hash = 3; hash = 29 * hash + super.hashCode(); - hash = 29 * hash + (int) (Double.doubleToLongBits(this.veinRarity) ^ (Double.doubleToLongBits(this.veinRarity) >>> 32)); + hash = 29 * hash + Double.hashCode(this.veinRarity); hash = 29 * hash + this.minRadius; hash = 29 * hash + this.maxRadius; hash = 29 * hash + this.oreSize; @@ -111,7 +112,7 @@ public int hashCode() hash = 29 * hash + this.oreRarity; hash = 29 * hash + this.minAltitude; hash = 29 * hash + this.maxAltitude; - hash = 29 * hash + (this.sourceBlocks != null ? this.sourceBlocks.hashCode() : 0); + hash = 29 * hash + Objects.hashCode(this.sourceBlocks); return hash; } @@ -137,7 +138,8 @@ protected void spawnInChunk(LocalWorld world, Random random, boolean villageInCh // Find all veins that reach this chunk, and spawn them int searchRadius = (this.maxRadius + 15) / 16; - parseMaterials(world, this.material, this.sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); if(world.getConfigs().getWorldConfig().disableOreGen) { diff --git a/common/src/main/java/com/pg85/otg/generator/resource/WellGen.java b/common/src/main/java/com/pg85/otg/generator/resource/WellGen.java index 2a7a6c83b..d8e34de41 100644 --- a/common/src/main/java/com/pg85/otg/generator/resource/WellGen.java +++ b/common/src/main/java/com/pg85/otg/generator/resource/WellGen.java @@ -97,7 +97,8 @@ public void spawn(LocalWorld world, Random random, boolean villageInChunk, int x --y; } - parseMaterials(world, material, sourceBlocks); + material = material.parseForWorld(world); + sourceBlocks.parseForWorld(world); worldMaterial = world.getMaterial(x, y, z, chunkBeingPopulated); diff --git a/common/src/main/java/com/pg85/otg/generator/surface/FrozenSurfaceHelper.java b/common/src/main/java/com/pg85/otg/generator/surface/FrozenSurfaceHelper.java index a501fec26..3dab959e2 100644 --- a/common/src/main/java/com/pg85/otg/generator/surface/FrozenSurfaceHelper.java +++ b/common/src/main/java/com/pg85/otg/generator/surface/FrozenSurfaceHelper.java @@ -3,7 +3,6 @@ import com.pg85.otg.common.LocalBiome; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.configuration.biome.BiomeConfig; import com.pg85.otg.configuration.standard.PluginStandardValues; import com.pg85.otg.configuration.standard.WorldStandardValues; import com.pg85.otg.configuration.world.WorldConfig; @@ -148,76 +147,37 @@ private boolean shouldFreeze(int x, int y, int z, LocalMaterialData thawedMateri */ private void startSnowFall(int x, int y, int z, LocalBiome biome, ChunkCoordinate chunkBeingPopulated) { - int decreaseFactor = 0; - BiomeConfig biomeConfig = biome.getBiomeConfig(); + int snowHeight = this.worldConfig.betterSnowFall ? biome.getBiomeConfig().getSnowHeight(biome.getTemperatureAt(x, y, z)) : 0; - float tempAtBlockToFreeze; - int snowHeight; - LocalMaterialData materialToSnowAt = null; - LocalMaterialData materialToSnowOn = null; - if(this.worldConfig.betterSnowFall) + while(y > PluginStandardValues.WORLD_DEPTH && snowHeight >= 0) { - tempAtBlockToFreeze = biome.getTemperatureAt(x, y, z); - snowHeight = biomeConfig.getSnowHeight(tempAtBlockToFreeze); - } else { - snowHeight = 0; - } - while ( - y > PluginStandardValues.WORLD_DEPTH + 1 && - decreaseFactor < 8 && - snowHeight - decreaseFactor >= 0 - ) - { - materialToSnowAt = world.getMaterial(x, y, z, chunkBeingPopulated); - materialToSnowOn = world.getMaterial(x, y - 1, z, chunkBeingPopulated); - if ( - materialToSnowAt != null && - materialToSnowOn != null && - materialToSnowAt.isAir() && - materialToSnowOn.canSnowFallOn() - ) + LocalMaterialData materialAt = world.getMaterial(x, y, z, chunkBeingPopulated); + if(materialAt.isMaterial(DefaultMaterial.LEAVES)) { - // If we've spawned all snow layers, exit. - if(this.setSnowFallAtLocation(x, y, z, snowHeight - decreaseFactor, materialToSnowOn, chunkBeingPopulated)) - { - break; - } - // Spawned on leaves, which can only carry maxLayersOnLeaves snow layers. - // We have more snow layers to spawn. - decreaseFactor += maxLayersOnLeaves; + y--; + continue; } - if(materialToSnowOn == null || materialToSnowOn.isSolid()) + if(!materialAt.isAir()) { - break; + break; } - y--; - } - } - /** - * Applied snow to a location - * @param x Location X - * @param y Location Y - * @param z Location Z - * @param baseSnowHeight The base height snow should be - * @param materialToSnowOn The material that might have snow applied - */ - private boolean setSnowFallAtLocation(int x, int y, int z, int baseSnowHeight, LocalMaterialData materialToSnowOn, ChunkCoordinate chunkBeingPopulated) - { - LocalMaterialData snowMass; - if (materialToSnowOn.isMaterial(DefaultMaterial.LEAVES) || materialToSnowOn.isMaterial(DefaultMaterial.LEAVES_2)) - { - // Snow Layer(s) for trees, let each leaf carry maxLayersOnLeaves or less layers of snow, - // any remaining layers will fall through. - snowMass = MaterialHelper.toLocalMaterialData(DefaultMaterial.SNOW, baseSnowHeight <= maxLayersOnLeaves - 1 ? baseSnowHeight : maxLayersOnLeaves - 1); - world.setBlock(x, y, z, snowMass, null, chunkBeingPopulated, true); - return baseSnowHeight <= maxLayersOnLeaves - 1; + LocalMaterialData materialOn = world.getMaterial(x, y - 1, z, chunkBeingPopulated); + if(materialOn.isAir()) + { + y--; + continue; + } + if(!world.canPlaceSnowAt(x, y, z, chunkBeingPopulated)) + { + break; + } + + int placedSnowHeight = materialOn.isLeaves() ? Math.min(snowHeight, maxLayersOnLeaves - 1) : snowHeight; + world.setBlock(x, y, z, MaterialHelper.toLocalMaterialData(DefaultMaterial.SNOW, placedSnowHeight), null, chunkBeingPopulated, true); + snowHeight -= placedSnowHeight + 1; + y -= 2; } - - // Basic Snow Layer(s) - snowMass = MaterialHelper.toLocalMaterialData(DefaultMaterial.SNOW, baseSnowHeight); - world.setBlock(x, y, z, snowMass, null, chunkBeingPopulated, true); - return true; } /** diff --git a/common/src/main/java/com/pg85/otg/generator/surface/MesaSurfaceGenerator.java b/common/src/main/java/com/pg85/otg/generator/surface/MesaSurfaceGenerator.java index 4bdef6fa7..ab48a35d3 100644 --- a/common/src/main/java/com/pg85/otg/generator/surface/MesaSurfaceGenerator.java +++ b/common/src/main/java/com/pg85/otg/generator/surface/MesaSurfaceGenerator.java @@ -344,7 +344,7 @@ public void spawn(LocalWorld world, GeneratingChunk generatingChunk, ChunkBuffer } else if (i1 < 15 || this.brycePillars) { - if(worldMaterial.isEmptyOrAir()) + if(worldMaterial.isAir()) { k1 = -1; } @@ -356,7 +356,7 @@ else if (i1 < 15 || this.brycePillars) // same biome water block as surface/ground/stone block. // TODO: If other mods have problems bc of replacedblocks in the chunk during ReplaceBiomeBlocks, // do replaceblock for stone/water here instead of when initially filling the chunk. - else if(!worldMaterial.equals(biomeConfig.getWaterBlockReplaced(world, y))) + else if(!biomeConfig.getWaterBlockReplaced(world, y).matches(worldMaterial)) { if (k1 == -1) { diff --git a/common/src/main/java/com/pg85/otg/generator/surface/MultipleLayersSurfaceGenerator.java b/common/src/main/java/com/pg85/otg/generator/surface/MultipleLayersSurfaceGenerator.java index e5936f36a..2800c4c95 100644 --- a/common/src/main/java/com/pg85/otg/generator/surface/MultipleLayersSurfaceGenerator.java +++ b/common/src/main/java/com/pg85/otg/generator/surface/MultipleLayersSurfaceGenerator.java @@ -18,19 +18,19 @@ public class MultipleLayersSurfaceGenerator extends SimpleSurfaceGenerator // Must be sorted based on the noise field private List layerChoices; - public MultipleLayersSurfaceGenerator(String[] args) throws InvalidConfigException + public MultipleLayersSurfaceGenerator(List args) throws InvalidConfigException { - if (args.length < 2) + if (args.size() < 2) { throw new InvalidConfigException("Needs at least two arguments"); } layerChoices = new ArrayList(); - for (int i = 0; i < args.length - 2; i += 3) + for (int i = 0; i < args.size() - 2; i += 3) { - LocalMaterialData surfaceBlock = MaterialHelper.readMaterial(args[i]); - LocalMaterialData groundBlock = MaterialHelper.readMaterial(args[i+1]); - float maxNoise = (float) StringHelper.readDouble(args[i + 2], -20, 20); + LocalMaterialData surfaceBlock = MaterialHelper.readMaterial(args.get(i)); + LocalMaterialData groundBlock = MaterialHelper.readMaterial(args.get(i+1)); + float maxNoise = (float) StringHelper.readDouble(args.get(i + 2), -20, 20); layerChoices.add(new LayerChoice(surfaceBlock, groundBlock, maxNoise)); } Collections.sort(layerChoices); diff --git a/common/src/main/java/com/pg85/otg/generator/surface/SimpleSurfaceGenerator.java b/common/src/main/java/com/pg85/otg/generator/surface/SimpleSurfaceGenerator.java index 9bf12e658..ce3ba98ad 100644 --- a/common/src/main/java/com/pg85/otg/generator/surface/SimpleSurfaceGenerator.java +++ b/common/src/main/java/com/pg85/otg/generator/surface/SimpleSurfaceGenerator.java @@ -72,7 +72,7 @@ protected final void spawnColumn(LocalWorld world, LayerChoice layer, Generating // Surface blocks logic (grass, dirt, sand, sandstone) blockOnCurrentPos = chunkBuffer.getBlock(x, y, z); - if (blockOnCurrentPos.isEmptyOrAir()) + if (blockOnCurrentPos.isAir()) { // Reset when air is found groundLayerDepth = -1; @@ -85,7 +85,7 @@ protected final void spawnColumn(LocalWorld world, LayerChoice layer, Generating // same biome water block as surface/ground/stone block. // TODO: If other mods have problems bc of replaced blocks in the chunk during ReplaceBiomeBlocks, // do replaceblock for stone/water here instead of when initially filling the chunk. - else if(!blockOnCurrentPos.equals(biomeConfig.getWaterBlockReplaced(world, y))) + else if(!biomeConfig.getWaterBlockReplaced(world, y).matches(blockOnCurrentPos)) { // Place surface/ground down to a certain depth per column, // determined via noise. groundLayerDepth == 0 means we're diff --git a/common/src/main/java/com/pg85/otg/generator/terrain/RavinesGen.java b/common/src/main/java/com/pg85/otg/generator/terrain/RavinesGen.java index 4d0784f34..cadb9942f 100644 --- a/common/src/main/java/com/pg85/otg/generator/terrain/RavinesGen.java +++ b/common/src/main/java/com/pg85/otg/generator/terrain/RavinesGen.java @@ -210,7 +210,7 @@ private void placeBlocks(long paramLong, ChunkBuffer generatingChunkBuffer, doub { material = generatingChunkBuffer.getBlock(localX, currentDepth, localZ); - if (!surfaceBlockFound && material.equals(biomeConfig.getSurfaceBlockReplaced(this.world, currentDepth))) + if (!surfaceBlockFound && biomeConfig.getSurfaceBlockReplaced(this.world, currentDepth).matches(material)) { surfaceBlockFound = true; surfaceBlockMaterial = material; diff --git a/common/src/main/java/com/pg85/otg/network/ClientConfigProvider.java b/common/src/main/java/com/pg85/otg/network/ClientConfigProvider.java index 890979786..fec5ff867 100644 --- a/common/src/main/java/com/pg85/otg/network/ClientConfigProvider.java +++ b/common/src/main/java/com/pg85/otg/network/ClientConfigProvider.java @@ -15,6 +15,7 @@ import com.pg85.otg.util.helpers.StreamHelper; import com.pg85.otg.util.minecraft.defaults.BiomeRegistryNames; import com.pg85.otg.util.minecraft.defaults.DefaultBiome; +import com.pg85.otg.worldsave.WorldSaveData; import java.io.DataInputStream; import java.io.File; @@ -41,6 +42,8 @@ public final class ClientConfigProvider implements ConfigProvider private LocalBiome[] biomesByOTGId; // For the server, OTGBiomeIds are used, for the client only non-virtual biomes are known and saved Id's are used private LocalBiome[] biomesBySavedId; // For the server, OTGBiomeIds are used, for the client only non-virtual biomes are known and saved Id's are used + private WorldSaveData worldSaveData; + public ClientConfigProvider(DataInputStream stream, LocalWorld world) throws IOException { // Create WorldConfig @@ -98,6 +101,8 @@ public ClientConfigProvider(DataInputStream stream, LocalWorld world) throws IOE OTG.getEngine().setOTGBiomeId(world.getName(), otgBiomeId, config, true); } + + worldSaveData = new WorldSaveData(stream.readInt()); } @Override @@ -161,4 +166,10 @@ public List getBiomeArrayLegacy() return outputBiomes; } + + @Override + public WorldSaveData getWorldSaveData() + { + return worldSaveData; + } } diff --git a/common/src/main/java/com/pg85/otg/network/ConfigProvider.java b/common/src/main/java/com/pg85/otg/network/ConfigProvider.java index 0322bde19..5fc1205c2 100644 --- a/common/src/main/java/com/pg85/otg/network/ConfigProvider.java +++ b/common/src/main/java/com/pg85/otg/network/ConfigProvider.java @@ -4,6 +4,7 @@ import com.pg85.otg.common.LocalBiome; import com.pg85.otg.configuration.world.WorldConfig; +import com.pg85.otg.worldsave.WorldSaveData; /** * Provides the configuration objects of a world. This includes: @@ -49,4 +50,6 @@ public interface ConfigProvider * method does nothing. */ void reload(); + + WorldSaveData getWorldSaveData(); } diff --git a/common/src/main/java/com/pg85/otg/network/ConfigToNetworkSender.java b/common/src/main/java/com/pg85/otg/network/ConfigToNetworkSender.java index 70f616b1b..4685f8e9d 100644 --- a/common/src/main/java/com/pg85/otg/network/ConfigToNetworkSender.java +++ b/common/src/main/java/com/pg85/otg/network/ConfigToNetworkSender.java @@ -75,5 +75,7 @@ public static void writeConfigsToStream(ConfigProvider configProvider, DataOutpu stream.writeInt(biome.getIds().getSavedId()); biome.getBiomeConfig().writeToStream(stream, isSinglePlayer); } + + stream.writeInt(configProvider.getWorldSaveData().version); } } diff --git a/common/src/main/java/com/pg85/otg/network/ServerConfigProvider.java b/common/src/main/java/com/pg85/otg/network/ServerConfigProvider.java index 17231d3d0..762896ae9 100644 --- a/common/src/main/java/com/pg85/otg/network/ServerConfigProvider.java +++ b/common/src/main/java/com/pg85/otg/network/ServerConfigProvider.java @@ -27,6 +27,8 @@ import com.pg85.otg.worldsave.BiomeIdData; import com.pg85.otg.worldsave.WorldSaveData; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; + /** * Holds the WorldConfig and all BiomeConfigs. * @@ -61,6 +63,8 @@ public final class ServerConfigProvider implements ConfigProvider */ private int biomesCount; + private WorldSaveData worldSaveData; + /** * Loads the settings from the given directory for the given world. * @param settingsDir The directory to load from. @@ -469,7 +473,7 @@ private String indexSettings(Map worldBiomes, boolean isNewWorl } // Get OTG world save version - WorldSaveData worldSaveData = WorldSaveData.loadWorldSaveData(worldSaveFolder); + worldSaveData = WorldSaveData.loadWorldSaveData(worldSaveFolder); // This is a legacy (pre-v7) world if its not being created and either has no worldsavedata or worldsavedata version 6. // If this world has biome data but not worldsavedata, it's v7. @@ -478,7 +482,7 @@ private String indexSettings(Map worldBiomes, boolean isNewWorl //boolean isLegacyWorld = (!hasWorldData || (worldSaveData != null && worldSaveData.version == 6)); if(worldSaveData == null) { - worldSaveData = new WorldSaveData(isLegacyWorld ? 6 : 8); + worldSaveData = new WorldSaveData(isLegacyWorld ? 6 : 9); WorldSaveData.saveWorldSaveData(worldSaveFolder, worldSaveData); } @@ -730,11 +734,11 @@ private void createAndRegisterBiome(ArrayList loadedBiomeIdData, Bi // Indexing BiomeColor if (this.worldConfig.biomeMode == OTG.getBiomeModeManager().FROM_IMAGE) { if (this.worldConfig.biomeColorMap == null) { - this.worldConfig.biomeColorMap = new HashMap(); + this.worldConfig.biomeColorMap = new Int2IntOpenHashMap(); + this.worldConfig.biomeColorMap.defaultReturnValue(-1); } - int color = biomeConfig.biomeColor; - this.worldConfig.biomeColorMap.put(color, biome.getIds().getOTGBiomeId()); + this.worldConfig.biomeColorMap.put(biomeConfig.biomeColor, biome.getIds().getOTGBiomeId()); } } /** Recursive method to get saved ID from ReplaceToBiomeName, allows chain replacing to virtual biomes. @@ -826,4 +830,10 @@ public List getBiomeArrayLegacy() return outputBiomes; } + + @Override + public WorldSaveData getWorldSaveData() + { + return worldSaveData; + } } diff --git a/common/src/main/java/com/pg85/otg/util/BlockPos2D.java b/common/src/main/java/com/pg85/otg/util/BlockPos2D.java index 491fb1b81..2e7c3835c 100644 --- a/common/src/main/java/com/pg85/otg/util/BlockPos2D.java +++ b/common/src/main/java/com/pg85/otg/util/BlockPos2D.java @@ -2,28 +2,36 @@ public class BlockPos2D { - final int x; - final int z; - - public BlockPos2D(int x, int z) - { - this.x = x; - this.z = z; - } - - public boolean equals(Object other) - { - if(this == other) - { - return true; - } - if(other instanceof BlockPos2D) - { - if(((BlockPos2D)other).x == this.x && ((BlockPos2D)other).z == this.z) - { - return true; - } - } - return false; - } + public final int x; + public final int z; + + public BlockPos2D(int x, int z) + { + this.x = x; + this.z = z; + } + + @Override + public int hashCode() + { + int hash = 1; + hash = hash * 31 + this.x; + hash = hash * 31 + this.z; + return hash; + } + + @Override + public boolean equals(Object obj) + { + if(this == obj) + { + return true; + } + if(obj instanceof BlockPos2D) + { + BlockPos2D other = (BlockPos2D) obj; + return this.x == other.x && this.z == other.z; + } + return false; + } } \ No newline at end of file diff --git a/common/src/main/java/com/pg85/otg/util/BlockPos3D.java b/common/src/main/java/com/pg85/otg/util/BlockPos3D.java index 8eb53dbcf..d790e050c 100644 --- a/common/src/main/java/com/pg85/otg/util/BlockPos3D.java +++ b/common/src/main/java/com/pg85/otg/util/BlockPos3D.java @@ -2,30 +2,39 @@ public class BlockPos3D { - final int x; - final int y; - final int z; - - public BlockPos3D(int x, int y, int z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public boolean equals(Object other) - { - if(this == other) - { - return true; - } - if(other instanceof BlockPos3D) - { - if(((BlockPos3D)other).x == this.x && ((BlockPos3D)other).z == this.z) - { - return true; - } - } - return false; - } + public final int x; + public final int y; + public final int z; + + public BlockPos3D(int x, int y, int z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public int hashCode() + { + int hash = 1; + hash = hash * 31 + this.x; + hash = hash * 31 + this.y; + hash = hash * 31 + this.z; + return hash; + } + + @Override + public boolean equals(Object obj) + { + if(this == obj) + { + return true; + } + if(obj instanceof BlockPos3D) + { + BlockPos3D other = (BlockPos3D) obj; + return this.x == other.x && this.y == other.y && this.z == other.z; + } + return false; + } } \ No newline at end of file diff --git a/common/src/main/java/com/pg85/otg/util/ChunkCoordinate.java b/common/src/main/java/com/pg85/otg/util/ChunkCoordinate.java index c146015d6..0cdf8ebe0 100644 --- a/common/src/main/java/com/pg85/otg/util/ChunkCoordinate.java +++ b/common/src/main/java/com/pg85/otg/util/ChunkCoordinate.java @@ -1,5 +1,10 @@ package com.pg85.otg.util; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +import com.pg85.otg.configuration.customobjects.CustomObjectConfigFunction; import com.pg85.otg.customobjects.structures.CustomStructureCache; import com.pg85.otg.util.helpers.MathHelper; @@ -139,22 +144,63 @@ public static ChunkCoordinate fromChunkCoords(int chunkX, int chunkZ) return new ChunkCoordinate(chunkX, chunkZ); } + public static ChunkCoordinate fromPacked(long packed) + { + return new ChunkCoordinate(packedX(packed), packedZ(packed)); + } + + public static long packed(int chunkX, int chunkZ) + { + return (chunkZ & 0xFFFFFFFFL) << 32 | chunkX & 0xFFFFFFFFL; + } + + public static int packedX(long packed) + { + return (int) packed; + } + + public static int packedZ(long packed) + { + return (int) (packed >> 32); + } + + public static ChunkCoordinate read(DataInput in) throws IOException + { + return new ChunkCoordinate(in.readInt(), in.readInt()); + } + + public static void write(DataOutput out, ChunkCoordinate chunkCoordinate) throws IOException + { + out.writeInt(chunkCoordinate.chunkX); + out.writeInt(chunkCoordinate.chunkZ); + } + public ChunkCoordinate toRegionCoord() { return ChunkCoordinate.fromChunkCoords( - MathHelper.floor((double)getChunkX() / (double)CustomStructureCache.REGION_SIZE), - MathHelper.floor((double)getChunkZ() / (double)CustomStructureCache.REGION_SIZE) + MathHelper.floorDiv(chunkX, CustomStructureCache.REGION_SIZE), + MathHelper.floorDiv(chunkZ, CustomStructureCache.REGION_SIZE) ); } + public boolean regionContainsChunk(ChunkCoordinate chunkCoordinate) + { + return chunkCoordinate.toRegionCoord().equals(this); + } + + public boolean regionContains(CustomObjectConfigFunction function) + { + return regionContainsChunk(ChunkCoordinate.fromChunkCoords(function.x(), function.z())); + } + public int getRegionInternalX() { - return MathHelper.mod(getChunkX(), CustomStructureCache.REGION_SIZE); + return MathHelper.floorMod(chunkX, CustomStructureCache.REGION_SIZE); } public int getRegionInternalZ() { - return MathHelper.mod(getChunkZ(), CustomStructureCache.REGION_SIZE); + return MathHelper.floorMod(chunkZ, CustomStructureCache.REGION_SIZE); } @Override @@ -168,7 +214,7 @@ public String toString() * @return The x position. */ public int getBlockXCenter() { - return chunkX * CHUNK_SIZE + CHUNK_POPULATION_OFFSET_X; + return (chunkX << 4) | CHUNK_POPULATION_OFFSET_X; } /** @@ -176,7 +222,7 @@ public int getBlockXCenter() { * @return The z position. */ public int getBlockZCenter() { - return chunkZ * CHUNK_SIZE + CHUNK_POPULATION_OFFSET_Z; + return (chunkZ << 4) | CHUNK_POPULATION_OFFSET_Z; } /** @@ -186,7 +232,7 @@ public int getBlockZCenter() { */ public int getBlockX() { - return chunkX * CHUNK_SIZE; + return chunkX << 4; } /** @@ -196,7 +242,7 @@ public int getBlockX() */ public int getBlockZ() { - return chunkZ * CHUNK_SIZE; + return chunkZ << 4; } /** diff --git a/common/src/main/java/com/pg85/otg/util/CompressionUtils.java b/common/src/main/java/com/pg85/otg/util/CompressionUtils.java index 2d765889c..9a77e155e 100644 --- a/common/src/main/java/com/pg85/otg/util/CompressionUtils.java +++ b/common/src/main/java/com/pg85/otg/util/CompressionUtils.java @@ -1,53 +1,51 @@ package com.pg85.otg.util; -import java.io.ByteArrayOutputStream; +import java.io.EOFException; import java.io.IOException; -import java.util.zip.DataFormatException; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - -import com.pg85.otg.OTG; -import com.pg85.otg.logging.LogMarker; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.Channels; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import java.util.zip.InflaterInputStream; public class CompressionUtils { - public static byte[] compress(byte[] data) throws IOException - { - Deflater deflater = new Deflater(); - deflater.setInput(data); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); - deflater.finish(); - byte[] buffer = new byte[1024]; - while (!deflater.finished()) - { - int count = deflater.deflate(buffer); // returns the generated code... index - outputStream.write(buffer, 0, count); - } - outputStream.close(); - byte[] output = outputStream.toByteArray(); - if(OTG.getPluginConfig().spawnLog) - { - OTG.log(LogMarker.INFO, "Original: " + data.length / 1024 + " Kb"); - OTG.log(LogMarker.INFO, "Compressed: " + output.length / 1024 + " Kb"); - } - return output; - } + public static InputStream newInflaterInputStream(Path file) throws IOException + { + return new InflaterInputStream(Files.newInputStream(file)); + } + + public static OutputStream newDeflaterOutputStream(Path file) throws IOException + { + return new DeflaterOutputStream(Files.newOutputStream(file)); + } + + public static InputStream newGZIPInputStream(Path file) throws IOException + { + @SuppressWarnings("resource") + SeekableByteChannel channel = Files.newByteChannel(file); + ByteBuffer buf = ByteBuffer.allocate(2).order(ByteOrder.nativeOrder()); + while(buf.hasRemaining()) + { + if(channel.read(buf) < 0) + { + throw new EOFException(); + } + } + channel.position(0); + InputStream in = Channels.newInputStream(channel); + return Short.toUnsignedInt(buf.getShort(0)) == GZIPInputStream.GZIP_MAGIC ? new GZIPInputStream(in) : in; + } - public static byte[] decompress(byte[] data) throws IOException, DataFormatException - { - Inflater inflater = new Inflater(); - inflater.setInput(data); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); - byte[] buffer = new byte[1024]; - while (!inflater.finished()) - { - int count = inflater.inflate(buffer); - outputStream.write(buffer, 0, count); - } - outputStream.close(); - byte[] output = outputStream.toByteArray(); - //OTG.log(LogMarker.INFO, "Original: " + data.length); - //OTG.log(LogMarker.INFO, "Decompressed: " + output.length); - return output; - } + public static OutputStream newGZIPOutputStream(Path file) throws IOException + { + return new GZIPOutputStream(Files.newOutputStream(file)); + } } diff --git a/common/src/main/java/com/pg85/otg/util/DataUtil.java b/common/src/main/java/com/pg85/otg/util/DataUtil.java new file mode 100644 index 000000000..b01277501 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/util/DataUtil.java @@ -0,0 +1,306 @@ +package com.pg85.otg.util; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; + +import com.pg85.otg.OTG; +import com.pg85.otg.logging.LogMarker; + +public class DataUtil +{ + public interface IOConsumer + { + void accept(T t) throws IOException; + } + + public interface IOBiConsumer + { + void accept(T t, U u) throws IOException; + } + + public interface IOTriConsumer + { + void accept(T t, U u, V v) throws IOException; + } + + public interface IOFunction + { + R apply(T t) throws IOException; + } + + public static void writeCompressed(Path file, Path backup, IOConsumer writer) + { + try + { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + writer.accept(new DataOutputStream(buffer)); + + if(!Files.exists(file)) + { + Files.createDirectories(file.getParent()); + } + else + { + Files.move(file, backup, StandardCopyOption.REPLACE_EXISTING); + } + try(OutputStream out = CompressionUtils.newDeflaterOutputStream(file)) + { + out.write(buffer.toByteArray()); + } + } + catch(IOException e) + { + e.printStackTrace(); + OTG.log(LogMarker.INFO, "OTG encountered an error writing " + file.toAbsolutePath() + ", skipping."); + } + } + + public static void readCompressed(Path file, Path backup, IOConsumer reader) + { + readCompressed(file, backup, (p, in) -> reader.accept(in)); + } + + public static void readCompressed(Path directory, String extension, String backupExtension, IOBiConsumer reader) + { + try + { + Files.find(directory, 0, (p, a) -> a.isRegularFile()) + .map(directory::relativize) + .map(Path::toString) + .filter(p -> p.endsWith(extension) || p.endsWith(backupExtension)) + .map(p -> StringUtils.removeEnd(p, extension)) + .map(p -> StringUtils.removeEnd(p, backupExtension)) + .distinct() + .forEach(p -> readCompressed(directory.resolve(p + extension), directory.resolve(p + backupExtension), reader)); + } + catch(IOException e) + { + e.printStackTrace(); + } + } + + public static void readCompressed(Path file, Path backup, IOBiConsumer reader) + { + try(DataInputStream in = new DataInputStream(new BufferedInputStream(CompressionUtils.newInflaterInputStream(file)))) + { + reader.accept(file, in); + return; + } + catch(IOException e) + { + OTG.log(LogMarker.INFO, "Failed to load " + file.toAbsolutePath() + ", trying to load backup."); + e.printStackTrace(); + + try(DataInputStream in = new DataInputStream(new BufferedInputStream(CompressionUtils.newInflaterInputStream(backup)))) + { + reader.accept(backup, in); + } + catch(IOException e1) + { + OTG.log(LogMarker.INFO, "OTG encountered an error loading " + file.toAbsolutePath() + " and could not load a backup, skipping."); + e1.printStackTrace(); + } + } + } + + public static IOBiConsumer mappingWriter(DataOutputStream out, UnaryOperator mappingFunction, IOBiConsumer mappedWriter) + { + return (out1, t) -> mappedWriter.accept(out1, mappingFunction.apply(t)); + } + + public static > IOBiConsumer collectionWriter(IOBiConsumer elementWriter) + { + return (out, collection) -> writeCollection(collection, out, elementWriter); + } + + public static > IOBiConsumer mapWriter(IOBiConsumer keyWriter, IOBiConsumer valueWriter) + { + return (out, map) -> writeMap(map, out, keyWriter, valueWriter); + } + + public static > IOBiConsumer mapWriter(IOTriConsumer entryWriter) + { + return (out, map) -> writeMap(map, out, entryWriter); + } + + public static void writeCollection(Collection collection, DataOutputStream out, IOBiConsumer elementWriter) throws IOException + { + out.writeInt(collection.size()); + for(T t : collection) + { + elementWriter.accept(out, t); + } + } + + public static void writeMap(Map map, DataOutputStream out, IOBiConsumer keyWriter, IOBiConsumer valueWriter) throws IOException + { + out.writeInt(map.size()); + for(Entry entry : map.entrySet()) + { + keyWriter.accept(out, entry.getKey()); + valueWriter.accept(out, entry.getValue()); + } + } + + public static void writeMap(Map map, DataOutputStream out, IOTriConsumer entryWriter) throws IOException + { + out.writeInt(map.size()); + for(Entry entry : map.entrySet()) + { + entryWriter.accept(out, entry.getKey(), entry.getValue()); + } + } + + public static > IOFunction collectionReader(Supplier collectionSupplier, IOFunction elementReader) + { + return in -> { + C collection = collectionSupplier.get(); + readCollection(collection, in, elementReader); + return collection; + }; + } + + public static > IOFunction mapReader(Supplier mapSupplier, IOFunction keyReader, IOFunction valueReader) + { + return in -> { + M map = mapSupplier.get(); + readMap(map, in, keyReader, valueReader); + return map; + }; + } + + public static > IOFunction mapReader(Supplier mapSupplier, IOFunction> entryReader) + { + return in -> { + M map = mapSupplier.get(); + readMap(map, in, entryReader); + return map; + }; + } + + public static > C readCollection(C collection, DataInputStream in, IOFunction elementReader) throws IOException + { + for(int i = in.readInt(); i > 0; i--) + { + collection.add(elementReader.apply(in)); + } + return collection; + } + + public static > C readCollection(C collection, DataInputStream in, IOBiConsumer elementReader) throws IOException + { + for(int i = in.readInt(); i > 0; i--) + { + elementReader.accept(collection, in); + } + return collection; + } + + public static > M readMap(M map, DataInputStream in, IOFunction keyReader, IOFunction valueReader) throws IOException + { + for(int i = in.readInt(); i > 0; i--) + { + map.put(keyReader.apply(in), valueReader.apply(in)); + } + return map; + } + + public static > M readMap(M map, DataInputStream in, IOFunction> entryReader) throws IOException + { + for(int i = in.readInt(); i > 0; i--) + { + Entry entry = entryReader.apply(in); + map.put(entry.getKey(), entry.getValue()); + } + return map; + } + + public static > M readMap(M map, DataInputStream in, IOBiConsumer entryReader) throws IOException + { + for(int i = in.readInt(); i > 0; i--) + { + entryReader.accept(map, in); + } + return map; + } + + public static void writeOptional(T t, Function mappingFunction, Predicate filter, DataOutputStream out, IOBiConsumer valueWriter) throws IOException + { + R r; + boolean matches = filter.test(r = mappingFunction.apply(t)); + out.writeBoolean(matches); + if(matches) + { + valueWriter.accept(out, r); + } + } + + public static void writeOptional(T t, Class c, Function mappingFunction, Predicate filter, DataOutputStream out, IOBiConsumer valueWriter) throws IOException + { + if(!c.isInstance(t)) + { + out.writeBoolean(false); + } + else + { + writeOptional(c.cast(t), mappingFunction, filter, out, valueWriter); + } + } + + public static T readOptional(T t, DataInputStream in, IOFunction reader) throws IOException + { + if(in.readBoolean()) + { + return reader.apply(in); + } + return t; + } + + public static UnaryOperator> filter(Predicate filter) + { + return collection -> collection.stream().filter(filter).collect(Collectors.toList()); + } + + public static UnaryOperator> filterByKey(Predicate keyFilter) + { + return filterByEntry((k, v) -> keyFilter.test(k)); + } + + public static UnaryOperator> filterByValue(Predicate valueFilter) + { + return filterByEntry((k, v) -> valueFilter.test(v)); + } + + public static UnaryOperator> filterByEntry(BiPredicate entryFilter) + { + return map -> map.entrySet().stream().filter(e -> entryFilter.test(e.getKey(), e.getValue())).collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + } + + public static boolean nonEmpty(Collection collection) + { + return !collection.isEmpty(); + } + + public static boolean nonEmpty(Map map) + { + return !map.isEmpty(); + } +} diff --git a/common/src/main/java/com/pg85/otg/util/FifoMap.java b/common/src/main/java/com/pg85/otg/util/FifoMap.java deleted file mode 100644 index 8af6c6ba6..000000000 --- a/common/src/main/java/com/pg85/otg/util/FifoMap.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.pg85.otg.util; - -import java.util.LinkedHashMap; -import java.util.Map; - -public class FifoMap extends LinkedHashMap -{ - private final int max; - - public FifoMap (int max) { - super(max + 1); - this.max = max; - } - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return this.size() > max; - } -} \ No newline at end of file diff --git a/common/src/main/java/com/pg85/otg/util/LRUCache.java b/common/src/main/java/com/pg85/otg/util/LRUCache.java new file mode 100644 index 000000000..9910ab381 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/util/LRUCache.java @@ -0,0 +1,23 @@ +package com.pg85.otg.util; + +import java.util.LinkedHashMap; +import java.util.Map; + +@SuppressWarnings("serial") +public class LRUCache extends LinkedHashMap +{ + private static final float LOAD_FACTOR = 0.75F; + private final int max; + + public LRUCache(int max) + { + super((int) Math.ceil((max + 1) / LOAD_FACTOR), LOAD_FACTOR, true); + this.max = max; + } + + @Override + protected boolean removeEldestEntry(Map.Entry eldest) + { + return this.size() > this.max; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/pg85/otg/util/RNG.java b/common/src/main/java/com/pg85/otg/util/RNG.java new file mode 100644 index 000000000..dbd16f067 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/util/RNG.java @@ -0,0 +1,104 @@ +package com.pg85.otg.util; + +public class RNG +{ + private long state; + + public RNG() + { + this(murmurHash3(System.nanoTime()) ^ murmurHash3(System.currentTimeMillis())); + } + + public RNG(long seed) + { + setSeed(seed); + } + + public void setSeed(long seed) + { + state = murmurHash3(seed); + } + + public void setState(long state) + { + this.state = state; + } + + public long getState() + { + return state; + } + + /** + * Based on MurmurHash3 + */ + public static long murmurHash3(long x) + { + x = (x ^ (x >>> 33)) * 0xff51afd7ed558ccdL; + x = (x ^ (x >>> 33)) * 0xc4ceb9fe1a85ec53L; + return x ^ (x >>> 33); + } + + /** + * Based on Stafford's Mix4 + */ + public int nextInt() + { + long z = (state += 0x9e3779b97f4a7c15L); + z = (z ^ (z >>> 33)) * 0x62a9d9ed799705f5L; + z = (z ^ (z >>> 28)) * 0xcb24d0a5c88c35b3L; + return (int) (z >>> 32); + } + + /** + * Based on Stafford's Mix13 + */ + public long nextLong() + { + long z = (state += 0x9e3779b97f4a7c15L); + z = (z ^ (z >>> 30)) * 0xbf58476d1ce4e5b9L; + z = (z ^ (z >>> 27)) * 0x94d049bb133111ebL; + return z ^ (z >>> 31); + } + + public boolean nextBoolean() + { + return nextInt() < 0; + } + + public float nextFloat() + { + return (nextInt() >>> 8) * 0x1.0p-24f; + } + + public double nextDouble() + { + return (nextLong() >>> 11) * 0x1.0p-53; + } + + public int nextInt(int bound) + { + if(bound <= 0) + throw new IllegalArgumentException("bound must be positive"); + int m = bound - 1; + if((bound & m) == 0) + return nextInt() & m; + int r; + while((r = nextInt() >>> 1) - (r %= bound) + m < 0) + ; + return r; + } + + public long nextLong(long bound) + { + if(bound <= 0) + throw new IllegalArgumentException("bound must be positive"); + long m = bound - 1; + if((bound & m) == 0) + return nextLong() & m; + long r; + while((r = nextLong() >>> 1) - (r %= bound) + m < 0) + ; + return r; + } +} diff --git a/common/src/main/java/com/pg85/otg/util/WeightedList.java b/common/src/main/java/com/pg85/otg/util/WeightedList.java new file mode 100644 index 000000000..e181482c8 --- /dev/null +++ b/common/src/main/java/com/pg85/otg/util/WeightedList.java @@ -0,0 +1,102 @@ +package com.pg85.otg.util; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.NoSuchElementException; +import java.util.function.IntUnaryOperator; + +import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; + +public class WeightedList +{ + private static final Field tree; + private static final Method left; + private static final Method right; + static + { + try + { + tree = Int2ObjectAVLTreeMap.class.getDeclaredField("tree"); + left = Class.forName(Int2ObjectAVLTreeMap.class.getName() + "$Entry").getDeclaredMethod("left"); + right = Class.forName(Int2ObjectAVLTreeMap.class.getName() + "$Entry").getDeclaredMethod("right"); + tree.setAccessible(true); + left.setAccessible(true); + right.setAccessible(true); + } + catch(ReflectiveOperationException e) + { + throw new UnsupportedOperationException(e); + } + } + + private final Int2ObjectAVLTreeMap map = new Int2ObjectAVLTreeMap<>(); + + public boolean isEmpty() + { + return this.map.isEmpty(); + } + + public int size() + { + return this.map.size(); + } + + public int totalWeight() + { + return !this.map.isEmpty() ? this.map.lastIntKey() : 0; + } + + public void add(T value, int weight) + { + if(weight > 0) + { + int key = this.totalWeight() + weight; + if(key < 0) + { + throw new IndexOutOfBoundsException(); + } + this.map.put(key, value); + } + } + + @SuppressWarnings("unchecked") + public T getRandom(IntUnaryOperator rng) + { + if(this.map.isEmpty()) + { + throw new NoSuchElementException(); + } + try + { + if(this.map.size() == 1) + { + return ((Int2ObjectMap.Entry) tree.get(this.map)).getValue(); + } + + int t = this.map.lastIntKey(); + int r = rng.applyAsInt(t); + if(r < 0 || r >= t) + { + throw new IndexOutOfBoundsException(); + } + T value = null; + Int2ObjectMap.Entry entry = (Int2ObjectMap.Entry) tree.get(this.map); + while(entry != null) + { + if(entry.getIntKey() <= r) + { + entry = (Int2ObjectMap.Entry) right.invoke(entry); + continue; + } + value = entry.getValue(); + entry = (Int2ObjectMap.Entry) left.invoke(entry); + } + return value; + } + catch(ReflectiveOperationException e) + { + throw new UnsupportedOperationException(e); + } + } +} diff --git a/common/src/main/java/com/pg85/otg/util/bo3/BoundingBox.java b/common/src/main/java/com/pg85/otg/util/bo3/BoundingBox.java index 713b6683f..daf56a807 100644 --- a/common/src/main/java/com/pg85/otg/util/bo3/BoundingBox.java +++ b/common/src/main/java/com/pg85/otg/util/bo3/BoundingBox.java @@ -67,15 +67,45 @@ public BoundingBox clone() * Gets a new bounding box that is rotated 90 degrees to the original. The original bounding box will not be modified. * @return A new, rotated bounding box. */ - public BoundingBox rotate() + public BoundingBox rotate(Rotation rotation) { + int rotatedMinX; + int rotatedMinZ; + int rotatedMaxX; + int rotatedMaxZ; + switch(rotation) + { + case NORTH: + return this; + case WEST: + rotatedMinX = minZ; + rotatedMinZ = -minX; + rotatedMaxX = maxZ; + rotatedMaxZ = -maxX; + break; + case SOUTH: + rotatedMinX = -minX; + rotatedMinZ = -minZ; + rotatedMaxX = -maxX; + rotatedMaxZ = -maxZ; + break; + case EAST: + rotatedMinX = -minZ; + rotatedMinZ = minX; + rotatedMaxX = -maxZ; + rotatedMaxZ = maxX; + break; + default: + throw new IllegalArgumentException(); + } + BoundingBox cloned = new BoundingBox(); - cloned.minX = minZ; + cloned.minX = rotatedMinX; cloned.minY = minY; - cloned.minZ = -minX; - cloned.maxX = maxZ; + cloned.minZ = rotatedMinZ; + cloned.maxX = rotatedMaxX; cloned.maxY = maxY; - cloned.maxZ = -maxX; + cloned.maxZ = rotatedMaxZ; return cloned; } diff --git a/common/src/main/java/com/pg85/otg/util/bo3/NamedBinaryTag.java b/common/src/main/java/com/pg85/otg/util/bo3/NamedBinaryTag.java index ba8280f3f..ca2ec676c 100644 --- a/common/src/main/java/com/pg85/otg/util/bo3/NamedBinaryTag.java +++ b/common/src/main/java/com/pg85/otg/util/bo3/NamedBinaryTag.java @@ -1,8 +1,15 @@ package com.pg85.otg.util.bo3; -import java.io.*; -import java.util.zip.GZIPInputStream; -import java.util.zip.GZIPOutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; + +import com.pg85.otg.exception.InvalidConfigException; +import com.pg85.otg.util.CompressionUtils; /** * NBT IO class @@ -34,7 +41,10 @@ public enum Type TAG_String, TAG_List, TAG_Compound, - TAG_Int_Array + TAG_Int_Array, + TAG_Long_Array; + + public static final Type[] VALUES = Type.values(); } /** @@ -129,6 +139,10 @@ public NamedBinaryTag(Type type, String name, Object value) if (!(value instanceof int[])) throw new IllegalArgumentException(); break; + case TAG_Long_Array: + if (!(value instanceof long[])) + throw new IllegalArgumentException(); + break; default: throw new IllegalArgumentException(); } @@ -211,6 +225,10 @@ public void setValue(Object newValue) if (!(value instanceof int[])) throw new IllegalArgumentException(); break; + case TAG_Long_Array: + if (!(value instanceof long[])) + throw new IllegalArgumentException(); + break; default: throw new IllegalArgumentException(); } @@ -360,232 +378,219 @@ private NamedBinaryTag[] values() return (NamedBinaryTag[]) value; } + public static NamedBinaryTag readFrom(Path file) throws IOException, InvalidConfigException + { + try(DataInputStream in = new DataInputStream(new BufferedInputStream(CompressionUtils.newGZIPInputStream(file)))) + { + return readFrom(in); + } + } + /** - * Read a tag and its nested tags from an InputStream. + * Read a tag and its nested tags from a {@link DataInputStream} * - * @param is stream to read from, like a FileInputStream - * @param compressed True if the stream is GZIP-compressed, false otherwise. + * @param in stream to read from * @return NBT tag or structure read from the InputStream * @throws IOException if there was no valid NBT structure in the * InputStream or if another IOException occurred. */ - public static NamedBinaryTag readFrom(InputStream is, boolean compressed) throws IOException + public static NamedBinaryTag readFrom(DataInputStream in) throws IOException, InvalidConfigException { - DataInputStream dis = null; - if (compressed) - { - dis = new DataInputStream(new GZIPInputStream(is)); - } else + try { - dis = new DataInputStream(is); + Type type = readType(in); + if(type == Type.TAG_End) + { + return new NamedBinaryTag(type, null, null); + } + return new NamedBinaryTag(type, in.readUTF(), readPayload(in, type)); } - - byte type = dis.readByte(); - NamedBinaryTag tag = null; - - if (type == 0) + catch(IOException e) { - tag = new NamedBinaryTag(Type.TAG_End, null, null); - } else + throw e; + } + catch(Exception e) { - try - { - tag = new NamedBinaryTag(Type.values()[type], dis.readUTF(), readPayload(dis, type)); - } - catch(IndexOutOfBoundsException ex) - { - // Incorrect NBT structure, NBT may need to be updated for use with this MC version. MC 1.11.2 changed nbt data structure for chests. - } + throw new InvalidConfigException(null, e); } + } - dis.close(); - - return tag; + private static Type readType(DataInputStream in) throws IOException + { + return Type.VALUES[in.readByte()]; } - private static Object readPayload(DataInputStream dis, byte type) throws IOException + private static Object readPayload(DataInputStream in, Type type) throws IOException { - switch (type) + switch(type) { - case 0: + case TAG_End: return null; - case 1: - return dis.readByte(); - case 2: - return dis.readShort(); - case 3: - return dis.readInt(); - case 4: - return dis.readLong(); - case 5: - return dis.readFloat(); - case 6: - return dis.readDouble(); - case 7: - int length = dis.readInt(); - byte[] ba = new byte[length]; - dis.readFully(ba); - return ba; - case 8: - return dis.readUTF(); - case 9: - byte lt = dis.readByte(); - int ll = dis.readInt(); - NamedBinaryTag[] lo = new NamedBinaryTag[ll]; - for (int i = 0; i < ll; i++) + case TAG_Byte: + return in.readByte(); + case TAG_Short: + return in.readShort(); + case TAG_Int: + return in.readInt(); + case TAG_Long: + return in.readLong(); + case TAG_Float: + return in.readFloat(); + case TAG_Double: + return in.readDouble(); + case TAG_Byte_Array: + byte[] bytes = new byte[in.readInt()]; + in.readFully(bytes); + return bytes; + case TAG_String: + return in.readUTF(); + case TAG_List: + Type elementType = readType(in); + int size = in.readInt(); + if(size == 0) + { + return elementType; + } + NamedBinaryTag[] elements = new NamedBinaryTag[size]; + for(int i = 0; i < size; i++) { - lo[i] = new NamedBinaryTag(Type.values()[lt], null, readPayload(dis, lt)); + elements[i] = new NamedBinaryTag(elementType, null, readPayload(in, elementType)); } - if (lo.length == 0) - return Type.values()[lt]; - else - return lo; - case 10: - byte stt; + return elements; + case TAG_Compound: NamedBinaryTag[] tags = new NamedBinaryTag[0]; - do + Type tagType; + while((tagType = readType(in)) != Type.TAG_End) { - stt = dis.readByte(); - String name = null; - if (stt != 0) - { - name = dis.readUTF(); - } - NamedBinaryTag[] newTags = new NamedBinaryTag[tags.length + 1]; - System.arraycopy(tags, 0, newTags, 0, tags.length); - newTags[tags.length] = new NamedBinaryTag(Type.values()[stt], name, readPayload(dis, stt)); - tags = newTags; - } while (stt != 0); + tags = Arrays.copyOf(tags, tags.length + 1); + tags[tags.length - 1] = new NamedBinaryTag(tagType, in.readUTF(), readPayload(in, tagType)); + } return tags; - case 11: - int len = dis.readInt(); - int[] ia = new int[len]; - for (int i = 0; i < len; i++) - ia[i] = dis.readInt(); - return ia; + case TAG_Int_Array: + int[] ints = new int[in.readInt()]; + for(int i = 0; i < ints.length; i++) + { + ints[i] = in.readInt(); + } + return ints; + case TAG_Long_Array: + long[] longs = new long[in.readInt()]; + for(int i = 0; i < longs.length; i++) + { + longs[i] = in.readLong(); + } + return longs; + default: + throw new IllegalArgumentException(); + } + } + public void writeTo(Path file) throws IOException + { + try(DataOutputStream out = new DataOutputStream(new BufferedOutputStream(CompressionUtils.newGZIPOutputStream(file)))) + { + writeTo(out); } - return null; } /** - * Read a tag and its nested tags from an InputStream. + * Write a tag and its nested tags to a {@link DataOutputStream} * - * @param os stream to write to, like a FileOutputStream + * @param out stream to write to * @throws IOException if this is not a valid NBT structure or if any * IOException occurred. */ - public void writeTo(OutputStream os) throws IOException + public void writeTo(DataOutputStream out) throws IOException { - GZIPOutputStream gzos; - DataOutputStream dos = new DataOutputStream(gzos = new GZIPOutputStream(os)); - dos.writeByte(type.ordinal()); - if (type != Type.TAG_End) + writeType(out, type); + if(type != Type.TAG_End) { - dos.writeUTF(name); - writePayload(dos); + out.writeUTF(""); + writePayload(out); } - gzos.flush(); - gzos.close(); } - private void writePayload(DataOutputStream dos) throws IOException + private void writeType(DataOutputStream out, Type type) throws IOException { - switch (type) + out.writeByte(type.ordinal()); + } + + private void writePayload(DataOutputStream out) throws IOException + { + switch(type) { case TAG_End: break; case TAG_Byte: - dos.writeByte((Byte) value); + out.writeByte((byte) value); break; case TAG_Short: - dos.writeShort((Short) value); + out.writeShort((short) value); break; case TAG_Int: - dos.writeInt((Integer) value); + out.writeInt((int) value); break; case TAG_Long: - dos.writeLong((Long) value); + out.writeLong((long) value); break; case TAG_Float: - dos.writeFloat((Float) value); + out.writeFloat((float) value); break; case TAG_Double: - dos.writeDouble((Double) value); + out.writeDouble((double) value); break; case TAG_Byte_Array: - byte[] ba = (byte[]) value; - dos.writeInt(ba.length); - dos.write(ba); + byte[] bytes = (byte[]) value; + out.writeInt(bytes.length); + out.write(bytes); break; case TAG_String: - dos.writeUTF((String) value); + out.writeUTF((String) value); break; case TAG_List: - NamedBinaryTag[] list = (NamedBinaryTag[]) value; - dos.writeByte(getListType().ordinal()); - dos.writeInt(list.length); - for (NamedBinaryTag tt : list) + NamedBinaryTag[] elements = (NamedBinaryTag[]) value; + writeType(out, listType); + out.writeInt(elements.length); + for(NamedBinaryTag element : elements) { - tt.writePayload(dos); + element.writePayload(out); } break; case TAG_Compound: - NamedBinaryTag[] subtags = (NamedBinaryTag[]) value; - for (NamedBinaryTag st : subtags) + for(NamedBinaryTag tag : (NamedBinaryTag[]) value) { - Type type = st.getType(); - dos.writeByte(type.ordinal()); - if (type != Type.TAG_End) + writeType(out, tag.type); + if(tag.type != Type.TAG_End) { - dos.writeUTF(st.getName()); - st.writePayload(dos); + out.writeUTF(tag.name); + tag.writePayload(out); } } break; case TAG_Int_Array: - int[] ia = (int[]) value; - dos.writeInt(ia.length); - for (int anIa : ia) + int[] ints = (int[]) value; + out.writeInt(ints.length); + for(int i : ints) { - dos.writeInt(anIa); + out.writeInt(i); } break; - + case TAG_Long_Array: + long[] longs = (long[]) value; + out.writeInt(longs.length); + for(long l : longs) + { + out.writeLong(l); + } + break; + default: + throw new IllegalStateException(); } } private String getTypeString(Type type) { - switch (type) - { - case TAG_End: - return "TAG_End"; - case TAG_Byte: - return "TAG_Byte"; - case TAG_Short: - return "TAG_Short"; - case TAG_Int: - return "TAG_Int"; - case TAG_Long: - return "TAG_Long"; - case TAG_Float: - return "TAG_Float"; - case TAG_Double: - return "TAG_Double"; - case TAG_Byte_Array: - return "TAG_Byte_Array"; - case TAG_String: - return "TAG_String"; - case TAG_List: - return "TAG_List"; - case TAG_Compound: - return "TAG_Compound"; - case TAG_Int_Array: - return "TAG_Int_Array"; - - } - return null; + return type.name(); } private void indent(int indent) diff --git a/common/src/main/java/com/pg85/otg/util/bo3/Rotation.java b/common/src/main/java/com/pg85/otg/util/bo3/Rotation.java index 125b322f8..89663253b 100644 --- a/common/src/main/java/com/pg85/otg/util/bo3/Rotation.java +++ b/common/src/main/java/com/pg85/otg/util/bo3/Rotation.java @@ -1,9 +1,10 @@ package com.pg85.otg.util.bo3; -import com.pg85.otg.exception.InvalidConfigException; - import java.util.Random; +import com.pg85.otg.exception.InvalidConfigException; +import com.pg85.otg.util.helpers.StringHelper; + /** * An enum to help with CustomObject rotation. * @@ -15,6 +16,7 @@ public enum Rotation SOUTH(2), EAST(3); + private static final Rotation[] VALUES = values(); private final int ROTATION_ID; private Rotation(int id) @@ -34,26 +36,16 @@ public int getRotationId() public static Rotation FromString(String rotation) { - if(rotation.toLowerCase().equals("north")) - { - return NORTH; - } - if(rotation.toLowerCase().equals("east")) - { - return EAST; - } - if(rotation.toLowerCase().equals("south")) - { - return SOUTH; - } - if(rotation.toLowerCase().equals("west")) - { - return WEST; - } else { - return WEST; // WEST is the default - } - } - + try + { + return Rotation.valueOf(rotation.toUpperCase()); + } + catch(IllegalArgumentException e) + { + return WEST; // WEST is the default + } + } + /** * Get the rotation with the given id. Returns null if the * rotation id isn't found. @@ -63,12 +55,9 @@ public static Rotation FromString(String rotation) */ public static Rotation getRotation(int id) { - for (Rotation rotation : values()) + if(id >= 0 && id < VALUES.length) { - if (rotation.ROTATION_ID == id) - { - return rotation; - } + return VALUES[id]; } return null; @@ -82,55 +71,47 @@ public static Rotation getRotation(int id) */ public static Rotation getRandomRotation(Random random) { - return values()[random.nextInt(values().length)]; + return VALUES[random.nextInt(VALUES.length)]; } /** * Returns the next rotation. NORTH -> WEST -> SOUTH -> EAST * @return The next rotation. */ - public Rotation next() + public Rotation next(Rotation rotation) { - int id = getRotationId(); - id++; - if (id >= values().length) - { - id = 0; - } - - return Rotation.getRotation(id); + return VALUES[(ROTATION_ID + rotation.ROTATION_ID) % VALUES.length]; } public static Rotation getRotation(String string) throws InvalidConfigException { - Rotation rotation = null; - // Try to parse it as a number - try - { - rotation = getRotation(Integer.parseInt(string)); - } catch (NumberFormatException e) - { - } - - if (rotation != null) - { - return rotation; - } - - // Try to parse it as a String - try - { - rotation = Rotation.valueOf(string.toUpperCase()); - } catch (IllegalArgumentException e) + if(StringHelper.isNumber(string)) { + int id; + try + { + id = Integer.parseInt(string); + } + catch(NumberFormatException e) + { + throw new InvalidConfigException("Unknown rotation \"" + string + "\"", e); + } + if(id < 0 || id >= VALUES.length) + { + throw new InvalidConfigException("Unknown rotation \"" + string + "\""); + } + return VALUES[id]; } - - if (rotation != null) + else { - return rotation; + try + { + return valueOf(string.toUpperCase()); + } + catch(IllegalArgumentException e) + { + throw new InvalidConfigException("Unknown rotation \"" + string + "\"", e); + } } - - // Failed - throw new InvalidConfigException("Unknown rotation: " + string); } } diff --git a/common/src/main/java/com/pg85/otg/util/helpers/BlockHelper.java b/common/src/main/java/com/pg85/otg/util/helpers/BlockHelper.java deleted file mode 100644 index 55a6ccb6f..000000000 --- a/common/src/main/java/com/pg85/otg/util/helpers/BlockHelper.java +++ /dev/null @@ -1,364 +0,0 @@ -package com.pg85.otg.util.helpers; - -import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; - -public class BlockHelper -{ - /** - * Rotate the block. North -> west - > south - > east - * - * @param mat Material of the block. - * @param data Block data. - * @return The rotated block data. - */ - public static int rotateData(DefaultMaterial mat, int data) - { - switch (mat) - { - case TORCH: - case REDSTONE_TORCH_OFF: - case REDSTONE_TORCH_ON: - switch (data) - { - case 3: - return 1; - case 4: - return 2; - case 2: - return 3; - case 1: - return 4; - } - break; - - case RAILS: - switch (data) - { - case 7: - return 6; - case 8: - return 7; - case 9: - return 8; - case 6: - return 9; - } - - case POWERED_RAIL: - case DETECTOR_RAIL: - case ACTIVATOR_RAIL: - int power = data & ~0x7; - switch (data & 0x7) - { - case 1: - return 0 | power; - case 0: - return 1 | power; - case 5: - return 2 | power; - case 4: - return 3 | power; - case 2: - return 4 | power; - case 3: - return 5 | power; - } - break; - - case LOG: - case LOG_2: - case HAY_BLOCK: - case BONE_BLOCK: - switch (data / 4) - { - case 1: - return data + 4; // East/West-->North/South horizontal log - case 2: - return data - 4; // North/South-->East/West horizontal log - // Default: vertical or all-bark log - } - break; - - case WOOD_STAIRS: - case BIRCH_WOOD_STAIRS: - case SPRUCE_WOOD_STAIRS: - case JUNGLE_WOOD_STAIRS: - case COBBLESTONE_STAIRS: - case BRICK_STAIRS: - case SMOOTH_STAIRS: - case NETHER_BRICK_STAIRS: - case SANDSTONE_STAIRS: - case QUARTZ_STAIRS: - case ACACIA_STAIRS: - case DARK_OAK_STAIRS: - case RED_SANDSTONE_STAIRS: - case PURPUR_STAIRS: - switch (data) - { - case 7: - return 5; - case 6: - return 4; - case 5: - return 6; - case 4: - return 7; - case 3: - return 1; - case 2: - return 0; - case 1: - return 2; - case 0: - return 3; - } - break; - - case LEVER: - case STONE_BUTTON: - case WOOD_BUTTON: - int thrown = data & 0x8; - int withoutThrown = data & ~0x8; - switch (withoutThrown) - { - case 3: - return 1 | thrown; - case 4: - return 2 | thrown; - case 2: - return 3 | thrown; - case 1: - return 4 | thrown; - } - break; - - case WOODEN_DOOR: - case IRON_DOOR_BLOCK: - case SPRUCE_DOOR: - case BIRCH_DOOR: - case JUNGLE_DOOR: - case ACACIA_DOOR: - case DARK_OAK_DOOR: - int topHalf = data & 0x8; - int swung = data & 0x4; - int withoutFlags = data & ~(0x8 | 0x4); - switch (withoutFlags) - { - case 1: - return 0 | topHalf | swung; - case 2: - return 1 | topHalf | swung; - case 3: - return 2 | topHalf | swung; - case 0: - return 3 | topHalf | swung; - } - break; - - case SIGN_POST: - case STANDING_BANNER: - return (data + 12) % 16; - - case WALL_BANNER: - case END_ROD: - case COMMAND: - case COMMAND_REPEATING: - case COMMAND_CHAIN: - case WHITE_SHULKER_BOX: - case ORANGE_SHULKER_BOX: - case MAGENTA_SHULKER_BOX: - case LIGHT_BLUE_SHULKER_BOX: - case YELLOW_SHULKER_BOX: - case LIME_SHULKER_BOX: - case PINK_SHULKER_BOX: - case GRAY_SHULKER_BOX: - case SILVER_SHULKER_BOX: - case CYAN_SHULKER_BOX: - case PURPLE_SHULKER_BOX: - case BLUE_SHULKER_BOX: - case BROWN_SHULKER_BOX: - case GREEN_SHULKER_BOX: - case RED_SHULKER_BOX: - case BLACK_SHULKER_BOX: - switch (data) - { - case 5: - return 2; - case 4: - return 3; - case 2: - return 4; - case 3: - return 5; - } - - case LADDER: - case WALL_SIGN: - case CHEST: - case ENDER_CHEST: - case TRAPPED_CHEST: - case FURNACE: - case BURNING_FURNACE: - case OBSERVER: - switch (data) - { - case 5: - return 2; - case 4: - return 3; - case 2: - return 4; - case 3: - return 5; - } - break; - - case DISPENSER: - case DROPPER: - case HOPPER: - int dispPower = data & 0x8; - switch (data & ~0x8) - { - case 5: - return 2 | dispPower; - case 4: - return 3 | dispPower; - case 2: - return 4 | dispPower; - case 3: - return 5 | dispPower; - } - break; - - case PUMPKIN: - case JACK_O_LANTERN: - switch (data) - { - case 1: - return 0; - case 2: - return 1; - case 3: - return 2; - case 0: - return 3; - } - break; - - case DIODE_BLOCK_OFF: - case DIODE_BLOCK_ON: - case REDSTONE_COMPARATOR_OFF: - case REDSTONE_COMPARATOR_ON: - case BED_BLOCK: - int dir = data & 0x03; - int withoutDir = data - dir; - switch (dir) - { - case 1: - return 0 | withoutDir; - case 2: - return 1 | withoutDir; - case 3: - return 2 | withoutDir; - case 0: - return 3 | withoutDir; - } - break; - - case TRAP_DOOR: - case IRON_TRAPDOOR: - int withoutOrientation = data & ~0x3; - int orientation = data & 0x3; - switch (orientation) - { - case 3: - return 0 | withoutOrientation; - case 2: - return 1 | withoutOrientation; - case 0: - return 2 | withoutOrientation; - case 1: - return 3 | withoutOrientation; - } - - case PISTON_BASE: - case PISTON_STICKY_BASE: - case PISTON_EXTENSION: - final int rest = data & ~0x7; - switch (data & 0x7) - { - case 5: - return 2 | rest; - case 4: - return 3 | rest; - case 2: - return 4 | rest; - case 3: - return 5 | rest; - } - break; - - case HUGE_MUSHROOM_1: - case HUGE_MUSHROOM_2: - if (data >= 10) - return data; - return (data * 7) % 10; - - case VINE: - return ((data >> 1) | (data << 3)) & 0xf; - - case FENCE_GATE: - case SPRUCE_FENCE_GATE: - case BIRCH_FENCE_GATE: - case JUNGLE_FENCE_GATE: - case DARK_OAK_FENCE_GATE: - case ACACIA_FENCE_GATE: - return ((data + 3) & 0x3) | (data & ~0x3); - - case COCOA: - case TRIPWIRE_HOOK: - int rotationData = data % 4; - if (rotationData == 0) - { - return data + 3; - } else - { - return data - 1; - } - - case ANVIL: - if (data % 2 == 0) - { - // North-south --> west-east - return data + 1; - } else - { - // West-east --> north-south - return data - 1; - } - - case QUARTZ_BLOCK: - if (data == 3) - return 4; - if (data == 4) - return 3; - break; - - case PURPUR_PILLAR: - if (data == 4) - return 8; - if (data == 8) - return 4; - break; - default: - break; - } - - return data; - } - - private BlockHelper() - { - } - -} diff --git a/common/src/main/java/com/pg85/otg/util/helpers/MathHelper.java b/common/src/main/java/com/pg85/otg/util/helpers/MathHelper.java index 25a11ff8a..027e0ad72 100644 --- a/common/src/main/java/com/pg85/otg/util/helpers/MathHelper.java +++ b/common/src/main/java/com/pg85/otg/util/helpers/MathHelper.java @@ -30,6 +30,12 @@ public static int floor(double d0) return d0 < i ? i - 1 : i; } + public static int ceil(double floatNumber) + { + int truncated = (int) floatNumber; + return floatNumber > truncated ? truncated + 1 : truncated; + } + public static long floor_double_long(double d) { long l = (long) d; @@ -53,6 +59,13 @@ public static int abs(int number) A[i] = (float) Math.sin(i * 3.141592653589793D * 2.0D / 65536.0D); } + public static int floor(float d0) + { + int i = (int) d0; + + return d0 < i ? i - 1 : i; + } + public static int ceil(float floatNumber) { int truncated = (int) floatNumber; @@ -64,18 +77,22 @@ public static int clamp(int check, int min, int max) return check > max ? max : (check < min ? min : check); } - /* - * Modulus, rather than java's modulo (%) - * which does a remainder operation. - */ - public static int mod(int x, int y) + public static int floorDiv(int x, int y) { - int result = x % y; - if (result < 0) - { - result += y; - } - return result; + int i = x / y; + return x < 0 != y < 0 && x != y * i ? i - 1 : i; + } + + public static int floorMod(int x, int y) + { + return x - floorDiv(x, y) * y; + } + + public static int transformMirror(int x, int y) + { + int i = floorDiv(x, y); + int r = x - i * y; + return (i & 1) == 1 ? y - r : r; } public static boolean tryParseInt(String value) diff --git a/common/src/main/java/com/pg85/otg/util/helpers/StreamHelper.java b/common/src/main/java/com/pg85/otg/util/helpers/StreamHelper.java index 04a1738b2..e82386d15 100644 --- a/common/src/main/java/com/pg85/otg/util/helpers/StreamHelper.java +++ b/common/src/main/java/com/pg85/otg/util/helpers/StreamHelper.java @@ -1,65 +1,32 @@ package com.pg85.otg.util.helpers; -import java.io.DataInputStream; +import java.io.DataInput; import java.io.DataOutput; -import java.io.EOFException; import java.io.IOException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; public class StreamHelper { + public static void writeStringToStream(DataOutput out, String value) throws IOException + { + boolean isNull; + out.writeBoolean(isNull = value == null); + if(!isNull) + { + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + out.writeShort(bytes.length); + out.write(bytes); + } + } - public static void writeStringToStream(DataOutput stream, String value) throws IOException - { - stream.writeBoolean(value == null); - if(value != null) - { - byte[] bytes = (value == null ? "" : value).getBytes(StandardCharsets.UTF_8); - stream.writeShort(bytes.length); - stream.write(bytes); - } - } - - public static String readStringFromStream(DataInputStream stream) throws IOException - { - boolean isNull = stream.readBoolean(); - if(isNull) - { - return null; - } - - short length = stream.readShort(); - byte[] chars = new byte[length]; - if(length > 0) - { - if (stream.read(chars, 0, chars.length) != chars.length) - { - throw new EOFException(); - } - return new String(chars); - } else { - return ""; - } - } - - public static String readStringFromBuffer(ByteBuffer buffer) throws IOException, BufferUnderflowException - { - boolean isNull = buffer.get() != 0; - if(isNull) - { - return null; - } - - short length = buffer.getShort(); - byte[] chars = new byte[length]; - if(length > 0) - { - buffer.get(chars, 0, chars.length); - return new String(chars); - } else { - return ""; - } - } + public static String readStringFromStream(DataInput in) throws IOException + { + if(in.readBoolean()) + { + return null; + } + byte[] bytes = new byte[in.readShort()]; + in.readFully(bytes); + return new String(bytes, StandardCharsets.UTF_8); + } } diff --git a/common/src/main/java/com/pg85/otg/util/helpers/StringHelper.java b/common/src/main/java/com/pg85/otg/util/helpers/StringHelper.java index 9c96661db..0096b22c5 100644 --- a/common/src/main/java/com/pg85/otg/util/helpers/StringHelper.java +++ b/common/src/main/java/com/pg85/otg/util/helpers/StringHelper.java @@ -1,35 +1,28 @@ package com.pg85.otg.util.helpers; -import com.pg85.otg.exception.InvalidConfigException; - +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.LinkedList; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; + +import com.pg85.otg.exception.InvalidConfigException; /** * Some methods for string parsing and printing. - * */ public abstract class StringHelper { public static String join(final Collection coll, final String glue) { - return join(coll.toArray(new Object[coll.size()]), glue); + return coll.stream().map(Object::toString).collect(Collectors.joining(glue)); } public static String join(final Object[] list, final String glue) { - StringBuilder ret = new StringBuilder(100); - for (int i = 0; i < list.length; i++) - { - if (i != 0) - { - ret.append(glue); - } - ret.append(list[i]); - } - return ret.toString(); - } + return Arrays.stream(list).map(Object::toString).collect(Collectors.joining(glue)); + } /** * Turns the given name into a name suitable for computers, so without @@ -40,11 +33,13 @@ public static String join(final Object[] list, final String glue) public static String toComputerFriendlyName(String name) { char[] charArray = name.toCharArray(); - for (int i = 0; i < charArray.length; i++) { - if (!Character.isJavaIdentifierPart(charArray[i])) + for(int i = 0; i < charArray.length; i++) + { + if(!Character.isJavaIdentifierPart(charArray[i])) { charArray[i] = '_'; - } else + } + else { charArray[i] = Character.toLowerCase(charArray[i]); } @@ -70,18 +65,76 @@ public static int readInt(String string, int minValue, int maxValue) throws Inva try { int number = Integer.parseInt(string); - if (number < minValue) + if(number < minValue) { return minValue; } - if (number > maxValue) + if(number > maxValue) { return maxValue; } return number; - } catch (NumberFormatException e) + } + catch(NumberFormatException e) { - throw new InvalidConfigException("Incorrect number: " + string); + throw new InvalidConfigException("Incorrect number: " + string, e); + } + } + + public static boolean isNumber(String s) + { + boolean digitFound = false; + boolean dotFound = false; + for(int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + if(i == 0 && c == '-') + continue; + if(c == '.') + { + if(dotFound) + return false; + dotFound = true; + continue; + } + if(!Character.isDigit(c)) + return false; + digitFound = true; + } + return digitFound; + } + + /** + * Parses the string and returns a number between minValue and maxValue. + * + * @param string + * The string to parse. + * @param minValue + * The minimum value, inclusive. + * @param maxValue + * The maximum value, inclusive. + * @return The number in the String, capped at the minValue and maxValue. + * @throws InvalidConfigException + * If the number is invalid. + */ + public static long readLong(String string, long minValue, long maxValue) throws InvalidConfigException + { + try + { + long number = Long.parseLong(string); + if(number < minValue) + { + return minValue; + } + if(number > maxValue) + { + return maxValue; + } + return number; + } + catch(NumberFormatException e) + { + throw new InvalidConfigException("Incorrect number: " + string, e); } } @@ -103,18 +156,19 @@ public static double readDouble(String string, double minValue, double maxValue) try { double number = Double.parseDouble(string); - if (number < minValue) + if(number < minValue) { return minValue; } - if (number > maxValue) + if(number > maxValue) { return maxValue; } return number; - } catch (NumberFormatException e) + } + catch(NumberFormatException e) { - throw new InvalidConfigException("Incorrect number: " + string); + throw new InvalidConfigException("Incorrect number: " + string, e); } } @@ -138,107 +192,50 @@ public static double readDouble(String string, double minValue, double maxValue) * The line to parse. * @return The parts of the string. */ - public static String[] readCommaSeperatedString(String line) + public static List readCommaSeperatedString(String line) { - if (line.trim().isEmpty()) + line = line.trim(); + if(line.isEmpty()) { - // Empty lines have no elements, not one empty element - return new String[0]; + return Collections.emptyList(); } - List buffer = new LinkedList(); - - int index = 0; - int lastFound = 0; - int inBracer = 0; - - for (char c : line.toCharArray()) + int i = indexOf(line, ',', 0); + if(i < 0) { - if (c == ',' && inBracer == 0) - { - buffer.add(line.substring(lastFound, index).trim()); - lastFound = index + 1; - } - - if (c == '(') - inBracer++; - if (c == ')') - inBracer--; - - index++; + return Collections.singletonList(line); } - buffer.add(line.substring(lastFound, index).trim()); - - String[] output = new String[0]; - - if (inBracer == 0) - output = buffer.toArray(output); - return output; - } - - /** - * Gets whether the input specifies which block data should be used. - *

- * A few examples: "WOOL" doesn't specify block data, while "WOOL:0" does. - * "buildcraft:blockRedLaser" doesn't specify block data, even though it - * contains a colon. However, "buildcraft:blockRedLaser:0" does specify - * block data. - * - * @param materialString - * The input. - * @return True if the input specifies block data, false otherwise. - */ - public static boolean specifiesBlockData(String materialString) { - int indexOfColon = materialString.lastIndexOf(":"); - if (indexOfColon > 0) + List list = new ArrayList<>(); + int j = 0; + while(i >= 0) { - String blockDataString = materialString.substring(indexOfColon + 1); - try { - Integer.parseInt(blockDataString); - // If we have reached this point, the text after the last colon - // was numeric, so it was indeed block data - return true; - } catch (NumberFormatException e) { - } + list.add(line.substring(j, i).trim()); + j = i + 1; + i = indexOf(line, ',', i + 1); } - return false; + list.add(line.substring(j, line.length()).trim()); + return list; } - private StringHelper() + private static int indexOf(String s, char c, int start) { - } - - /** - * Parses the string and returns a number between minValue and maxValue. - * - * @param string - * The string to parse. - * @param minValue - * The minimum value, inclusive. - * @param maxValue - * The maximum value, inclusive. - * @return The number in the String, capped at the minValue and maxValue. - * @throws InvalidConfigException - * If the number is invalid. - */ - public static long readLong(String string, long minValue, long maxValue) throws InvalidConfigException - { - try + for(int i = start; i < s.length(); i++) { - long number = Long.parseLong(string); - if (number < minValue) + char c1 = s.charAt(i); + if(c1 == c) { - return minValue; + return i; } - if (number > maxValue) + if(c1 == '(') { - return maxValue; + i = indexOf(s, ')', i + 1); + if(i < 0) + { + return -1; + } } - return number; - } catch (NumberFormatException e) - { - throw new InvalidConfigException("Incorrect number: " + string); } + return -1; } } diff --git a/common/src/main/java/com/pg85/otg/util/materials/MaterialHelper.java b/common/src/main/java/com/pg85/otg/util/materials/MaterialHelper.java index 79c118b7f..ed4ae23b5 100644 --- a/common/src/main/java/com/pg85/otg/util/materials/MaterialHelper.java +++ b/common/src/main/java/com/pg85/otg/util/materials/MaterialHelper.java @@ -4,14 +4,14 @@ import com.pg85.otg.OTGEngine; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.exception.InvalidConfigException; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; //TODO: Clean up and optimise ForgeMaterialData/BukkitMaterialData/LocalMaterialData/MaterialHelper/OTGEngine.readMaterial public class MaterialHelper { // Materials are stored in a cache for fast parsing from config (txt) files, each material should exist only once. - private static FifoMap CachedMaterials = new FifoMap(4096); // TODO: This is probably too large, makes lookups slow. + private static LRUCache CachedMaterials = new LRUCache(4096); // TODO: This is probably too large, makes lookups slow. public static final LocalMaterialData AIR = MaterialHelper.toLocalMaterialData(DefaultMaterial.AIR, 0); public static final LocalMaterialData SANDSTONE = MaterialHelper.toLocalMaterialData(DefaultMaterial.SANDSTONE, 0); diff --git a/common/src/main/java/com/pg85/otg/util/materials/MaterialSet.java b/common/src/main/java/com/pg85/otg/util/materials/MaterialSet.java index 4ab6a8cb3..a887120db 100644 --- a/common/src/main/java/com/pg85/otg/util/materials/MaterialSet.java +++ b/common/src/main/java/com/pg85/otg/util/materials/MaterialSet.java @@ -1,15 +1,25 @@ package com.pg85.otg.util.materials; -import com.pg85.otg.OTG; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.pg85.otg.OTGEngine; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; import com.pg85.otg.exception.InvalidConfigException; +import com.pg85.otg.util.bo3.Rotation; import com.pg85.otg.util.helpers.StringHelper; -import com.pg85.otg.util.materials.MaterialSetEntry; +import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; -import java.util.Arrays; -import java.util.LinkedHashSet; -import java.util.Set; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.Reference2IntMap; +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; /** * A material set that accepts special values such as "All" or "Solid". These @@ -19,6 +29,70 @@ */ public class MaterialSet { + enum Mode + { + ALL + { + @Override + boolean contains(LocalMaterialData material) + { + return true; + } + + @Override + boolean contains(MaterialSet set, LocalMaterialData material) + { + return true; + } + }, + SOLID + { + @Override + boolean contains(LocalMaterialData material) + { + return material.isSolid(); + } + + @Override + boolean contains(MaterialSet set, LocalMaterialData material) + { + return material.isSolid() || DEFAULT.contains(set, material); + } + }, + NON_SOLID + { + @Override + boolean contains(LocalMaterialData material) + { + return !material.isSolid(); + } + + @Override + boolean contains(MaterialSet set, LocalMaterialData material) + { + return !material.isSolid() || DEFAULT.contains(set, material); + } + }, + DEFAULT + { + @Override + boolean contains(LocalMaterialData material) + { + return false; + } + + @Override + boolean contains(MaterialSet set, LocalMaterialData material) + { + return (set.map.getInt(material.withoutBlockData()) & (1 << material.getBlockData())) != 0; + } + }; + + abstract boolean contains(LocalMaterialData material); + + abstract boolean contains(MaterialSet set, LocalMaterialData material); + } + /** * Keyword that adds all materials to the set when used in * {@link #parseAndAdd(String)}. @@ -37,14 +111,61 @@ public class MaterialSet */ private static final String NON_SOLID_MATERIALS = "NonSolid"; - private boolean allMaterials = false; - private boolean allSolidMaterials = false; - private boolean allNonSolidMaterials = false; + private static final Object2ObjectMap, MaterialSet> INSTANCES = new Object2ObjectOpenHashMap<>(); + private Mode mode = Mode.DEFAULT; + private final Set materials = new LinkedHashSet<>(); + private final Reference2IntMap map = new Reference2IntOpenHashMap<>(); - private int[] materialIntSet = new int[0]; - public Set materials = new LinkedHashSet(); - private boolean intSetUpToDate = true; - private boolean parsed = false; + private MaterialSet() + { + + } + + public static MaterialSet create(DefaultMaterial... materials) + { + if(materials.length == 0) + { + return _create(Collections.emptyList()); + } + return _create(Arrays.stream(materials).map(DefaultMaterial::toString).collect(Collectors.toList())); + } + + public static MaterialSet create(String... materials) + { + if(materials.length == 0) + { + return _create(Collections.emptyList()); + } + return _create(Arrays.asList(materials.clone())); + } + + public static MaterialSet create(List materials) + { + if(materials.isEmpty()) + { + return _create(Collections.emptyList()); + } + return _create(Arrays.asList(materials.toArray(new String[materials.size()]))); + } + + private static MaterialSet _create(List materials) + { + return INSTANCES.computeIfAbsent(materials, k -> { + MaterialSet v = new MaterialSet(); + try + { + for(String material : k) + { + v.parseAndAdd(material); + } + } + catch(InvalidConfigException e) + { + throw new RuntimeException(e); + } + return v; + }); + } /** * Adds the given material to the list. @@ -52,7 +173,7 @@ public class MaterialSet *

If the material is "All", all * materials in existence are added to the list. If the material is * "Solid", all solid materials are added to the list. Otherwise, - * {@link OTG#readMaterial(String)} is used to read the + * {@link OTGEngine#readMaterial(String)} is used to read the * material. * *

If the material {@link StringHelper#specifiesBlockData(String) @@ -63,132 +184,121 @@ public class MaterialSet * @param input The name of the material to add. * @throws InvalidConfigException If the name is invalid. */ - public void parseAndAdd(String input) throws InvalidConfigException + private void parseAndAdd(String input) throws InvalidConfigException { - if (input.equalsIgnoreCase(ALL_MATERIALS)) + if(this.mode == Mode.ALL) { - this.allMaterials = true; return; } - if (input.equalsIgnoreCase(SOLID_MATERIALS)) + if(input.equalsIgnoreCase(ALL_MATERIALS) || this.mode == Mode.SOLID && input.equalsIgnoreCase(SOLID_MATERIALS) || this.mode == Mode.NON_SOLID && input.equalsIgnoreCase(NON_SOLID_MATERIALS)) { - this.allSolidMaterials = true; + this.mode = Mode.ALL; + this.materials.clear(); + this.map.clear(); return; } - if (input.equalsIgnoreCase(NON_SOLID_MATERIALS)) + if(input.equalsIgnoreCase(SOLID_MATERIALS) && this.mode != Mode.SOLID) { - this.allNonSolidMaterials = true; + this.mode = Mode.SOLID; + if(this.materials.removeIf(this.mode::contains)) + { + this.recomputeMap(); + } + return; + } + if(input.equalsIgnoreCase(NON_SOLID_MATERIALS) && this.mode != Mode.NON_SOLID) + { + this.mode = Mode.NON_SOLID; + if(this.materials.removeIf(this.mode::contains)) + { + this.recomputeMap(); + } return; } LocalMaterialData material = MaterialHelper.readMaterial(input); - - boolean checkIncludesBlockData = StringHelper.specifiesBlockData(input); - - if(material == null) + if(!this.mode.contains(material)) { - throw new InvalidConfigException("Invalid block check, material \"" + input + "\" could not be found."); + if(this.materials.add(material)) + { + this.computeMapEntry(material); + } + } + } + + private void recomputeMap() + { + this.map.clear(); + this.materials.forEach(this::computeMapEntry); + } + + private void computeMapEntry(LocalMaterialData material) + { + if(material == null || material.isEmpty()) + { + return; + } + LocalMaterialData k = material.withoutBlockData(); + int v = this.map.getInt(k); + if((v & material.getBlockDataMask()) != material.getBlockDataMask()) + { + this.map.put(k, v | material.getBlockDataMask()); } - - // Add to set - add(new MaterialSetEntry(material, checkIncludesBlockData)); } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (allMaterials ? 1231 : 1237); - result = prime * result + (allNonSolidMaterials ? 1231 : 1237); - result = prime * result + (allSolidMaterials ? 1231 : 1237); - result = prime * result + materials.hashCode(); - return result; + int hash = 1; + hash = 31 * hash + this.mode.hashCode(); + hash = 31 * hash + this.materials.hashCode(); + return hash; } @Override public boolean equals(Object obj) { - if (this == obj) + if(obj == this) { return true; } - if (obj == null) - { - return false; - } - if (getClass() != obj.getClass()) + if(!(obj instanceof MaterialSet)) { return false; } MaterialSet other = (MaterialSet) obj; - if (allMaterials != other.allMaterials) + if(this.mode != other.mode) { return false; } - if (allNonSolidMaterials != other.allNonSolidMaterials) - { - return false; - } - if (allSolidMaterials != other.allSolidMaterials) - { - return false; - } - if (!materials.equals(other.materials)) + if(!this.materials.equals(other.materials)) { return false; } return true; } - /** - * Adds the entry to this material set. - * - * @param entry The entry to add, may not be null. - */ - private void add(MaterialSetEntry entry) - { - // Add the appropriate hashCode - intSetUpToDate = false; - materials.add(entry); - } - + private boolean parsedFallBacks; + public void parseForWorld(LocalWorld world) { - if (!parsed) + if(!this.parsedFallBacks) { - for (MaterialSetEntry material : materials) + this.parsedFallBacks = true; + for(LocalMaterialData material : this.materials) { - material.parseForWorld(world); + LocalMaterialData replacement = material.parseForWorld(world); + if(replacement != material) + { + this.materials.remove(material); + this.map.remove(material.withoutBlockData()); + if(this.materials.add(replacement)) + { + this.computeMapEntry(replacement); + } + } } - parsed = true; - intSetUpToDate = false; - } - } - - /** - * Updates the int (hashCode) set, so that is is up to date again with the - * material set. - */ - private void updateIntSet() - { - if (intSetUpToDate) - { - // Already up to date - return; - } - - // Update the int set - materialIntSet = new int[materials.size()]; - int i = 0; - for (MaterialSetEntry entry : materials) - { - materialIntSet[i] = entry.hashCode(); - i++; } - // Sort int set so that we can use Arrays.binarySearch - Arrays.sort(materialIntSet); - intSetUpToDate = true; } /** @@ -200,37 +310,11 @@ private void updateIntSet() */ public boolean contains(LocalMaterialData material) { - if (material == null || material.isEmpty()) + if(material == null || material.isEmpty()) { return false; } - if (allMaterials) - { - return true; - } - if (allSolidMaterials && material.isSolid()) - { - return true; - } - if (allNonSolidMaterials && !material.isSolid()) - { - return true; - } - - // Try to update int set - updateIntSet(); - - // Check if the material is included - // If SAND is in the list, both SAND:0 and SAND:1 return true. - if (Arrays.binarySearch(materialIntSet, material.hashCodeWithoutBlockData()) >= 0) - { - return true; - } - if (Arrays.binarySearch(materialIntSet, material.hashCode()) >= 0) - { - return true; - } - return false; + return this.mode.contains(this, material); } /** @@ -243,36 +327,22 @@ public boolean contains(LocalMaterialData material) @Override public String toString() { - // Check if all materials are included - if (allMaterials) + Stream prefix; + switch(this.mode) { - return ALL_MATERIALS; + case ALL: + return SOLID_MATERIALS; + case SOLID: + prefix = Stream.of(SOLID_MATERIALS); + break; + case NON_SOLID: + prefix = Stream.of(NON_SOLID_MATERIALS); + break; + default: + prefix = Stream.empty(); + break; } - - StringBuilder builder = new StringBuilder(); - // Check for solid materials - if (allSolidMaterials) - { - builder.append(SOLID_MATERIALS).append(','); - } - // Check for non-solid materials - if (allNonSolidMaterials) - { - builder.append(NON_SOLID_MATERIALS).append(','); - } - // Add all other materials - for (MaterialSetEntry material : materials) - { - builder.append(material.toString()).append(','); - } - - // Remove last ',' - if (builder.length() > 0) - { - builder.deleteCharAt(builder.length() - 1); - } - - return builder.toString(); + return Stream.concat(prefix, this.materials.stream().map(Object::toString)).collect(Collectors.joining(",")); } /** @@ -280,26 +350,35 @@ public String toString() * * @return The new material set. */ - public MaterialSet rotate() + public MaterialSet rotate(Rotation rotation) { - MaterialSet rotated = new MaterialSet(); - if (this.allMaterials) - { - rotated.allMaterials = true; - } - if (this.allSolidMaterials) - { - rotated.allSolidMaterials = true; - } - if (this.allNonSolidMaterials) - { - rotated.allNonSolidMaterials = true; - } - rotated.intSetUpToDate = false; - for (MaterialSetEntry material : this.materials) + switch(rotation) { - rotated.materials.add(material.rotate()); + case NORTH: + return this; + case WEST: + case SOUTH: + case EAST: + MaterialSet rotatedSet = new MaterialSet(); + rotatedSet.mode = this.mode; + boolean rotatedEqualsThis = true; + for(LocalMaterialData material : this.materials) + { + LocalMaterialData rotatedMaterial = material.rotate(rotation); + rotatedSet.materials.add(rotatedMaterial); + if(rotatedMaterial != material) + { + rotatedEqualsThis = true; + } + } + if(rotatedEqualsThis) + { + return this; + } + rotatedSet.recomputeMap(); + return rotatedSet; + default: + throw new IllegalArgumentException(); } - return rotated; } } diff --git a/common/src/main/java/com/pg85/otg/util/materials/MaterialSetEntry.java b/common/src/main/java/com/pg85/otg/util/materials/MaterialSetEntry.java deleted file mode 100644 index 049bd7933..000000000 --- a/common/src/main/java/com/pg85/otg/util/materials/MaterialSetEntry.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.pg85.otg.util.materials; - -import com.pg85.otg.common.LocalMaterialData; -import com.pg85.otg.common.LocalWorld; - -public class MaterialSetEntry -{ - public LocalMaterialData material; - private final boolean includesBlockData; - - MaterialSetEntry(LocalMaterialData material, boolean includesBlockData) - { - this.material = material; - this.includesBlockData = includesBlockData; - } - - @Override - public boolean equals(Object other) - { - // Uses hashCode, as it is guaranteed to be unique for this class - if (other instanceof MaterialSetEntry) - { - return other.hashCode() == hashCode(); - } - return false; - } - - /** - * Gets the hashCode of this entry, which is equal to either - * {@link LocalMaterialData#hashCode()} or - * {@link LocalMaterialData#hashCodeWithoutBlockData()}. This means that - * the hashCode is unique. - * - * @return The unique hashCode. - */ - @Override - public int hashCode() - { - if (includesBlockData) - { - return material.hashCode(); - } else - { - return material.hashCodeWithoutBlockData(); - } - } - - public void parseForWorld(LocalWorld world) - { - material.parseForWorld(world); - } - - @Override - public String toString() - { - return material.toString(); - } - - /** - * Rotates this check 90 degrees. If block data was ignored in this check, - * it will still be ignored, otherwise the block data will be rotated too. - * - * @return The rotated check. - */ - MaterialSetEntry rotate() - { - if (!includesBlockData) - { - // Don't rotate block data - return new MaterialSetEntry(material, false); - } else - { - // Actually rotate block data, to maintain check correctness - return new MaterialSetEntry(material.rotate(), true); - } - } -} diff --git a/common/src/main/java/com/pg85/otg/util/minecraft/defaults/DefaultMaterial.java b/common/src/main/java/com/pg85/otg/util/minecraft/defaults/DefaultMaterial.java index 96bc3cdd5..874a369bf 100644 --- a/common/src/main/java/com/pg85/otg/util/minecraft/defaults/DefaultMaterial.java +++ b/common/src/main/java/com/pg85/otg/util/minecraft/defaults/DefaultMaterial.java @@ -1,10 +1,11 @@ package com.pg85.otg.util.minecraft.defaults; import com.pg85.otg.common.LocalMaterialData; -import com.pg85.otg.common.LocalWorld; +import java.util.Arrays; import java.util.Map; -import java.util.TreeMap; +import java.util.function.Function; +import java.util.stream.Collectors; /** * Enum of the materials the server will at least support. @@ -13,25 +14,25 @@ */ public enum DefaultMaterial { - AIR(0, false), + AIR(0), STONE(1), GRASS(2), DIRT(3), COBBLESTONE(4), WOOD(5), - SAPLING(6, false), + SAPLING(6), BEDROCK(7), - WATER(8, false), - STATIONARY_WATER(9, false), - LAVA(10, false), - STATIONARY_LAVA(11, false), + WATER(8), + STATIONARY_WATER(9), + LAVA(10), + STATIONARY_LAVA(11), SAND(12), GRAVEL(13), GOLD_ORE(14), IRON_ORE(15), COAL_ORE(16), LOG(17), - LEAVES(18, false), + LEAVES(18), SPONGE(19), GLASS(20), LAPIS_ORE(21), @@ -39,190 +40,190 @@ public enum DefaultMaterial DISPENSER(23), SANDSTONE(24), NOTE_BLOCK(25), - BED_BLOCK(26, false), - POWERED_RAIL(27, false), - DETECTOR_RAIL(28, false), - PISTON_STICKY_BASE(29, false), - WEB(30, false), - LONG_GRASS(31, false), - DEAD_BUSH(32, false), + BED_BLOCK(26), + POWERED_RAIL(27), + DETECTOR_RAIL(28), + PISTON_STICKY_BASE(29), + WEB(30), + LONG_GRASS(31), + DEAD_BUSH(32), PISTON_BASE(33), - PISTON_EXTENSION(34, false), + PISTON_EXTENSION(34), WOOL(35), - PISTON_MOVING_PIECE(36, false), - YELLOW_FLOWER(37, false), - RED_ROSE(38, false), - BROWN_MUSHROOM(39, false), - RED_MUSHROOM(40, false), + PISTON_MOVING_PIECE(36), + YELLOW_FLOWER(37), + RED_ROSE(38), + BROWN_MUSHROOM(39), + RED_MUSHROOM(40), GOLD_BLOCK(41), IRON_BLOCK(42), DOUBLE_STEP(43), - STEP(44, false), // stone_slab + STEP(44), // stone_slab BRICK(45), TNT(46), BOOKSHELF(47), MOSSY_COBBLESTONE(48), OBSIDIAN(49), - TORCH(50, false), - FIRE(51, false), + TORCH(50), + FIRE(51), MOB_SPAWNER(52), WOOD_STAIRS(53), - CHEST(54, false), - REDSTONE_WIRE(55, false), + CHEST(54), + REDSTONE_WIRE(55), DIAMOND_ORE(56), DIAMOND_BLOCK(57), WORKBENCH(58), - CROPS(59, false), + CROPS(59), SOIL(60), FURNACE(61), BURNING_FURNACE(62), - SIGN_POST(63, false), - WOODEN_DOOR(64, false), - LADDER(65, false), - RAILS(66, false), + SIGN_POST(63), + WOODEN_DOOR(64), + LADDER(65), + RAILS(66), COBBLESTONE_STAIRS(67), - WALL_SIGN(68, false), - LEVER(69, false), - STONE_PLATE(70, false), - IRON_DOOR_BLOCK(71, false), - WOOD_PLATE(72, false), + WALL_SIGN(68), + LEVER(69), + STONE_PLATE(70), + IRON_DOOR_BLOCK(71), + WOOD_PLATE(72), REDSTONE_ORE(73), GLOWING_REDSTONE_ORE(74), - REDSTONE_TORCH_OFF(75, false), - REDSTONE_TORCH_ON(76, false), - STONE_BUTTON(77, false), - SNOW(78, false), + REDSTONE_TORCH_OFF(75), + REDSTONE_TORCH_ON(76), + STONE_BUTTON(77), + SNOW(78), ICE(79), SNOW_BLOCK(80), CACTUS(81), CLAY(82), SUGAR_CANE_BLOCK(83), - JUKEBOX(84, false), - FENCE(85, false), + JUKEBOX(84), + FENCE(85), PUMPKIN(86), NETHERRACK(87), SOUL_SAND(88), GLOWSTONE(89), - PORTAL(90, false), + PORTAL(90), JACK_O_LANTERN(91), CAKE_BLOCK(92), - DIODE_BLOCK_OFF(93, false), - DIODE_BLOCK_ON(94, false), - STAINED_GLASS(95, false), - TRAP_DOOR(96, false), + DIODE_BLOCK_OFF(93), + DIODE_BLOCK_ON(94), + STAINED_GLASS(95), + TRAP_DOOR(96), MONSTER_EGGS(97), SMOOTH_BRICK(98), HUGE_MUSHROOM_1(99), HUGE_MUSHROOM_2(100), - IRON_FENCE(101, false), - THIN_GLASS(102, false), + IRON_FENCE(101), + THIN_GLASS(102), MELON_BLOCK(103), - PUMPKIN_STEM(104, false), - MELON_STEM(105, false), - VINE(106, false), - FENCE_GATE(107, false), + PUMPKIN_STEM(104), + MELON_STEM(105), + VINE(106), + FENCE_GATE(107), BRICK_STAIRS(108), SMOOTH_STAIRS(109), - MYCEL(110, true), - WATER_LILY(111, false), + MYCEL(110), + WATER_LILY(111), NETHER_BRICK(112), - NETHER_FENCE(113, false), + NETHER_FENCE(113), NETHER_BRICK_STAIRS(114), - NETHER_WARTS(115, false), - ENCHANTMENT_TABLE(116, false), - BREWING_STAND(117, false), + NETHER_WARTS(115), + ENCHANTMENT_TABLE(116), + BREWING_STAND(117), CAULDRON(118), - ENDER_PORTAL(119, false), - ENDER_PORTAL_FRAME(120, false), + ENDER_PORTAL(119), + ENDER_PORTAL_FRAME(120), ENDER_STONE(121), DRAGON_EGG(122), REDSTONE_LAMP_OFF(123), REDSTONE_LAMP_ON(124), WOOD_DOUBLE_STEP(125), WOOD_STEP(126), - COCOA(127, false), + COCOA(127), SANDSTONE_STAIRS(128), EMERALD_ORE(129), ENDER_CHEST(130), - TRIPWIRE_HOOK(131, false), - TRIPWIRE(132, false), + TRIPWIRE_HOOK(131), + TRIPWIRE(132), EMERALD_BLOCK(133), SPRUCE_WOOD_STAIRS(134), BIRCH_WOOD_STAIRS(135), JUNGLE_WOOD_STAIRS(136), COMMAND(137), BEACON(138), - COBBLE_WALL(139, false), - FLOWER_POT(140, false), - CARROT(141, false), - POTATO(142, false), - WOOD_BUTTON(143, false), - SKULL(144, false), - ANVIL(145, false), + COBBLE_WALL(139), + FLOWER_POT(140), + CARROT(141), + POTATO(142), + WOOD_BUTTON(143), + SKULL(144), + ANVIL(145), TRAPPED_CHEST(146), - GOLD_PLATE(147, false), - IRON_PLATE(148, false), - REDSTONE_COMPARATOR_OFF(149, false), - REDSTONE_COMPARATOR_ON(150, false), - DAYLIGHT_DETECTOR(151, false), + GOLD_PLATE(147), + IRON_PLATE(148), + REDSTONE_COMPARATOR_OFF(149), + REDSTONE_COMPARATOR_ON(150), + DAYLIGHT_DETECTOR(151), REDSTONE_BLOCK(152), QUARTZ_ORE(153), HOPPER(154), QUARTZ_BLOCK(155), QUARTZ_STAIRS(156), - ACTIVATOR_RAIL(157, false), + ACTIVATOR_RAIL(157), DROPPER(158), STAINED_CLAY(159), - STAINED_GLASS_PANE(160, false), - LEAVES_2(161, false), + STAINED_GLASS_PANE(160), + LEAVES_2(161), LOG_2(162), ACACIA_STAIRS(163), DARK_OAK_STAIRS(164), SLIME_BLOCK(165), BARRIER(166), - IRON_TRAPDOOR(167, false), + IRON_TRAPDOOR(167), PRISMARINE(168), SEA_LANTERN(169), HAY_BLOCK(170), - CARPET(171, false), + CARPET(171), HARD_CLAY(172), COAL_BLOCK(173), PACKED_ICE(174), - DOUBLE_PLANT(175, false), - STANDING_BANNER(176, false), - WALL_BANNER(177, false), - DAYLIGHT_DETECTOR_INVERTED(178, false), + DOUBLE_PLANT(175), + STANDING_BANNER(176), + WALL_BANNER(177), + DAYLIGHT_DETECTOR_INVERTED(178), RED_SANDSTONE(179), RED_SANDSTONE_STAIRS(180), DOUBLE_STONE_SLAB2(181), - STONE_SLAB2(182, false), - SPRUCE_FENCE_GATE(183, false), - BIRCH_FENCE_GATE(184, false), - JUNGLE_FENCE_GATE(185, false), - DARK_OAK_FENCE_GATE(186, false), - ACACIA_FENCE_GATE(187, false), - SPRUCE_FENCE(188, false), - BIRCH_FENCE(189, false), - JUNGLE_FENCE(190, false), - DARK_OAK_FENCE(191, false), - ACACIA_FENCE(192, false), - SPRUCE_DOOR(193, false), - BIRCH_DOOR(194, false), - JUNGLE_DOOR(195, false), - ACACIA_DOOR(196, false), - DARK_OAK_DOOR(197, false), - END_ROD(198, false), - CHORUS_PLANT(199, false), - CHORUS_FLOWER(200, false), + STONE_SLAB2(182), + SPRUCE_FENCE_GATE(183), + BIRCH_FENCE_GATE(184), + JUNGLE_FENCE_GATE(185), + DARK_OAK_FENCE_GATE(186), + ACACIA_FENCE_GATE(187), + SPRUCE_FENCE(188), + BIRCH_FENCE(189), + JUNGLE_FENCE(190), + DARK_OAK_FENCE(191), + ACACIA_FENCE(192), + SPRUCE_DOOR(193), + BIRCH_DOOR(194), + JUNGLE_DOOR(195), + ACACIA_DOOR(196), + DARK_OAK_DOOR(197), + END_ROD(198), + CHORUS_PLANT(199), + CHORUS_FLOWER(200), PURPUR_BLOCK(201), PURPUR_PILLAR(202), PURPUR_STAIRS(203), PURPUR_DOUBLE_SLAB(204), - PURPUR_SLAB(205, false), + PURPUR_SLAB(205), END_BRICKS(206), - BEETROOT_BLOCK(207, false), - GRASS_PATH(208, false), - END_GATEWAY(209, false), + BEETROOT_BLOCK(207), + GRASS_PATH(208), + END_GATEWAY(209), COMMAND_REPEATING(210), COMMAND_CHAIN(211), FROSTED_ICE(212), @@ -230,7 +231,7 @@ public enum DefaultMaterial NETHER_WART_BLOCK(214), RED_NETHER_BRICK(215), BONE_BLOCK(216), - STRUCTURE_VOID(217, false), + STRUCTURE_VOID(217), OBSERVER(218), WHITE_SHULKER_BOX(219), ORANGE_SHULKER_BOX(220), @@ -240,7 +241,7 @@ public enum DefaultMaterial LIME_SHULKER_BOX(224), PINK_SHULKER_BOX(225), GRAY_SHULKER_BOX(226), - SILVER_SHULKER_BOX(228), + SILVER_SHULKER_BOX(227), CYAN_SHULKER_BOX(228), PURPLE_SHULKER_BOX(229), BLUE_SHULKER_BOX(230), @@ -248,39 +249,42 @@ public enum DefaultMaterial GREEN_SHULKER_BOX(232), RED_SHULKER_BOX(233), BLACK_SHULKER_BOX(234), + WHITE_GLAZED_TERRACOTTA(235), + ORANGE_GLAZED_TERRACOTTA(236), + MAGENTA_GLAZED_TERRACOTTA(237), + LIGHT_BLUE_GLAZED_TERRACOTTA(238), + YELLOW_GLAZED_TERRACOTTA(239), + LIME_GLAZED_TERRACOTTA(240), + PINK_GLAZED_TERRACOTTA(241), + GRAY_GLAZED_TERRACOTTA(242), + SILVER_GLAZED_TERRACOTTA(243), + CYAN_GLAZED_TERRACOTTA(244), + PURPLE_GLAZED_TERRACOTTA(245), + BLUE_GLAZED_TERRACOTTA(246), + BROWN_GLAZED_TERRACOTTA(247), + GREEN_GLAZED_TERRACOTTA(248), + RED_GLAZED_TERRACOTTA(249), + BLACK_GLAZED_TERRACOTTA(250), CONCRETE(251), CONCRETE_POWDER(252), - UNKNOWN_BLOCK(254), - STRUCTURE_BLOCK(255, false); + STRUCTURE_BLOCK(255); /** * A DefaultMaterial lookup table with the material ID as the index */ - private static DefaultMaterial[] LookupID; + private static final DefaultMaterial[] LookupID = new DefaultMaterial[Arrays.stream(DefaultMaterial.values()).mapToInt(m -> m.id + 1).max().orElse(0)]; + static + { + for(DefaultMaterial m : DefaultMaterial.values()) + { + LookupID[m.id] = m; + } + } /** * The ID of the material */ public final int id; - /** - * Whether or not a material is solid. If set to false, it will prevent - * snowfall. Note: this isn't always equal to what Minecraft calls solid. - */ - private final boolean solid; - - /** - * Creates a new material. - * - * @param id Id of the material. - * @param solid Whether the material is solid. If set to false, it will - * prevent snowfall. Note: this isn't always equal to what - * Minecraft calls solid. - */ - private DefaultMaterial(int id, boolean solid) - { - this.id = id; - this.solid = solid; - } /** * Creates a new solid material where snow will fall on. @@ -290,66 +294,12 @@ private DefaultMaterial(int id, boolean solid) private DefaultMaterial(int id) { this.id = id; - this.solid = true; - } - - public boolean isAir() - { - return this == AIR; - } - - /** - * Returns true only if this material is flowing or stationary Water - * - * @return boolean whether or not this material is flowing or stationary - * Water - */ - public boolean isLiquid() - { - return this == WATER || this == STATIONARY_WATER || this == LAVA || this == STATIONARY_LAVA; - } - - /** - * Gets whether this material is solid. Materials that aren't solid are - * nonexistant for {@link LocalWorld#getSolidHeight(int, int)}. Note: this - * isn't always equal to what Minecraft calls solid. - * - * @return boolean Whether or not the material is considered solid - */ - public boolean isSolid() - { - return this.solid; - } - - /** - * Gets whether snow can fall on this block. - * - * @return Whether snow can fall on this block. - */ - public boolean canSnowFallOn() - { - // Exceptions for nonsolid leaves - // IF we get much more exceptions, we may want to make this a - // parameter in the constructor instead. - return this == LEAVES || this == LEAVES_2 || (this.solid && this != PACKED_ICE && this != ICE); } /** * A DefaultMaterial lookup table with the material name as the index */ - private static Map lookupName; - - static - { - LookupID = new DefaultMaterial[256]; - lookupName = new TreeMap(String.CASE_INSENSITIVE_ORDER); - - for (DefaultMaterial material : DefaultMaterial.values()) - { - LookupID[material.id] = material; - lookupName.put(material.name(), material); - } - } + private static final Map lookupName = Arrays.stream(DefaultMaterial.values()).collect(Collectors.toMap(Enum::name, Function.identity())); /** * Returns a DefaultMaterial object with the given material name. Name is @@ -367,7 +317,7 @@ public static DefaultMaterial getMaterial(String blockName) return null; } - DefaultMaterial defaultMaterial = lookupName.get(blockName); + DefaultMaterial defaultMaterial = lookupName.get(blockName.toUpperCase()); if (defaultMaterial != null) { return defaultMaterial; @@ -411,7 +361,7 @@ public static boolean contains(String name) */ public static DefaultMaterial getMaterial(int id) { - if (id < 256 && id > -1 && LookupID[id] != null) + if (id >= 0 && id < LookupID.length) { return LookupID[id]; } @@ -430,7 +380,7 @@ public static DefaultMaterial getMaterial(int id) */ public static boolean contains(int id) { - return id < 256 && LookupID[id] != null; + return id >= 0 && id < LookupID.length && LookupID[id] != null; } } diff --git a/common/src/main/java/com/pg85/otg/util/minecraft/defaults/EntityNames.java b/common/src/main/java/com/pg85/otg/util/minecraft/defaults/EntityNames.java index ac8b448d4..09947c7ca 100644 --- a/common/src/main/java/com/pg85/otg/util/minecraft/defaults/EntityNames.java +++ b/common/src/main/java/com/pg85/otg/util/minecraft/defaults/EntityNames.java @@ -3,6 +3,8 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; + /** * Contains a lot of alternative mob names. The implementation should support * this names, along with the other names that are available on the current @@ -108,7 +110,7 @@ public enum EntityNames WITHER_SKULL("wither_skull", "witherskull"); // Contains all aliases (alias, internalName) - private static Map MobAliases = new HashMap(); + private static final Map MobAliases = new HashMap<>(); // Auto-register all aliases in the enum static @@ -128,19 +130,7 @@ public enum EntityNames */ public static String toInternalName(String alias) { - for(String key : MobAliases.keySet()) - { - if( - key.toLowerCase().trim().replace("minecraft:","").replace("entity","").trim().replace("_","").equalsIgnoreCase( - alias.toLowerCase().trim().replace("minecraft:","").replace("entity","").trim().replace("_","") - ) - ) - { - return MobAliases.get(key); - } - } - - return alias; + return MobAliases.getOrDefault(keyFromAlias(alias), alias); } /** @@ -151,18 +141,27 @@ public static String toInternalName(String alias) */ private static void register(String internalMinecraftName, String... aliases) { - for (String alias : aliases) + for(String alias : aliases) { - MobAliases.put("minecraft:" + alias, "minecraft:" + internalMinecraftName); + MobAliases.put(keyFromAlias(alias), internalMinecraftName); } } + private static String keyFromAlias(String alias) + { + String key = alias.trim().toLowerCase(); + key = StringUtils.removeStart(key, "minecraft:"); + key = StringUtils.remove(key, '_'); + return key; + } + private String[] aliases; private String internalMinecraftName; private EntityNames(String internalMinecraftName, String... aliases) { - this.internalMinecraftName = internalMinecraftName; + int i = internalMinecraftName.indexOf(':'); + this.internalMinecraftName = i > 0 ? internalMinecraftName : i == 0 ? ("minecraft" + internalMinecraftName) : ("minecraft:" + internalMinecraftName); this.aliases = aliases; } @@ -172,6 +171,6 @@ private EntityNames(String internalMinecraftName, String... aliases) */ public String getInternalName() { - return "minecraft:" + this.internalMinecraftName; + return this.internalMinecraftName; } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index aa695a798..cc0f1228f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,11 +15,11 @@ forge_mappings_channel=stable forge_mappings_version=39-1.12 forge_access_transformers=openterraingenerator_at.cfg forge_plugin_class=com.pg85.otg.forge.asm.excluded.launch.OTGCorePlugin -worldedit_forge_version=6.1.6 +worldedit_forge_version=2941712 # bukkit bukkit_version=R0.1-SNAPSHOT -worldedit_bukkit_version=6.1.5 +worldedit_bukkit_version=2597538 # mod mod_name=OpenTerrainGenerator @@ -30,4 +30,3 @@ mod_version=v9.7 shadow_version=9.2.2 jackson_version=2.9.7 snakeyaml_version=1.23 -worldedit_core_version=6.1.4-SNAPSHOT diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b953..1b33c55ba 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18bc253b..d4081da47 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b0..23d15a936 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a2183..db3a6ac20 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/platforms/bukkit/build.gradle b/platforms/bukkit/build.gradle index 824757898..5228d7ccd 100644 --- a/platforms/bukkit/build.gradle +++ b/platforms/bukkit/build.gradle @@ -7,15 +7,14 @@ base.archivesName = "${mod_name}-bukkit" repositories { maven { url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } // bukkit maven { url = "https://rutgerkok.nl/repo/" } // spigot - maven { url = 'https://maven.enginehub.org/artifactory/repo/' } // worldedit } dependencies { implementation "org.bukkit:bukkit:${minecraft_version}-${bukkit_version}" implementation "org.spigotmc:spigot:${minecraft_version}-${bukkit_version}" implementation project(':common') - compileOnly "com.sk89q.worldedit:worldedit-bukkit:${worldedit_bukkit_version}" - compileOnly "com.sk89q.worldedit:worldedit-core:${worldedit_core_version}" + implementation "curse.maven:worldedit-31043:${worldedit_bukkit_version}" + implementation 'it.unimi.dsi:fastutil:7.1.0' } processResources { diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/BukkitMojangSettings.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/BukkitMojangSettings.java index 5e04a6fc1..90ba4fbde 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/BukkitMojangSettings.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/BukkitMojangSettings.java @@ -76,13 +76,13 @@ public float getSurfaceVolatility() @Override public LocalMaterialData getSurfaceBlock() { - return BukkitMaterialData.ofMinecraftBlockData(biomeBase.q); + return BukkitMaterialData.ofMinecraftBlockState(biomeBase.q); } @Override public LocalMaterialData getGroundBlock() { - return BukkitMaterialData.ofMinecraftBlockData(biomeBase.r); + return BukkitMaterialData.ofMinecraftBlockState(biomeBase.r); } @Override diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/OTGBiomeBase.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/OTGBiomeBase.java index 34d6187fa..efd867b50 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/OTGBiomeBase.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/biomes/OTGBiomeBase.java @@ -73,8 +73,8 @@ private OTGBiomeBase(BiomeConfig biomeConfig) throw new AssertionError("Biome temperature mismatch"); } - this.q = ((BukkitMaterialData) biomeConfig.getDefaultSurfaceBlock()).internalBlock(); - this.r = ((BukkitMaterialData) biomeConfig.getDefaultGroundBlock()).internalBlock(); + this.q = ((BukkitMaterialData) biomeConfig.getDefaultSurfaceBlock()).getBlockState(); + this.r = ((BukkitMaterialData) biomeConfig.getDefaultGroundBlock()).getBlockState(); // Mob spawning // We add the merged lists and not the default lists diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/OTGListener.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/OTGListener.java index b6b8eb829..9131b8a9a 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/OTGListener.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/OTGListener.java @@ -2,10 +2,7 @@ import com.pg85.otg.OTG; import com.pg85.otg.bukkit.OTGPlugin; -import com.pg85.otg.bukkit.world.BukkitWorld; -import com.pg85.otg.bukkit.world.WorldHelper; import com.pg85.otg.configuration.standard.PluginStandardValues; -import com.pg85.otg.util.ChunkCoordinate; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -14,7 +11,6 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerChangedWorldEvent; import org.bukkit.event.player.PlayerRegisterChannelEvent; -import org.bukkit.event.world.ChunkUnloadEvent; import org.bukkit.event.world.StructureGrowEvent; import org.bukkit.event.world.WorldInitEvent; import org.bukkit.event.world.WorldSaveEvent; @@ -55,16 +51,6 @@ public void onUnload(WorldUnloadEvent event) ((BukkitEngine)OTG.getEngine()).onSave(event.getWorld()); } - @EventHandler - public void onChunkUnload(ChunkUnloadEvent unloadEvent) - { - BukkitWorld bukkitWorld = (BukkitWorld)WorldHelper.toLocalWorld(unloadEvent.getWorld()); - if(bukkitWorld != null && bukkitWorld.getChunkGenerator() != null && unloadEvent.getChunk() != null) - { - bukkitWorld.getChunkGenerator().clearChunkFromCache(ChunkCoordinate.fromChunkCoords(unloadEvent.getChunk().getX(), unloadEvent.getChunk().getZ())); - } - } - @EventHandler public void onWorldUnload(WorldUnloadEvent event) { diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/SaplingListener.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/SaplingListener.java index 9faec1ba9..dbc393be2 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/SaplingListener.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/events/SaplingListener.java @@ -33,7 +33,7 @@ void onStructureGrow(StructureGrowEvent event) // Need the event location for later - might also need the material Location location = event.getLocation(); IBlockData blockData = world.getWorld().getType(new BlockPosition(location.getBlockX(), location.getBlockY(), location.getBlockZ())); - BukkitMaterialData material = BukkitMaterialData.ofMinecraftBlockData(blockData); + BukkitMaterialData material = BukkitMaterialData.ofMinecraftBlockState(blockData); SaplingGen sapling; // Get sapling type diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/BukkitChunkBuffer.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/BukkitChunkBuffer.java index 55defa669..19a0277a4 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/BukkitChunkBuffer.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/BukkitChunkBuffer.java @@ -31,7 +31,7 @@ public LocalMaterialData getBlock(int blockX, int blockY, int blockZ) int blockId = chunkData.getTypeId(blockX, blockY, blockZ); @SuppressWarnings("deprecation") byte blockData = chunkData.getData(blockX, blockY, blockZ); - return BukkitMaterialData.ofIds(blockId, blockData); + return BukkitMaterialData.ofMinecraftBlockState(blockId, blockData); } @Override diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/OTGChunkGenerator.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/OTGChunkGenerator.java index 1bafa7cba..85f215dfa 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/OTGChunkGenerator.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/generator/OTGChunkGenerator.java @@ -14,28 +14,28 @@ import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.BlockPos2D; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; import com.pg85.otg.util.bo3.NamedBinaryTag; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; import net.minecraft.server.v1_12_R1.BlockPosition; +import net.minecraft.server.v1_12_R1.Blocks; import net.minecraft.server.v1_12_R1.Chunk; +import net.minecraft.server.v1_12_R1.ChunkSection; import net.minecraft.server.v1_12_R1.DataConverter; import net.minecraft.server.v1_12_R1.DataConverterRegistry; import net.minecraft.server.v1_12_R1.DataConverterTypes; -import net.minecraft.server.v1_12_R1.IBlockData; -import net.minecraft.server.v1_12_R1.ITileEntity; import net.minecraft.server.v1_12_R1.NBTTagCompound; import net.minecraft.server.v1_12_R1.TileEntity; + import org.bukkit.World; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; import org.bukkit.material.MaterialData; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; -import java.util.Map.Entry; +import java.util.Optional; import java.util.Random; public class OTGChunkGenerator extends ChunkGenerator @@ -49,13 +49,8 @@ public class OTGChunkGenerator extends ChunkGenerator private BukkitWorld world; // Caches - private FifoMap unloadedBlockColumnsCache; - private FifoMap unloadedChunksCache; - private Entry lastUsedChunk1; - private Entry lastUsedChunk2; - private Entry lastUsedChunk3; - private Entry lastUsedChunk4; - Object chunkCacheLock = new Object(); + private LRUCache unloadedBlockColumnsCache; + private LRUCache unloadedChunksCache; // public OTGChunkGenerator(OTGPlugin _plugin, BukkitWorld world) @@ -65,50 +60,8 @@ public OTGChunkGenerator(OTGPlugin _plugin, BukkitWorld world) this.dataConverter = DataConverterRegistry.a(); // TODO: Add a setting to the worldconfig for the size of these caches. // Worlds with lots of BO4's and large smoothing areas may want to increase this. - this.unloadedBlockColumnsCache = new FifoMap(1024); - this.unloadedChunksCache = new FifoMap(1024); //Changed 128 chunks cache to 1024 chunks cache for customstructures - lastUsedChunk1 = null; - lastUsedChunk2 = null; - lastUsedChunk3 = null; - lastUsedChunk4 = null; - } - - // Called by /otg flush command to clear memory. - // TODO: Implement /otg flush for spigot. - public void clearChunkCache() - { - synchronized(this.chunkCacheLock) - { - lastUsedChunk1 = null; - lastUsedChunk2 = null; - lastUsedChunk3 = null; - lastUsedChunk4 = null; - this.unloadedBlockColumnsCache.clear(); - this.unloadedChunksCache.clear(); - } - } - - public void clearChunkFromCache(ChunkCoordinate chunkCoordinate) - { - synchronized(this.chunkCacheLock) - { - if(lastUsedChunk1 != null && lastUsedChunk1.getKey().equals(chunkCoordinate)) - { - lastUsedChunk1 = null; - } - else if(lastUsedChunk2 != null && lastUsedChunk2.getKey().equals(chunkCoordinate)) - { - lastUsedChunk2 = null; - } - else if(lastUsedChunk3 != null && lastUsedChunk3.getKey().equals(chunkCoordinate)) - { - lastUsedChunk3 = null; - } - else if(lastUsedChunk4 != null && lastUsedChunk4.getKey().equals(chunkCoordinate)) - { - lastUsedChunk4 = null; - } - } + this.unloadedBlockColumnsCache = new LRUCache(1024); + this.unloadedChunksCache = new LRUCache(1024); //Changed 128 chunks cache to 1024 chunks cache for customstructures } /** @@ -180,263 +133,99 @@ public boolean canSpawn(World world, int x, int z) public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) { makeSureWorldIsInitialized(world); - - ChunkData chunkData = this.NotGenerate ? null : unloadedChunksCache.get(ChunkCoordinate.fromChunkCoords(chunkX,chunkZ)); - if(chunkData == null) - { - chunkData = createChunkData(world); - if (this.NotGenerate) - { - return chunkData; - } + if(this.NotGenerate) + { + return this.createChunkData(this.world.getWorld().getWorld()); + } - ChunkCoordinate chunkCoord = ChunkCoordinate.fromChunkCoords(chunkX, chunkZ); - BukkitChunkBuffer chunkBuffer = new BukkitChunkBuffer(chunkCoord, chunkData); - this.chunkProviderOTG.generate(chunkBuffer); - - } - return chunkData; + return this.unloadedChunksCache.computeIfAbsent(ChunkCoordinate.fromChunkCoords(chunkX, chunkZ), k -> { + ChunkData v = this.createChunkData(this.world.getWorld().getWorld()); + this.chunkProviderOTG.generate(new BukkitChunkBuffer(k, v)); + return v; + }); } public Chunk getChunk(int x, int z) { - ChunkCoordinate chunkCoord = ChunkCoordinate.fromBlockCoords(x, z); - - Chunk chunk = null; - synchronized(this.chunkCacheLock) - { - if(lastUsedChunk1 != null && lastUsedChunk1.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk1.getValue(); - } - else if(lastUsedChunk2 != null && lastUsedChunk2.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk2.getValue(); - } - else if(lastUsedChunk3 != null && lastUsedChunk3.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk3.getValue(); - } - else if(lastUsedChunk4 != null && lastUsedChunk4.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk4.getValue(); - } - } - if(chunk == null) - { - // Hopefully this is equal to ChunkProviderServer.getLoadedChunk - // So it won't try to populate the chunk. - chunk = this.world.getWorld().getChunkProvider().getLoadedChunkAt(chunkCoord.getChunkX(), chunkCoord.getChunkZ()); - if(chunk == null) - { - // Request the chunk with a risk of it being populated.. - chunk = this.world.getWorld().getChunkAt(chunkCoord.getChunkX(), chunkCoord.getChunkZ()); - } - if(chunk != null) - { - synchronized(this.chunkCacheLock) - { - lastUsedChunk4 = lastUsedChunk3; - lastUsedChunk3 = lastUsedChunk2; - lastUsedChunk2 = lastUsedChunk1; - lastUsedChunk1 = new AbstractMap.SimpleEntry(chunkCoord, chunk); - } - } - } - return chunk; - } - + return this.world.getWorld().getChunkAt(x >> 4, z >> 4); + } + public void setBlock(int x, int y, int z, LocalMaterialData material, NamedBinaryTag metaDataTag, BiomeConfig biomeConfig) { - if (y < PluginStandardValues.WORLD_DEPTH || y >= PluginStandardValues.WORLD_HEIGHT) + if(y < PluginStandardValues.WORLD_DEPTH || y >= PluginStandardValues.WORLD_HEIGHT) { return; - } - - try - { - IBlockData blockData = ((BukkitMaterialData) material).internalBlock(); + } + + BlockPosition pos = new BlockPosition(x, y, z); - // Get chunk from (faster) custom cache - Chunk chunk = this.getChunk(x, z); + this.world.getWorld().setTypeAndData(pos, ((BukkitMaterialData) material).getBlockState(), 2 | 16); - if (chunk == null) + if(metaDataTag != null) + { + TileEntity tileEntity = this.world.getWorld().getTileEntity(pos); + if(tileEntity != null) { - throw new RuntimeException("Could not provide chunk."); + NBTTagCompound nbtTag = NBTHelper.getNMSFromNBTTagCompound(metaDataTag); + nbtTag.setInt("x", x); + nbtTag.setInt("y", y); + nbtTag.setInt("z", z); + // Update to current Minecraft format (maybe we want to do this at + // server startup instead, and then save the result?) + nbtTag = this.dataConverter.a(DataConverterTypes.BLOCK_ENTITY, nbtTag, -1); + tileEntity.load(nbtTag); } - - BlockPosition blockPos = new BlockPosition(x, y, z); - - // Disable nearby block physics (except for tile entities) and set block - boolean oldCaptureBlockStates = this.world.getWorld().captureBlockStates; - this.world.getWorld().captureBlockStates = !(blockData.getBlock() instanceof ITileEntity); - //this.world.getWorld().captureBlockStates = true; - IBlockData oldBlockData = chunk.a(blockPos, blockData); - this.world.getWorld().captureBlockStates = oldCaptureBlockStates; - - if (oldBlockData == null) + else { - return; // Happens when block to place is the same as block being placed? TODO: Is that the only time this happens? + if(OTG.getPluginConfig().spawnLog) + { + OTG.log(LogMarker.WARN, "Skipping tile entity with id {}, cannot be placed at {},{},{}", Optional.ofNullable(metaDataTag.getTag("id")).map(NamedBinaryTag::getValue).orElse(null), x, y, z); + } } - - //if (blockData.c() != oldBlockData.c() || blockData.d() != oldBlockData.d()) - //{ - //if (isSafeForLightUpdates(chunk, x, z)) - //{ - // Relight - //this.world.getWorld().methodProfiler.a("checkLight"); - //this.world.getWorld().w(blockPos); - //this.world.getWorld().methodProfiler.b(); - //} - //} - - if (metaDataTag != null) - { - attachMetadata(x, y, z, metaDataTag); - } - - // Notify world: (2 | 16) == update client, don't update observers - //notifyAndUpdatePhysics(this.world.getWorld(), blockPos, chunk, oldBlockData, blockData, 2 | 16); TODO: Is this no longer needed? - world.getWorld().notifyAndUpdatePhysics(blockPos, chunk, oldBlockData, blockData, 2 | 16); - } catch (Throwable t) { - // TODO: What is this? remove? - /* - String populatingChunkInfo = this.chunkCache == null? "(no chunk)" : - this.chunkCache[0].locX + "," + this.chunkCache[0].locZ; - // Add location info to error - RuntimeException runtimeException = new RuntimeException("Error setting " - + material + " block at " + x + "," + y + "," + z - + " while populating chunk " + populatingChunkInfo, t); - runtimeException.setStackTrace(new StackTraceElement[0]); - throw runtimeException; - */ } } - - // CraftBukkit start - Split off from above in order to directly send client and physic updates - public void notifyAndUpdatePhysics(net.minecraft.server.v1_12_R1.World _this, BlockPosition blockposition, Chunk chunk, IBlockData oldBlock, IBlockData newBlock, int i) + + @SuppressWarnings("deprecation") + public LocalMaterialData[] getBlockColumnInUnloadedChunk(int x, int z) { - net.minecraft.server.v1_12_R1.Block block = newBlock.getBlock(); - if ( - (i & 2) != 0 && - ( - !_this.isClientSide || - (i & 4) == 0 - ) && ( - chunk == null || - //chunk.isReady() - // Replace isReady with Forge's isPopulated to prevent - // updates that would cause cascading chunkgen: - // return this.ticked && this.isTerrainPopulated && this.isLightPopulated; - (chunk.j() && chunk.isDone() && chunk.v()) - ) - ) // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement - { - _this.notify(blockposition, oldBlock, newBlock, i); - } + LocalMaterialData[] blockColumn = new LocalMaterialData[256]; - if ( - !_this.isClientSide && - (i & 1) != 0 - ) + Chunk chunk = this.world.getWorld().getChunkProvider().getLoadedChunkAt(x >> 4, z >> 4); + if(chunk != null) { - _this.update(blockposition, oldBlock.getBlock(), true); - if (newBlock.n()) + int y = 0; + for(ChunkSection section : chunk.getSections()) { - _this.updateAdjacentComparators(blockposition, block); + if(section != null) + { + for(int i = 0; i < 16; i++) + { + blockColumn[y++] = BukkitMaterialData.ofMinecraftBlockState(section.getType(x & 15, i, z & 15)); + } + } + else + { + for(int i = 0; i < 16; i++) + { + blockColumn[y++] = BukkitMaterialData.AIR; + } + } } } - else if ( - !_this.isClientSide && - (i & 16) == 0 - ) + else { - _this.c(blockposition, block); + ChunkData chunkData = this.generateChunkData(this.world.getWorld().getWorld(), null, x >> 4, z >> 4, null); + for(int y = 0; y < 256; y++) + { + MaterialData materialData = chunkData.getTypeAndData(x & 15, y, z & 15); + blockColumn[y] = BukkitMaterialData.ofMinecraftBlockState(materialData.getItemTypeId(), materialData.getData()); + } } - } - private void attachMetadata(int x, int y, int z, NamedBinaryTag tag) - { - // Convert NamedBinaryTag to a native nms tag - NBTTagCompound nmsTag = NBTHelper.getNMSFromNBTTagCompound(tag); - // Add the x, y and z position to it - nmsTag.setInt("x", x); - nmsTag.setInt("y", y); - nmsTag.setInt("z", z); - // Update to current Minecraft format (maybe we want to do this at - // server startup instead, and then save the result?) - nmsTag = this.dataConverter.a(DataConverterTypes.BLOCK_ENTITY, nmsTag, -1); - // Add that data to the current tile entity in the world - TileEntity tileEntity = this.world.getWorld().getTileEntity(new BlockPosition(x, y, z)); - if (tileEntity != null) - { - tileEntity.load(nmsTag); - } else { - if(OTG.getPluginConfig().spawnLog) - { - OTG.log(LogMarker.WARN, "Skipping tile entity with id {}, cannot be placed at {},{},{}.", nmsTag.getString("id"), x, y, z); - } - } + return blockColumn; } - - public LocalMaterialData[] getBlockColumnInUnloadedChunk(int x, int z) - { - BlockPos2D blockPos = new BlockPos2D(x, z); - ChunkCoordinate chunkCoord = ChunkCoordinate.fromBlockCoords(x, z); - int chunkX = chunkCoord.getChunkX(); - int chunkZ = chunkCoord.getChunkZ(); - - // Get internal coordinates for block in chunk - byte blockX = (byte)(x &= 0xF); - byte blockZ = (byte)(z &= 0xF); - - LocalMaterialData[] cachedColumn = this.unloadedBlockColumnsCache.get(blockPos); - if(cachedColumn != null) - { - return cachedColumn; - } - - cachedColumn = new LocalMaterialData[256]; - LocalMaterialData[] blocksInColumn = new LocalMaterialData[256]; - - Chunk chunk = this.world.getWorld().getChunkProvider().getLoadedChunkAt(chunkX, chunkZ); - if(chunk == null) - { - ChunkData chunkData = this.unloadedChunksCache.get(chunkCoord); - if(chunkData == null) - { - // Generate a chunk without populating it - chunkData = this.generateChunkData(this.world.getWorld().getWorld(), this.world.getWorld().random, chunkX, chunkZ, (BiomeGrid)null); - } - for(short y = 0; y < 256; y++) - { - MaterialData blockInChunk = chunkData.getTypeAndData(blockX, y, blockZ); - if(blockInChunk != null) - { - blocksInColumn[y] = BukkitMaterialData.ofIds(blockInChunk.getItemTypeId(), blockInChunk.getData()); - } else { - break; - } - } - unloadedChunksCache.put(chunkCoord, chunkData); - } else { - for(short y = 0; y < 256; y++) - { - IBlockData blockInChunk = chunk.getBlockData(new BlockPosition(blockX, y, blockZ)); - if(blockInChunk != null) - { - blocksInColumn[y] = BukkitMaterialData.ofMinecraftBlockData(blockInChunk); - } else { - break; - } - } - } - unloadedBlockColumnsCache.put(blockPos, cachedColumn); - return blocksInColumn; - } - public double getBiomeBlocksNoiseValue(int blockX, int blockZ) { return this.chunkProviderOTG.getBiomeBlocksNoiseValue(blockX, blockZ); diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BlockMaterialData.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BlockMaterialData.java new file mode 100644 index 000000000..27e4e65f4 --- /dev/null +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BlockMaterialData.java @@ -0,0 +1,96 @@ +package com.pg85.otg.bukkit.materials; + +import com.pg85.otg.util.bo3.Rotation; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.server.v1_12_R1.Block; +import net.minecraft.server.v1_12_R1.IBlockData; + +class BlockMaterialData extends BukkitMaterialData +{ + private static final Reference2ReferenceMap INSTANCES = new Reference2ReferenceOpenHashMap<>(); + private StateMaterialData withDefaultBlockData; + private final Block block; + + private BlockMaterialData(Block block) + { + super(Block.getId(block)); + this.block = block; + } + + static BlockMaterialData of(Block block) + { + return INSTANCES.computeIfAbsent(block, BlockMaterialData::new); + } + + @Override + public BlockMaterialData withoutBlockData() + { + return this; + } + + @Override + public StateMaterialData withBlockData() + { + return withDefaultBlockData(); + } + + @Override + public StateMaterialData withDefaultBlockData() + { + StateMaterialData withDefaultBlockData; + if((withDefaultBlockData = this.withDefaultBlockData) == null) + { + this.withDefaultBlockData = withDefaultBlockData = StateMaterialData.of(this.getBlockState()); + } + return withDefaultBlockData; + } + + @SuppressWarnings("deprecation") + @Override + public StateMaterialData withBlockData(int newData) + { + return BukkitMaterialData.ofMinecraftBlockState(this.block.fromLegacyData(newData)); + } + + @Override + public String getName() + { + if(this.defaultMaterial != null) + { + return this.defaultMaterial.name(); + } + return Block.REGISTRY.b(this.block).toString(); + } + + @Override + public Block getBlock() + { + return this.block; + } + + @Override + public IBlockData getBlockState() + { + return this.block.getBlockData(); + } + + @Override + public byte getBlockData() + { + return 0; + } + + @Override + public int getBlockDataMask() + { + return -1; + } + + @Override + public BlockMaterialData rotate(Rotation rotation) + { + return this; + } +} \ No newline at end of file diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BukkitMaterialData.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BukkitMaterialData.java index 6624b3e30..3139c9a31 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BukkitMaterialData.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/BukkitMaterialData.java @@ -1,491 +1,183 @@ package com.pg85.otg.bukkit.materials; -import com.pg85.otg.OTG; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.configuration.standard.PluginStandardValues; import com.pg85.otg.exception.InvalidConfigException; -import com.pg85.otg.logging.LogMarker; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; import net.minecraft.server.v1_12_R1.Block; import net.minecraft.server.v1_12_R1.BlockFalling; import net.minecraft.server.v1_12_R1.Blocks; import net.minecraft.server.v1_12_R1.IBlockData; +import net.minecraft.server.v1_12_R1.Material; -//TODO: Make this class unmodifiable (parseForWorld modifies atm), -//implement a world-specific materials cache and ensure only one -//instance of each unique material (id+metadata) exists in memory. -//TODO: Do creation of new material instances in one place only? - -/** - * Implementation of LocalMaterial that wraps one of Minecraft's Blocks. - * - */ -public class BukkitMaterialData extends LocalMaterialData +public abstract class BukkitMaterialData extends LocalMaterialData { - /** - * Block id and data, calculated as {@code blockId << 4 | blockData}, or - * without binary operators: {@code blockId * 16 + blockData}. - * - *

Note that Minecraft's Block.getCombinedId uses another format (at - * least in Minecraft 1.8). However, Minecraft's ChunkSection uses the same - * format as this field. - */ - private int combinedBlockId; - // Used only for blocks read from settings, so we know whether to append data when writing - // TODO: Clean up the constructors, hasData is only used for blocks parsed from configs, - // so we can write them back properly, so may produce unexpected results when used differently. - private boolean hasData; - - private BukkitMaterialData(int blockId, int blockData, boolean hasData) - { - this.combinedBlockId = blockId << 4 | blockData; - this.hasData = hasData; - } - - public BukkitMaterialData(String input, boolean hasData) - { - this.combinedBlockId = -1; - this.rawEntry = input; - this.hasData = hasData; - } + private static final Set PROBLEMATIC_BLOCKS = ImmutableSet.of(Blocks.PORTAL, Blocks.DISPENSER, Blocks.ACACIA_STAIRS, Blocks.BIRCH_STAIRS, Blocks.BRICK_STAIRS, Blocks.DARK_OAK_STAIRS, Blocks.JUNGLE_STAIRS, Blocks.NETHER_BRICK_STAIRS, Blocks.OAK_STAIRS, Blocks.PURPUR_STAIRS, Blocks.QUARTZ_STAIRS, Blocks.RED_SANDSTONE_STAIRS, Blocks.SANDSTONE_STAIRS, Blocks.SPRUCE_STAIRS, Blocks.STONE_BRICK_STAIRS, Blocks.STONE_STAIRS); + private static final BukkitMaterialData BLANK = UnknownMaterialData.of(BLANK_NAME); + public static final StateMaterialData AIR = ofMinecraftBlockState(Blocks.AIR.getBlockData()); - public static BukkitMaterialData getBlank() + BukkitMaterialData(int blockId) { - BukkitMaterialData material = new BukkitMaterialData(null, false); - material.isBlank = true; - return material; + super(blockId); } - public static LocalMaterialData ofString(String input) throws InvalidConfigException - { - // Try parsing as an internal Minecraft name - // This is so that things like "minecraft:stone" aren't parsed - // as the block "minecraft" with data "stone", but instead as the - // block "minecraft:stone" with no block data. - - // Used in BO4's as placeholder/detector block. - if(input.toLowerCase().equals("blank")) - { - return BukkitMaterialData.getBlank(); - } - - // Try blockname / minecraft:blockname syntax - String newInput = input; - - Block block = Block.getByName(newInput); - if (block != null) + public static BukkitMaterialData ofString(String input) throws InvalidConfigException + { + if(input.equalsIgnoreCase(BLANK_NAME)) { - // Some old apps exported schematics/bo3's exported "STAIRS" without metadata (for instance "STAIRS:0"). - // However, the default rotation has changed so fix this by adding the correct metadata. - - if( - block == Blocks.PORTAL || - block == Blocks.DISPENSER || - block == Blocks.ACACIA_STAIRS || - block == Blocks.BIRCH_STAIRS || - block == Blocks.BRICK_STAIRS || - block == Blocks.DARK_OAK_STAIRS || - block == Blocks.JUNGLE_STAIRS || - block == Blocks.NETHER_BRICK_STAIRS || - block == Blocks.OAK_STAIRS || - block == Blocks.PURPUR_STAIRS || - block == Blocks.QUARTZ_STAIRS || - block == Blocks.RED_SANDSTONE_STAIRS || - block == Blocks.SANDSTONE_STAIRS || - block == Blocks.SPRUCE_STAIRS || - block == Blocks.STONE_BRICK_STAIRS || - block == Blocks.STONE_STAIRS - ) - { - newInput = input + ":0"; // TODO: Shouldn't this be 3? This appears to fix the problem for the dungeon dimension but I still see it in BB, double check? - } else { - return BukkitMaterialData.ofMinecraftBlock(block, false); - } + return BLANK; } - // Try block(:data) syntax - - String blockName = newInput; - int blockData = -1; - - // When there is a . or a : in the name, extract block data - int splitIndex = newInput.lastIndexOf(":"); - if (splitIndex == -1) - { - splitIndex = newInput.lastIndexOf("."); - } - if (splitIndex != -1) + Block block = parseBlock(input); + int meta = -1; + if(block == null) { - blockName = newInput.substring(0, splitIndex); - try + int i = input.lastIndexOf(':'); + if(i < 0) { - blockData = Integer.parseInt(newInput.substring(splitIndex + 1)); + i = input.lastIndexOf('.'); } - catch (NumberFormatException e) + if(i >= 0) { - blockName = newInput; + try + { + meta = Integer.parseInt(input.substring(i + 1)); + block = parseBlock(input.substring(0, i)); + } + catch(NumberFormatException e) + { + // ignore + } } } - // Try block name without data - block = Block.getByName(blockName); - if (block == null) + if(block != null) { - DefaultMaterial defaultMaterial = DefaultMaterial.getMaterial(blockName); - if (defaultMaterial != null) + if(meta < 0 && PROBLEMATIC_BLOCKS.contains(block)) { - block = Block.getById(defaultMaterial.id); - - // Some old apps exported schematics/bo3's exported "STAIRS" without metadata (for instance "STAIRS:0"). - // However, the default rotation has changed so fix this by adding the correct metadata. - - // TODO: Check if the block uses the Facing property instead of checking a list of known blocks? - if( - blockData == -1 && - ( - block == Blocks.PORTAL || - block == Blocks.DISPENSER || - block == Blocks.ACACIA_STAIRS || - block == Blocks.BIRCH_STAIRS || - block == Blocks.BRICK_STAIRS || - block == Blocks.DARK_OAK_STAIRS || - block == Blocks.JUNGLE_STAIRS || - block == Blocks.NETHER_BRICK_STAIRS || - block == Blocks.OAK_STAIRS || - block == Blocks.PURPUR_STAIRS || - block == Blocks.QUARTZ_STAIRS || - block == Blocks.RED_SANDSTONE_STAIRS || - block == Blocks.SANDSTONE_STAIRS || - block == Blocks.SPRUCE_STAIRS || - block == Blocks.STONE_BRICK_STAIRS || - block == Blocks.STONE_STAIRS - ) - ) - { - blockData = 0; // TODO: Shouldn't this be 3? This appears to fix the problem for the dungeon dimension but I still see it in BB, double check? - } + return ofMinecraftBlockState(block.getBlockData()); } - } - // Get the block - if (block != null) - { - if (blockData == -1) + if(meta < 0) + { + return ofMinecraftBlock(block); + } + else { - // Use default - return BukkitMaterialData.ofMinecraftBlock(block, false); - } else { - // Use specified data try { - return BukkitMaterialData.ofMinecraftBlockData(block.fromLegacyData(blockData)); + return ofMinecraftBlockState(block, meta); } - catch(java.lang.ArrayIndexOutOfBoundsException e) + catch(ArrayIndexOutOfBoundsException e) { - throw new InvalidConfigException("Illegal meta data for the block type, cannot use " + newInput); + throw new InvalidConfigException("Illegal meta data for the block type, cannot use " + input); } - catch (IllegalArgumentException e) + catch(IllegalArgumentException e) { - throw new InvalidConfigException("Illegal block data for the block type, cannot use " + newInput); + throw new InvalidConfigException("Illegal block data for the block type, cannot use " + input); } } } - - // Failed, try parsing later as a fallback. - return new BukkitMaterialData(newInput, false); // TODO: Assuming all fallback blocks contain data atm. + return UnknownMaterialData.of(input); } - /** - * Gets a {@code BukkitMaterialData} of the given id and data. - * @param id The block id. - * @param data The block data. - * @return The {@code BukkitMateialData} instance. - */ - public static BukkitMaterialData ofIds(int id, int data) + private static Block parseBlock(String input) { - return new BukkitMaterialData(id, data, true); - } - - /** - * Gets a {@code BukkitMaterialData} of the given material and data. - * @param material The material. - * @param data The block data. - * @return The {@code BukkitMateialData} instance. - */ - public static BukkitMaterialData ofDefaultMaterial(DefaultMaterial material, int data) - { - return ofIds(material.id, data); + Block block = Block.getByName(input); + if(block == null) + { + DefaultMaterial defaultMaterial = DefaultMaterial.getMaterial(input); + if(defaultMaterial != null) + { + block = Block.getById(defaultMaterial.id); + } + } + return block; } - @Override - protected BukkitMaterialData ofDefaultMaterialPrivate(DefaultMaterial material, int data) + public static StateMaterialData ofDefaultMaterial(DefaultMaterial defaultMaterial, int blockData) { - return ofDefaultMaterial(material, data); + return ofMinecraftBlockState(defaultMaterial.id, blockData); } - - /** - * Gets a {@code BukkitMaterialData} of the given Minecraft block. The - * default block data (usually 0) will be used. - * @param block The material. - * @return The {@code BukkitMateialData} instance. - */ - public static BukkitMaterialData ofMinecraftBlock(Block block, boolean hasData) + + public static BlockMaterialData ofMinecraftBlock(Block block) { - BukkitMaterialData material = ofIds(Block.getId(block), block.toLegacyData(block.getBlockData())); - material.hasData = hasData; - return material; + return BlockMaterialData.of(block); } - public static BukkitMaterialData ofBukkitBlock(org.bukkit.block.Block block) + public static StateMaterialData ofMinecraftBlockState(int blockId, int blockData) { - return ofIds(block.getType().getId(), block.getData()); + return ofMinecraftBlockState(Block.getById(blockId), blockData); } - - /** - * Gets a {@code BukkitMaterialData} of the given Minecraft blockData. - * @param blockData The material an data. - * @return The {@code BukkitMateialData} instance. - */ - public static BukkitMaterialData ofMinecraftBlockData(IBlockData blockData) - { - Block block = blockData.getBlock(); - return new BukkitMaterialData(Block.getId(block), block.toLegacyData(blockData), true); - } - + @SuppressWarnings("deprecation") - @Override - public LocalMaterialData withBlockData(int i) + public static StateMaterialData ofMinecraftBlockState(Block block, int blockData) { - if (i == getBlockData()) - { - return this; - } - - Block block = Block.getById(getBlockId()); - return ofMinecraftBlockData(block.fromLegacyData(i)); + return ofMinecraftBlockState(block.fromLegacyData(blockData)); } - @Override - public LocalMaterialData withDefaultBlockData() + public static StateMaterialData ofMinecraftBlockState(IBlockData state) { - Block block = Block.getById(getBlockId()); - byte defaultData = (byte) block.toLegacyData(block.getBlockData()); - return this.withBlockData(defaultData); - } - - @Override - public byte getBlockData() - { - return (byte) (combinedBlockId & 15); + return StateMaterialData.of(state); } - @Override - public int getBlockId() - { - return combinedBlockId >> 4; - } - - @Override - public String getName() - { - if(isBlank) - { - return "BLANK"; - } - else if(this.combinedBlockId == -1) - { - if(this.rawEntry != null) - { - return this.rawEntry; - } else { - return "Unknown"; - } - } else { - Block block = Block.getById(getBlockId()); - byte data = getBlockData(); - // Note that the above line is not equivalent to data != 0, as for - // example pumpkins have a default data value of 2 - - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial == null) - { - boolean nonDefaultData = block.toLegacyData(block.getBlockData()) != data; - - // Use Minecraft's name - if (nonDefaultData) - { - return Block.REGISTRY.b(block) + (!this.hasData ? "" : ":" + data); - } else { - return Block.REGISTRY.b(block).toString(); - } - } else { - return defaultMaterial.name() + (!this.hasData ? "" : ":" + data); - } - } - } - @SuppressWarnings("deprecation") - public IBlockData internalBlock() + public static StateMaterialData ofBukkitBlock(org.bukkit.block.Block block) { - return Block.getById(getBlockId()).fromLegacyData(getBlockData()); - } - - @Override - public boolean isMaterial(DefaultMaterial material) - { - return material.id == getBlockId(); + return ofMinecraftBlockState(block.getType().getId(), block.getData()); } + public abstract Block getBlock(); + + public abstract IBlockData getBlockState(); + @Override public boolean isLiquid() { - // For some reason, .isLiquid() appears to be - // really slow, so use defaultMaterial instead. - - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - return defaultMaterial.isLiquid(); - } - - return this.internalBlock().getMaterial().isLiquid(); + return this.getBlockState().getMaterial().isLiquid(); } @Override public boolean isSolid() { - // Let us override whether materials are solid - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - return defaultMaterial.isSolid(); - } - - return this.internalBlock().getMaterial().isSolid(); - } - - @Override - public boolean isEmptyOrAir() - { - return combinedBlockId == -1 || combinedBlockId == 0; + return this.getBlockState().getMaterial().isSolid() && this.getBlockState().r(); } - + @Override public boolean isAir() { - return combinedBlockId == 0; + return this.getBlock() == Blocks.AIR; } - + @Override - public boolean isEmpty() - { - return combinedBlockId == -1; - } - - @Override public boolean canFall() { - return Block.getById(getBlockId()) instanceof BlockFalling; + return this.getBlock() instanceof BlockFalling; } - - @Override - public boolean canSnowFallOn() - { - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - return defaultMaterial.canSnowFallOn(); - } - return this.internalBlock().getMaterial().isSolid(); - } - - // TODO: Caching result means fallbacks will only work for one world, fix this! - // TODO: Not returning a copy means that any block parsed is modified, this may - // unintentionally include things like MaterialHelper blocks. - // Redesign this, or at least make sure this doesn't cause problems. - // TODO: This is only applied for settings using a materialset and (non-bo) resources atm, - // fix this for bo's and any other material settings (do all use materialset?), will - // need a world-specific materials cache for readMaterial, should probably just parse - // materials immediately when reading settings, so pass world info to configs loading - // code? - @Override - public LocalMaterialData parseForWorld(LocalWorld world) - { - if (!this.checkedFallbacks && this.isEmpty() && this.rawEntry != null) - { - this.checkedFallbacks = true; - int newId; - try { - newId = ((BukkitMaterialData)world.getConfigs().getWorldConfig().parseFallback(this.rawEntry)).combinedBlockId; - } catch (NullPointerException e) { - OTG.log(LogMarker.ERROR, "Could not parse fallback for "+rawEntry); - throw e; - } - if(newId != this.combinedBlockId) - { - this.combinedBlockId = newId; - this.defaultMaterial = null; - } - } - return this; - } - - @Override - public DefaultMaterial toDefaultMaterial() - { - if(this.defaultMaterial == null && !parsedDefaultMaterial) - { - parsedDefaultMaterial = true; - if(this.combinedBlockId == -1) - { - this.defaultMaterial = null; - } else { - this.defaultMaterial = DefaultMaterial.getMaterial(getBlockId()); - } - } - return defaultMaterial; - } - @Override - public int hashCode() + public boolean isLeaves() { - // From 4096 to 69632 when there are 4096 block ids - return PluginStandardValues.SUPPORTED_BLOCK_IDS + combinedBlockId; + return this.getBlockState().getMaterial() == Material.LEAVES; } - + @Override - public boolean equals(Object obj) + public boolean isSmoothAreaAnchor(boolean allowWood, boolean ignoreWater) { - if (this == obj) - { - return true; - } - if (!(obj instanceof BukkitMaterialData)) - { - return false; - } - BukkitMaterialData other = (BukkitMaterialData) obj; - if(this.isBlank != other.isBlank) - { - return false; - } - else if(this.isBlank) - { - return true; - } - if (combinedBlockId != other.combinedBlockId) + if(this.isSolid()) { - return false; + return allowWood || this.getBlockState().getMaterial() != Material.WOOD; } - return true; + return !ignoreWater && this.getBlockState().getMaterial().isLiquid(); } - @Override - public boolean hasData() - { - return this.hasData; - } -} + @Override + public LocalMaterialData parseForWorld(LocalWorld world) + { + return this; + } +} \ No newline at end of file diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/StateMaterialData.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/StateMaterialData.java new file mode 100644 index 000000000..415b7cba5 --- /dev/null +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/StateMaterialData.java @@ -0,0 +1,128 @@ +package com.pg85.otg.bukkit.materials; + +import com.pg85.otg.util.bo3.Rotation; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.server.v1_12_R1.Block; +import net.minecraft.server.v1_12_R1.EnumBlockRotation; +import net.minecraft.server.v1_12_R1.IBlockData; + +class StateMaterialData extends BukkitMaterialData +{ + private static final Reference2ReferenceMap INSTANCES = new Reference2ReferenceOpenHashMap<>(); + private BlockMaterialData withoutBlockData; + private final IBlockData state; + private final byte blockData; + + private StateMaterialData(IBlockData state) + { + super(Block.getId(state.getBlock())); + this.state = state; + this.blockData = (byte) state.getBlock().toLegacyData(state); + } + + static StateMaterialData of(IBlockData state) + { + return INSTANCES.computeIfAbsent(state, StateMaterialData::new); + } + + @Override + public BlockMaterialData withoutBlockData() + { + BlockMaterialData withoutBlockData; + if((withoutBlockData = this.withoutBlockData) == null) + { + this.withoutBlockData = withoutBlockData = BlockMaterialData.of(this.getBlock()); + } + return withoutBlockData; + } + + @Override + public StateMaterialData withBlockData() + { + return this; + } + + @Override + public StateMaterialData withDefaultBlockData() + { + if(this.blockData == 0) + { + return this; + } + return BukkitMaterialData.ofMinecraftBlockState(this.getBlock().getBlockData()); + } + + @SuppressWarnings("deprecation") + @Override + public StateMaterialData withBlockData(int newData) + { + if(this.blockData == newData) + { + return this; + } + return BukkitMaterialData.ofMinecraftBlockState(this.getBlock().fromLegacyData(newData)); + } + + @Override + public String getName() + { + if(this.defaultMaterial != null) + { + return this.defaultMaterial.name() + ":" + this.blockData; + } + return Block.REGISTRY.b(this.getBlock()) + ":" + this.blockData; + } + + @Override + public Block getBlock() + { + return this.state.getBlock(); + } + + @Override + public IBlockData getBlockState() + { + return this.state; + } + + @Override + public byte getBlockData() + { + return this.blockData; + } + + @Override + public int getBlockDataMask() + { + return 1 << this.blockData; + } + + @Override + public StateMaterialData rotate(Rotation rotation) + { + IBlockData newState; + switch(rotation) + { + case NORTH: + return this; + case WEST: + newState = this.state.a(EnumBlockRotation.CLOCKWISE_90); + break; + case SOUTH: + newState = this.state.a(EnumBlockRotation.CLOCKWISE_180); + break; + case EAST: + newState = this.state.a(EnumBlockRotation.COUNTERCLOCKWISE_90); + break; + default: + throw new IllegalArgumentException(); + } + if(newState == this.state) + { + return this; + } + return BukkitMaterialData.ofMinecraftBlockState(newState); + } +} \ No newline at end of file diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/UnknownMaterialData.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/UnknownMaterialData.java new file mode 100644 index 000000000..72aecb768 --- /dev/null +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/materials/UnknownMaterialData.java @@ -0,0 +1,112 @@ +package com.pg85.otg.bukkit.materials; + +import com.pg85.otg.common.LocalMaterialData; +import com.pg85.otg.common.LocalWorld; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import net.minecraft.server.v1_12_R1.Block; +import net.minecraft.server.v1_12_R1.Blocks; +import net.minecraft.server.v1_12_R1.IBlockData; + +class UnknownMaterialData extends BukkitMaterialData +{ + private static final Object2ReferenceMap INSTANCES = new Object2ReferenceOpenHashMap<>(); + private final String raw; + private LocalMaterialData replacement; + + private UnknownMaterialData(String raw) + { + super(DefaultMaterial.AIR.id); + this.raw = raw; + } + + static UnknownMaterialData of(String raw) + { + return INSTANCES.computeIfAbsent(raw, UnknownMaterialData::new); + } + + @Override + public boolean isEmpty() + { + return true; + } + + @Override + public LocalMaterialData parseForWorld(LocalWorld world) + { + LocalMaterialData result; + if((result = this.replacement) == null) + { + result = world.getConfigs().getWorldConfig().parseFallback(this.raw); + if(result == null) + { + result = this; + } + this.replacement = result; + } + return result; + } + + @Override + public LocalMaterialData withoutBlockData() + { + return this; + } + + @Override + public LocalMaterialData withBlockData() + { + return this; + } + + @Override + public LocalMaterialData withDefaultBlockData() + { + return this; + } + + @Override + public LocalMaterialData withBlockData(int newData) + { + return this; + } + + @Override + public String getName() + { + return this.raw; + } + + @Override + public Block getBlock() + { + return Blocks.AIR; + } + + @Override + public IBlockData getBlockState() + { + return Blocks.AIR.getBlockData(); + } + + @Override + public byte getBlockData() + { + return 0; + } + + @Override + public int getBlockDataMask() + { + return 0; + } + + @Override + public LocalMaterialData rotate(Rotation rotation) + { + return this; + } +} diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO3Creator.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO3Creator.java index a9b5e4900..6b1229e66 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO3Creator.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO3Creator.java @@ -1,7 +1,6 @@ package com.pg85.otg.bukkit.util; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -37,7 +36,6 @@ public BO3Creator(String name) this.name = name; } - @SuppressWarnings("deprecation") public boolean create(Selection selection, String blockName, boolean branch) { int tileEntityCount = 1; @@ -94,7 +92,7 @@ public boolean create(Selection selection, String blockName, boolean branch) block = world.getBlockAt(x + start.getBlockX(), y + start.getBlockY(), z + start.getBlockZ()); data = BukkitMaterialData.ofBukkitBlock(block); - if (centerBlock != null && centerBlock.equals(data)) + if (centerBlock != null && centerBlock.matches(data)) { centerPointX = x + start.getBlockX(); centerPointY = y + start.getBlockY(); @@ -151,7 +149,6 @@ public boolean create(Selection selection, String blockName, boolean branch) NamedBinaryTag tag; String tileEntityName; File tileEntityFile; - FileOutputStream fos; for (int x = start.getBlockX(); x <= end.getBlockX(); x++) { @@ -216,13 +213,8 @@ public boolean create(Selection selection, String blockName, boolean branch) tileEntityCount++; try { - tileEntityFile.createNewFile(); - fos = new FileOutputStream(tileEntityFile); - tag.writeTo(fos); - fos.flush(); - fos.close(); - blockFunction.metaDataTag = tag; - blockFunction.metaDataName = name + "/" + tileEntityName; + tag.writeTo(tileEntityFile.toPath()); + blockFunction.blockContainer = material.blockContainer(tileEntitiesFolder.getParentFile().toPath(), name + "/" + tileEntityName); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO4Creator.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO4Creator.java index 94d48b5c7..956501f4c 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO4Creator.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/BO4Creator.java @@ -22,7 +22,6 @@ import org.bukkit.block.Block; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -89,7 +88,7 @@ public boolean create(Selection selection, String blockName, boolean branch) data = BukkitMaterialData.ofBukkitBlock(block); // If we have a match for the center block, update values and end the loop - if (centerBlock.equals(data)) { + if (centerBlock.matches(data)) { centerPointX = x + start.getBlockX(); centerPointY = y + start.getBlockY(); centerPointZ = z + start.getBlockZ(); @@ -119,7 +118,6 @@ public boolean create(Selection selection, String blockName, boolean branch) NamedBinaryTag tag; String tileEntityName; File tileEntityFile; - FileOutputStream fos; for (int x = start.getBlockX(); x <= end.getBlockX(); x++) { @@ -182,13 +180,8 @@ public boolean create(Selection selection, String blockName, boolean branch) tileEntityCount++; try { - tileEntityFile.createNewFile(); - fos = new FileOutputStream(tileEntityFile); - tag.writeTo(fos); - fos.flush(); - fos.close(); - blockFunction.metaDataTag = tag; - blockFunction.metaDataName = name + "/" + tileEntityName; + tag.writeTo(tileEntityFile.toPath()); + blockFunction.blockContainer = material.blockContainer(tileEntitiesFolder.getParentFile().toPath(), name + "/" + tileEntityName); } catch (IOException e) { throw new RuntimeException(e); diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/NBTHelper.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/NBTHelper.java index 866b957a1..dddc7313d 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/NBTHelper.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/util/NBTHelper.java @@ -21,6 +21,7 @@ import net.minecraft.server.v1_12_R1.NBTTagIntArray; import net.minecraft.server.v1_12_R1.NBTTagList; import net.minecraft.server.v1_12_R1.NBTTagLong; +import net.minecraft.server.v1_12_R1.NBTTagLongArray; import net.minecraft.server.v1_12_R1.NBTTagShort; import net.minecraft.server.v1_12_R1.NBTTagString; import net.minecraft.server.v1_12_R1.TileEntity; @@ -75,6 +76,7 @@ public static NamedBinaryTag getNBTFromNMSTagCompound(String name, NBTTagCompoun case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: compoundTag.addTag(new NamedBinaryTag(type, entry.getKey(), getValueFromNms(nmsChildTag))); break; case TAG_List: @@ -177,6 +179,9 @@ private static Object getValueFromNms(NBTBase nmsTag) { case TAG_Int_Array: int[] theIntArray = ((NBTTagIntArray) nmsTag).d(); return theIntArray; + case TAG_Long_Array: + long[] theLongArray = new long[0]; + return theLongArray; default: // Cannot read this from a tag throw new IllegalArgumentException(type + "doesn't have a simple value!"); @@ -206,6 +211,7 @@ public static NBTTagCompound getNMSFromNBTTagCompound(NamedBinaryTag compoundTag case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: nmsTag.set(tag.getName(), createTagNms(tag.getType(), tag.getValue())); break; case TAG_List: @@ -244,6 +250,7 @@ private static NBTTagList getNMSFromNBTTagList(NamedBinaryTag listTag) { case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: nmsTag.add(createTagNms(tag.getType(), tag.getValue())); break; case TAG_List: @@ -323,6 +330,8 @@ private static NBTBase createTagNms(NamedBinaryTag.Type type, Object value) { return new NBTTagString((String) value); case TAG_Int_Array: return new NBTTagIntArray((int[]) value); + case TAG_Long_Array: + return new NBTTagLongArray((long[]) value); default: // Cannot make this into a tag throw new IllegalArgumentException(type + "doesn't have a simple value!"); diff --git a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/world/BukkitWorld.java b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/world/BukkitWorld.java index 3be868271..000905866 100644 --- a/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/world/BukkitWorld.java +++ b/platforms/bukkit/src/main/java/com/pg85/otg/bukkit/world/BukkitWorld.java @@ -1,7 +1,7 @@ package com.pg85.otg.bukkit.world; import java.io.File; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; @@ -647,54 +647,47 @@ public boolean chunkHasDefaultStructure(Random rand, ChunkCoordinate chunkCoord) (worldConfig.woodLandMansionsEnabled && isStructureInRadius(chunkCoord, this.woodLandMansionGen, 4)) ; } - - static Method canSpawnStructureAtCoordsMethod; - public boolean isStructureInRadius(ChunkCoordinate startChunk, StructureGenerator structure, int radiusInChunks) - { - if(canSpawnStructureAtCoordsMethod == null) + + private static final Field worldField; + private static final Method canSpawnStructureAtCoordsMethod; + static + { + try { - try - { - canSpawnStructureAtCoordsMethod = StructureGenerator.class.getDeclaredMethod("a", int.class, int.class); - canSpawnStructureAtCoordsMethod.setAccessible(true); - } catch (NoSuchMethodException | SecurityException e) { - OTG.log(LogMarker.ERROR, "Error, could not reflect canSpawnStructureAtCoords, BO4's may not be able to detect default/modded structures. OTG may not fully support your Spigot/Bukkit version."); - e.printStackTrace(); - } + worldField = WorldGenBase.class.getDeclaredField("g"); + worldField.setAccessible(true); + canSpawnStructureAtCoordsMethod = StructureGenerator.class.getDeclaredMethod("a", int.class, int.class); + canSpawnStructureAtCoordsMethod.setAccessible(true); } - - int chunkX = startChunk.getChunkX(); - int chunkZ = startChunk.getChunkZ(); - for (int cycle = 0; cycle <= radiusInChunks; ++cycle) + catch(ReflectiveOperationException e) + { + throw new UnsupportedOperationException(e); + } + } + + public boolean isStructureInRadius(ChunkCoordinate startChunk, StructureGenerator structure, int radiusInChunks) + { + try { - for (int xRadius = -cycle; xRadius <= cycle; ++xRadius) + worldField.set(structure, this.world); + for(int x = -radiusInChunks; x <= radiusInChunks; x++) { - for (int zRadius = -cycle; zRadius <= cycle; ++zRadius) + for(int z = -radiusInChunks; z <= radiusInChunks; z++) { - int distance = (int)Math.floor(Math.sqrt(Math.pow (chunkX-chunkX + xRadius, 2) + Math.pow (chunkZ-chunkZ + zRadius, 2))); - if (distance == cycle) + if((boolean) canSpawnStructureAtCoordsMethod.invoke(structure, startChunk.getChunkX() + x, startChunk.getChunkZ() + z)) { - boolean canSpawnStructureAtCoords = false; - try - { - canSpawnStructureAtCoords = (boolean) canSpawnStructureAtCoordsMethod.invoke(structure, chunkX + xRadius, chunkZ + zRadius); - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) - { - OTG.log(LogMarker.ERROR, "Error, could not reflect canSpawnStructureAtCoords, BO4's may not be able to detect default/modded structures. OTG may not fully support your Spigot/Bukkit version."); - e.printStackTrace(); - } - if(canSpawnStructureAtCoords) - { - return true; - } + return true; } } } + return false; + } + catch(ReflectiveOperationException e) + { + throw new UnsupportedOperationException(e); } - return false; } - + @Override public boolean placeDefaultStructures(Random random, ChunkCoordinate chunkCoord) { @@ -837,7 +830,7 @@ private void replaceBlocks(Chunk rawChunk) } if(instruction.getFrom().getBlockId() == blockId) { - section.setType(sectionX, sectionY, sectionZ, ((BukkitMaterialData)instruction.getTo()).internalBlock()); + section.setType(sectionX, sectionY, sectionZ, ((BukkitMaterialData)instruction.getTo()).getBlockState()); } } } @@ -1250,9 +1243,9 @@ public void spawnEntity(EntityFunction entityData, ChunkCoordinate chunkBeing { if(OTG.getPluginConfig().spawnLog) { - OTG.log(LogMarker.DEBUG, "Attempting to spawn BO3 Entity() " + entityData.groupSize + " x " + entityData.name + " at " + entityData.x + " " + entityData.y + " " + entityData.z); + OTG.log(LogMarker.DEBUG, "Attempting to spawn BO3 Entity() " + entityData.groupSize + " x " + entityData.name + " at " + entityData.x() + " " + entityData.y() + " " + entityData.z()); } - if (chunkBeingPopulated != null && !OTG.IsInAreaBeingPopulated((int) Math.floor(entityData.x), (int) Math.floor(entityData.z), chunkBeingPopulated)) { + if (chunkBeingPopulated != null && !OTG.IsInAreaBeingPopulated((int) Math.floor(entityData.x()), (int) Math.floor(entityData.z()), chunkBeingPopulated)) { // If outside area being populated, abort and remove entity if(OTG.getPluginConfig().spawnLog) { @@ -1260,7 +1253,7 @@ public void spawnEntity(EntityFunction entityData, ChunkCoordinate chunkBeing } return; } - if (entityData.y < 0 || entityData.y >= 256) { + if (entityData.y() < 0 || entityData.y() >= 256) { if(OTG.getPluginConfig().spawnLog) { OTG.log(LogMarker.ERROR, "Failed to spawn mob "+entityData.name +", spawn position out of bounds"); @@ -1274,7 +1267,7 @@ public void spawnEntity(EntityFunction entityData, ChunkCoordinate chunkBeing if(entity == null) return; // If either the block is a full block, or entity is a fish out of water, then we cancel - org.bukkit.Material material = world.getWorld().getBlockAt(new Location(world.getWorld(), entityData.x, entityData.y, entityData.z)).getType(); + org.bukkit.Material material = world.getWorld().getBlockAt(new Location(world.getWorld(), entityData.x(), entityData.y(), entityData.z())).getType(); if (!material.isTransparent() || material.isSolid() || ((entity.getBukkitEntity() instanceof CraftGuardian || EnumCreatureType.WATER_CREATURE.a().isAssignableFrom(entity.getClass()) && (material != org.bukkit.Material.WATER && material != org.bukkit.Material.STATIONARY_WATER)))) @@ -1359,12 +1352,12 @@ else if (entityData.nameTagOrNBTFileName.toLowerCase().trim().endsWith(".nbt")) list.a(0, new NBTTagFloat((f+ ((2 - entityData.rotation) % 4)*90) % 360)); } // Spawn entity, with potential passengers - entity = ChunkRegionLoader.spawnEntity(nbttagcompound, world, entityData.x+0.5, entityData.y, entityData.z+0.5, true, CreatureSpawnEvent.SpawnReason.CUSTOM); + entity = ChunkRegionLoader.spawnEntity(nbttagcompound, world, entityData.x()+0.5, entityData.y(), entityData.z()+0.5, true, CreatureSpawnEvent.SpawnReason.CUSTOM); if (entity == null) return null; } else { try { - org.bukkit.entity.Entity e = world.getWorld().spawn(new Location(world.getWorld(), entityData.x+0.5, entityData.y+0.0, entityData.z+0.5), EntityType.fromName(entityData.name).getEntityClass()); + org.bukkit.entity.Entity e = world.getWorld().spawn(new Location(world.getWorld(), entityData.x()+0.5, entityData.y()+0.0, entityData.z()+0.5), EntityType.fromName(entityData.name).getEntityClass()); //entity = (Entity) EntityType.fromName(entityData.name).getEntityClass().getConstructor(new Class[] {World.class}).newInstance(world); entity = world.getEntity( e.getUniqueId()); } catch (Exception e) { @@ -1400,46 +1393,30 @@ public boolean isInsidePregeneratedRegion(ChunkCoordinate chunk) @Override public LocalMaterialData getMaterial(int x, int y, int z, ChunkCoordinate chunkBeingPopulated) { - if (y >= PluginStandardValues.WORLD_HEIGHT || y < PluginStandardValues.WORLD_DEPTH) + if(y >= PluginStandardValues.WORLD_HEIGHT || y < PluginStandardValues.WORLD_DEPTH) { - return null; + return BukkitMaterialData.AIR; } - // If the chunk exists or is inside the area being populated, fetch it normally. - Chunk chunk = null; - if( - (chunkBeingPopulated != null && OTG.IsInAreaBeingPopulated(x, z, chunkBeingPopulated)) - //|| getChunkGenerator().chunkExists(x, z) - ) - { - chunk = getChunkGenerator().getChunk(x, z); - } - - // If the chunk doesn't exist and we're doing something outside the - // population sequence, return the material without loading the chunk. - if(chunk == null && chunkBeingPopulated == null) - { - ChunkCoordinate chunkCoord = ChunkCoordinate.fromBlockCoords(x, z); - // If the chunk has already been loaded, no need to use fake chunks. - if(world.getChunkProviderServer().isLoaded(chunkCoord.getChunkX(), chunkCoord.getChunkZ())) - { - chunk = getChunkGenerator().getChunk(x, z); - } else { - // Calculate the material without loading the chunk. - return getChunkGenerator().getMaterialInUnloadedChunk(x,y,z); - } - } - - // Tried to query an unloaded chunk outside the area being populated - if(chunk == null) - { - return null; - } - - // Get internal coordinates for block in chunk - int internalX = x & 0xF; - int internalZ = z & 0xF; - return BukkitMaterialData.ofMinecraftBlockData(chunk.a(internalX, y, internalZ)); + Chunk chunk; + if(chunkBeingPopulated != null) + { + if(!OTG.IsInAreaBeingPopulated(x, z, chunkBeingPopulated)) + { + return BukkitMaterialData.AIR; + } + chunk = this.world.getChunkAt(x >> 4, z >> 4); + } + else + { + chunk = this.world.getChunkProvider().getLoadedChunkAt(x >> 4, z >> 4); + if(chunk == null) + { + return this.generator.getMaterialInUnloadedChunk(x, y, z); + } + } + + return BukkitMaterialData.ofMinecraftBlockState(chunk.a(x & 15, y, z & 15)); } @Override @@ -1538,7 +1515,7 @@ public int getHighestBlockYAt(int x, int z, boolean findSolid, boolean findLiqui { blockData = chunk.getBlockData(new BlockPosition(internalX, i, internalZ)); block = blockData.getBlock(); - material = BukkitMaterialData.ofMinecraftBlockData(blockData); + material = BukkitMaterialData.ofMinecraftBlockState(blockData); isLiquid = material.isLiquid(); isSolid = ( @@ -1733,4 +1710,14 @@ public void updateSpawnPointY() { // TODO: Implement this for spigot. } + + @Override + public boolean canPlaceSnowAt(int x, int y, int z, ChunkCoordinate chunkBeingPopulated) + { + if(chunkBeingPopulated != null && OTG.IsInAreaBeingPopulated(x, z, chunkBeingPopulated)) + { + return false; + } + return Blocks.SNOW_LAYER.canPlace(world, new BlockPosition(x, y, z)); + } } \ No newline at end of file diff --git a/platforms/forge/build.gradle b/platforms/forge/build.gradle index 9534f5238..a7a9d1e17 100644 --- a/platforms/forge/build.gradle +++ b/platforms/forge/build.gradle @@ -8,17 +8,26 @@ minecraft { mappings channel: "${forge_mappings_channel}", version: "${forge_mappings_version}" accessTransformer = file("src/main/resources/META-INF/${forge_access_transformers}") -} -repositories { - maven { url = 'https://maven.enginehub.org/artifactory/repo/' } // worldedit + runs { + client { + taskName "runClient ${mod_name} ${minecraft_version}" + environment 'MC_VERSION', "${minecraft_version}" + property 'fml.coreMods.load', "${forge_plugin_class}" + } + + server { + taskName "runServer ${mod_name} ${minecraft_version}" + environment 'MC_VERSION', "${minecraft_version}" + property 'fml.coreMods.load', "${forge_plugin_class}" + } + } } dependencies { minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" implementation project(':common') - compileOnly "com.sk89q.worldedit:worldedit-forge-mc1.11:${worldedit_forge_version}" - compileOnly "com.sk89q.worldedit:worldedit-core:${worldedit_core_version}" + implementation "curse.maven:worldedit-225608:${worldedit_forge_version}" } processResources { diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/OTGPlugin.java b/platforms/forge/src/main/java/com/pg85/otg/forge/OTGPlugin.java index 36a7653cd..f65e245de 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/OTGPlugin.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/OTGPlugin.java @@ -114,9 +114,6 @@ public void load(FMLInitializationEvent event) // Register fog event handler for biome-specific fog color MinecraftForge.EVENT_BUS.register(new ClientFogHandler()); - - // Register fog event handler for biome-specific fog color - MinecraftForge.EVENT_BUS.register(new ChunkListener()); // Register gui handler for replacing MC's gui with OTG's MinecraftForge.EVENT_BUS.register(new GuiHandler()); diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/blocks/portal/OTGBlockPortalSize.java b/platforms/forge/src/main/java/com/pg85/otg/forge/blocks/portal/OTGBlockPortalSize.java index 55d2c3e14..8c9a59827 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/blocks/portal/OTGBlockPortalSize.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/blocks/portal/OTGBlockPortalSize.java @@ -208,7 +208,7 @@ private int getDistanceUntilEdge(ArrayList portalMaterials, B { for(LocalMaterialData portalMaterial : portalMaterials) { - if(material.equals(portalMaterial)) + if(portalMaterial.matches(material)) { isPortalMaterial = true; } @@ -276,7 +276,7 @@ private int calculatePortalHeight(ArrayList portalMaterials) boolean isPortalMaterial = false; for(LocalMaterialData portalMaterial : portalMaterials) { - if(material.equals(portalMaterial)) + if(portalMaterial.matches(material)) { isPortalMaterial = true; } diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/EntitiesCommand.java b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/EntitiesCommand.java index 977fe5a59..416a9eb8e 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/EntitiesCommand.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/EntitiesCommand.java @@ -44,7 +44,7 @@ public boolean onCommand(ICommandSender sender, List args) msg += VALUE_COLOR + " (" + enumcreaturetype.name() + ")"; } } - OTG.log(LogMarker.INFO, msg.replace("§2", "").replace("§", "").replace("§a", "")); + OTG.log(LogMarker.INFO, msg.replace("§2", "").replace("§", "").replace("§a", "")); sender.sendMessage(new TextComponentString(MESSAGE_COLOR + "- " + msg)); } else { // This can happen for LIGHTNING_BOLT since it appears to be added to the diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/FlushCommand.java b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/FlushCommand.java index f17f26b4e..a7f7529e8 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/FlushCommand.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/FlushCommand.java @@ -3,7 +3,6 @@ import java.util.List; import com.pg85.otg.OTG; -import com.pg85.otg.forge.world.ForgeWorld; import com.pg85.otg.logging.LogMarker; import net.minecraft.command.ICommandSender; @@ -21,13 +20,9 @@ public class FlushCommand extends BaseCommand @Override public boolean onCommand(ICommandSender sender, List args) { - ForgeWorld world = (ForgeWorld) this.getWorld(sender, ""); - OTG.log(LogMarker.INFO, "Unloading BO2/BO3/BO4 files"); OTG.getEngine().getCustomObjectManager().reloadCustomObjectFiles(); sender.sendMessage(new TextComponentString("Objects unloaded.")); - OTG.log(LogMarker.INFO, "Clearing chunkgenerator cache"); - world.getChunkGenerator().clearChunkCache(); OTG.log(LogMarker.INFO, "Caches cleared."); return true; } diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/OTGCommandHandler.java b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/OTGCommandHandler.java index 998315f70..8e9962eb7 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/OTGCommandHandler.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/OTGCommandHandler.java @@ -3,8 +3,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.stream.Collectors; import net.minecraft.command.ICommand; import net.minecraft.command.ICommandSender; @@ -113,16 +115,13 @@ public List getTabCompletions(MinecraftServer server, ICommandSender sen { if (args.length == 1) { - List commands = new ArrayList(); - - for (BaseCommand command : commandHashMap.values()) - { - commands.add(command.name); - } - - return commands; + return commandHashMap.keySet() + .stream() + .filter(name -> name.startsWith(args[0])) + .sorted() + .collect(Collectors.toList()); } - return new ArrayList(); + return Collections.emptyList(); } @Override diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/ParticlesCommand.java b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/ParticlesCommand.java index 5e2439a2f..27e5282b2 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/commands/ParticlesCommand.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/commands/ParticlesCommand.java @@ -28,7 +28,7 @@ public boolean onCommand(ICommandSender sender, List args) for (String entry : EnumParticleTypes.getParticleNames()) { String msg = entry; - OTG.log(LogMarker.INFO, msg.replace("§2", "").replace("§", "").replace("§a", "")); + OTG.log(LogMarker.INFO, msg.replace("§2", "").replace("§", "").replace("§a", "")); sender.sendMessage(new TextComponentString(MESSAGE_COLOR + "- " + msg)); } diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGTeleporter.java b/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGTeleporter.java index fc25884a8..b0cc3580e 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGTeleporter.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGTeleporter.java @@ -834,7 +834,7 @@ private static boolean makePortal(ForgeMaterialData portalMaterial, Block portal int k10 = k2 + k8; int k11 = k6 + (l7 - 1) * i3 - j7 * l6; boolean flag = k8 < 0; - destinationWorld.setBlockState(new BlockPos(k9, k10, k11), flag ? portalMaterial.internalBlock() : Blocks.AIR.getDefaultState()); + destinationWorld.setBlockState(new BlockPos(k9, k10, k11), flag ? portalMaterial.getBlockState() : Blocks.AIR.getDefaultState()); } } } @@ -852,7 +852,7 @@ private static boolean makePortal(ForgeMaterialData portalMaterial, Block portal int l11 = k2 + l9; int k12 = k6 + (l8 - 1) * i3; boolean flag1 = l8 == 0 || l8 == 3 || l9 == -1 || l9 == 3; - destinationWorld.setBlockState(new BlockPos(l10, l11, k12), flag1 ? portalMaterial.internalBlock() : iblockstate, 2); + destinationWorld.setBlockState(new BlockPos(l10, l11, k12), flag1 ? portalMaterial.getBlockState() : iblockstate, 2); } } } diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGWorldProvider.java b/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGWorldProvider.java index 9a6487495..6ec753b59 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGWorldProvider.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/dimensions/OTGWorldProvider.java @@ -1,7 +1,5 @@ package com.pg85.otg.forge.dimensions; -import java.util.Random; - import com.pg85.otg.OTG; import com.pg85.otg.configuration.dimensions.DimensionConfig; import com.pg85.otg.configuration.dimensions.DimensionsConfig; @@ -164,29 +162,31 @@ public boolean canCoordinateBeSpawn(int x, int z) @Override public BlockPos getRandomizedSpawnPoint() { - DimensionConfig dimConfig = getDimensionConfig(); - if(dimConfig != null && dimConfig.Settings.SpawnPointSet) - { - int randX = 0, randZ = 0; - // Calculate random spawn position within a circular radius around the spawn coords - if (dimConfig.GameRules.SpawnRadius != 0) + DimensionConfig dimConfig = getDimensionConfig(); + if(dimConfig != null && dimConfig.Settings.SpawnPointSet) + { + int x = 0; + int z = 0; + // Calculate random spawn position within a circular radius around the spawn coords + if(dimConfig.GameRules.SpawnRadius != 0) { - Random rand = new Random(); - randX = -dimConfig.GameRules.SpawnRadius + rand.nextInt(dimConfig.GameRules.SpawnRadius * 2); - // MaxZ = sqrt(spawnradius^2 - randX^2) - int maxZ = (int)Math.floor(Math.sqrt((dimConfig.GameRules.SpawnRadius * dimConfig.GameRules.SpawnRadius) - (randX * randX))); - randZ = maxZ == 0 ? 0 : -maxZ + rand.nextInt(maxZ * 2); + int radius = dimConfig.GameRules.SpawnRadius; + do + { + x = -radius + this.world.rand.nextInt(radius * 2 + 1); + z = -radius + this.world.rand.nextInt(radius * 2 + 1); + } + while(x * x + z * z > radius * radius); } - int SpawnPosX = (this.world.getWorldInfo().getSpawnX() + randX); - int SpawnPosZ = (this.world.getWorldInfo().getSpawnZ() + randZ); + x += this.world.getWorldInfo().getSpawnX(); + z += this.world.getWorldInfo().getSpawnZ(); - BlockPos SpawnPos = new BlockPos(SpawnPosX, 0, SpawnPosZ); - BlockPos SpawnPosNew = world.getTopSolidOrLiquidBlock(SpawnPos); - - return new BlockPos(SpawnPosNew); - } else { - return super.getRandomizedSpawnPoint(); - } + return world.getTopSolidOrLiquidBlock(new BlockPos(x, 0, z)).toImmutable(); + } + else + { + return super.getRandomizedSpawnPoint(); + } } // True if the player can respawn in this dimension (true = overworld, false = nether). diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/events/ChunkListener.java b/platforms/forge/src/main/java/com/pg85/otg/forge/events/ChunkListener.java deleted file mode 100644 index efce471fb..000000000 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/events/ChunkListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.pg85.otg.forge.events; - -import com.pg85.otg.OTG; -import com.pg85.otg.forge.ForgeEngine; -import com.pg85.otg.forge.world.ForgeWorld; -import com.pg85.otg.util.ChunkCoordinate; - -import net.minecraftforge.event.world.ChunkEvent; -import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; - -public class ChunkListener -{ - @SubscribeEvent - public void onChunkUnload(ChunkEvent.Unload unloadEvent) - { - ForgeWorld forgeWorld = ((ForgeEngine)OTG.getEngine()).getWorld(unloadEvent.getWorld()); - if(forgeWorld != null && forgeWorld.getChunkGenerator() != null && unloadEvent.getChunk() != null && unloadEvent.getChunk().getPos() != null) - { - forgeWorld.getChunkGenerator().clearChunkFromCache(ChunkCoordinate.fromChunkCoords(unloadEvent.getChunk().getPos().x, unloadEvent.getChunk().getPos().z)); - } - } -} diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/events/client/ClientTickHandler.java b/platforms/forge/src/main/java/com/pg85/otg/forge/events/client/ClientTickHandler.java index ebda12186..79ab8cc6d 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/events/client/ClientTickHandler.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/events/client/ClientTickHandler.java @@ -35,9 +35,9 @@ public void onClientTick(ClientTickEvent event) lastSpawnedTimeIn100Ms = currentTimeIn100Ms; for(ParticleFunction particleData : particleFunctions) { - double x = (double)particleData.x + 0.5F; - double y = (double)particleData.y; - double z = (double)particleData.z + 0.5F; + double x = (double)particleData.x() + 0.5F; + double y = (double)particleData.y(); + double z = (double)particleData.z() + 0.5F; String particleName = particleData.particleName; double interval = particleData.interval * 10; diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/events/dimensions/EntityTravelToDimensionListener.java b/platforms/forge/src/main/java/com/pg85/otg/forge/events/dimensions/EntityTravelToDimensionListener.java index 11e7f407c..c4379d3ad 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/events/dimensions/EntityTravelToDimensionListener.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/events/dimensions/EntityTravelToDimensionListener.java @@ -81,7 +81,7 @@ public void entityTravelToDimension(EntityTravelToDimensionEvent e) boolean bIsPortalMaterial = false; for(LocalMaterialData portalMaterial : portalMaterials) { - if(playerPortalMaterial.equals(portalMaterial)) + if(portalMaterial.matches(playerPortalMaterial)) { bIsPortalMaterial = true; bOTGPortalFound = true; diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/events/server/ServerTickHandler.java b/platforms/forge/src/main/java/com/pg85/otg/forge/events/server/ServerTickHandler.java index f6121b602..c2ab1c74d 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/events/server/ServerTickHandler.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/events/server/ServerTickHandler.java @@ -138,7 +138,7 @@ public void onServerTick(ServerTickEvent event) { for(ModDataFunction modData : modNameAndData.getValue()) { - messageString += "[" + modData.x + "," + modData.y + "," + modData.z + "," + modData.modData + "]"; + messageString += "[" + modData.x() + "," + modData.y() + "," + modData.z() + "," + modData.modData + "]"; } } } @@ -208,14 +208,14 @@ public void onServerTick(ServerTickEvent event) } else { entityFunc = new BO3EntityFunction(); } - entityFunc.x = modDataBlockX; - entityFunc.y = modDataBlockY; - entityFunc.z = modDataBlockZ; + entityFunc.x(modDataBlockX); + entityFunc.y(modDataBlockY); + entityFunc.z(modDataBlockZ); entityFunc.processEntityName(paramString2[1]); entityFunc.groupSize = paramString2.length > 2 ? Integer.parseInt(paramString2[2]) : 1; if (paramString2.length > 5) - entityFunc.processNameTagOrFileName(paramString2[5]); + entityFunc.processNameTagOrFileName(null, paramString2[5]); else { entityFunc.nameTagOrNBTFileName = null; @@ -366,9 +366,9 @@ private void findChunksForSpawning(ForgeWorld world, boolean spawnHostileMobs, b for (int a = 0; a < playerCoords.size(); ++a) { - float f = (float)((Double)playerCoords.get(a)[0] - spawnerData.x); - float f1 = (float)((Double)playerCoords.get(a)[1] - spawnerData.y); - float f2 = (float)((Double)playerCoords.get(a)[2] - spawnerData.z); + float f = (float)((Double)playerCoords.get(a)[0] - spawnerData.x()); + float f1 = (float)((Double)playerCoords.get(a)[1] - spawnerData.y()); + float f2 = (float)((Double)playerCoords.get(a)[2] - spawnerData.z()); double distance = f * f + f1 * f1 + f2 * f2; if(distance < distToClosestPlayer) @@ -493,12 +493,12 @@ else if(r == spawnerDatasWithDistance.size() - 1) if (entity1.getClass() == entityClass && entity1.getEntityData().hasKey("OTG")) { if( - entity1.posX >= spawnerData.x - mobCountRadius && - entity1.posX <= spawnerData.x + mobCountRadius && - entity1.posY >= spawnerData.y - mobCountRadius && - entity1.posY <= spawnerData.y + mobCountRadius && - entity1.posZ >= spawnerData.z - mobCountRadius && - entity1.posZ <= spawnerData.z + mobCountRadius + entity1.posX >= spawnerData.x() - mobCountRadius && + entity1.posX <= spawnerData.x() + mobCountRadius && + entity1.posY >= spawnerData.y() - mobCountRadius && + entity1.posY <= spawnerData.y() + mobCountRadius && + entity1.posZ >= spawnerData.z() - mobCountRadius && + entity1.posZ <= spawnerData.z() + mobCountRadius ) { worldMobCount++; @@ -511,9 +511,9 @@ else if(r == spawnerDatasWithDistance.size() - 1) continue; } - int j1 = spawnerData.x; - int k1 = spawnerData.y; - int l1 = spawnerData.z; + int j1 = spawnerData.x(); + int k1 = spawnerData.y(); + int l1 = spawnerData.z(); float x = (float)j1 + 0.5F; float y = (float)k1; @@ -720,14 +720,14 @@ else if(r == spawnerDatasWithDistance.size() - 1) { for(ParticleFunction particleData : particleDataForOTG) { - float f = (float)((Double)playerCoords.get(a)[0] - particleData.x); - float f1 = (float)((Double)playerCoords.get(a)[1] - particleData.y); - float f2 = (float)((Double)playerCoords.get(a)[2] - particleData.z); + float f = (float)((Double)playerCoords.get(a)[0] - particleData.x()); + float f1 = (float)((Double)playerCoords.get(a)[1] - particleData.y()); + float f2 = (float)((Double)playerCoords.get(a)[2] - particleData.z()); double distance = f * f + f1 * f1 + f2 * f2; if(distance > 0 && distance < maxDistToClosestPlayerSq) { - if(!worldServer.getBlockState(new BlockPos(particleData.x, particleData.y, particleData.z)).getMaterial().isSolid()) + if(!worldServer.getBlockState(new BlockPos(particleData.x(), particleData.y(), particleData.z())).getMaterial().isSolid()) { particleDataForOTGPerPlayer.add(particleData); } else { diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/generator/ForgeChunkBuffer.java b/platforms/forge/src/main/java/com/pg85/otg/forge/generator/ForgeChunkBuffer.java index a36a092cf..f25f59816 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/generator/ForgeChunkBuffer.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/generator/ForgeChunkBuffer.java @@ -39,7 +39,7 @@ public ChunkCoordinate getChunkCoordinate() public void setBlock(int blockX, int blockY, int blockZ, LocalMaterialData material) { - this.chunkPrimer.setBlockState(blockX, blockY, blockZ, ((ForgeMaterialData) material).internalBlock()); + this.chunkPrimer.setBlockState(blockX, blockY, blockZ, ((ForgeMaterialData) material).getBlockState()); } public LocalMaterialData getBlock(int blockX, int blockY, int blockZ) diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/generator/OTGChunkGenerator.java b/platforms/forge/src/main/java/com/pg85/otg/forge/generator/OTGChunkGenerator.java index a3bb13124..6b875370d 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/generator/OTGChunkGenerator.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/generator/OTGChunkGenerator.java @@ -2,10 +2,10 @@ import static com.pg85.otg.util.ChunkCoordinate.CHUNK_SIZE; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Optional; import java.util.Map.Entry; import com.pg85.otg.OTG; @@ -25,14 +25,14 @@ import com.pg85.otg.network.ConfigProvider; import com.pg85.otg.util.BlockPos2D; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; import com.pg85.otg.util.bo3.NamedBinaryTag; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; import net.minecraft.block.BlockGravel; import net.minecraft.block.BlockSand; -import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EnumCreatureType; +import net.minecraft.init.Blocks; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.datafix.DataFixer; @@ -42,7 +42,9 @@ import net.minecraft.world.World; import net.minecraft.world.biome.Biome.SpawnListEntry; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import net.minecraft.world.gen.IChunkGenerator; +import net.minecraftforge.common.util.Constants.BlockFlags; import net.minecraftforge.fml.common.event.FMLInterModComms; public class OTGChunkGenerator implements IChunkGenerator @@ -53,15 +55,10 @@ public class OTGChunkGenerator implements IChunkGenerator public ObjectSpawner spawner; // Caches - private FifoMap unloadedBlockColumnsCache; - private FifoMap unloadedChunksCache; - private Entry lastUsedChunk1; - private Entry lastUsedChunk2; - private Entry lastUsedChunk3; - private Entry lastUsedChunk4; + private LRUCache unloadedBlockColumnsCache; + private LRUCache unloadedChunksCache; ForgeChunkBuffer chunkBuffer; Object chunkBufferLock = new Object(); - Object chunkCacheLock = new Object(); // private DataFixer dataFixer = DataFixesManager.createFixer(); @@ -76,57 +73,34 @@ public OTGChunkGenerator(ForgeWorld _world) this.spawner = new ObjectSpawner(this.world.getConfigs(), this.world); // TODO: Add a setting to the worldconfig for the size of these caches. // Worlds with lots of BO4's and large smoothing areas may want to increase this. - this.unloadedBlockColumnsCache = new FifoMap(1024); - this.unloadedChunksCache = new FifoMap(1024); //Changed 128 chunks cache to 1024 chunks cache for customstructures - lastUsedChunk1 = null; - lastUsedChunk2 = null; - lastUsedChunk3 = null; - lastUsedChunk4 = null; + this.unloadedBlockColumnsCache = new LRUCache(1024); + this.unloadedChunksCache = new LRUCache(1024); //Changed 128 chunks cache to 1024 chunks cache for customstructures } // Chunks - - // Called by /otg flush command to clear memory. - public void clearChunkCache() - { - synchronized(this.chunkCacheLock) - { - lastUsedChunk1 = null; - lastUsedChunk2 = null; - lastUsedChunk3 = null; - lastUsedChunk4 = null; - this.unloadedBlockColumnsCache.clear(); - this.unloadedChunksCache.clear(); - } - } - - public void clearChunkFromCache(ChunkCoordinate chunkCoordinate) - { - synchronized(this.chunkCacheLock) - { - if(lastUsedChunk1 != null && lastUsedChunk1.getKey().equals(chunkCoordinate)) - { - lastUsedChunk1 = null; - } - else if(lastUsedChunk2 != null && lastUsedChunk2.getKey().equals(chunkCoordinate)) - { - lastUsedChunk2 = null; - } - else if(lastUsedChunk3 != null && lastUsedChunk3.getKey().equals(chunkCoordinate)) - { - lastUsedChunk3 = null; - } - else if(lastUsedChunk4 != null && lastUsedChunk4.getKey().equals(chunkCoordinate)) - { - lastUsedChunk4 = null; - } - } - } @Override public Chunk generateChunk(int chunkX, int chunkZ) { - return getBlocks(chunkX, chunkZ, true); + Chunk chunk = this.generateRawChunk(chunkX, chunkZ); + fillBiomeArray(chunk); + chunk.generateSkylightMap(); + return chunk; + } + + private Chunk generateRawChunk(int chunkX, int chunkZ) + { + return this.unloadedChunksCache.computeIfAbsent(ChunkCoordinate.fromChunkCoords(chunkX, chunkZ), k -> { + Chunk v; + synchronized(chunkBufferLock) + { + chunkBuffer = new ForgeChunkBuffer(k); + this.chunkProviderOTG.generate(chunkBuffer); + v = chunkBuffer.toChunk(this.world.getWorld()); + chunkBuffer = null; + } + return v; + }); } @Override @@ -166,7 +140,7 @@ public void populate(int chunkX, int chunkZ) boolean autoSpawn = paramString2.length > 4 ? Boolean.parseBoolean(paramString2[4]) : false; if(autoSpawn) { - messageString += "[" + modData.x + "," + modData.y + "," + modData.z + "," + modData.modData + "]"; + messageString += "[" + modData.x() + "," + modData.y() + "," + modData.z() + "," + modData.modData + "]"; } } } @@ -174,7 +148,7 @@ public void populate(int chunkX, int chunkZ) } else { for(ModDataFunction modData : modNameAndData.getValue()) { - messageString += "[" + modData.x + "," + modData.y + "," + modData.z + "," + modData.modData + "]"; + messageString += "[" + modData.x() + "," + modData.y() + "," + modData.z() + "," + modData.modData + "]"; } } if(messageString.length() > 0) @@ -198,84 +172,11 @@ public void populate(int chunkX, int chunkZ) public Chunk getChunk(int x, int z) { - ChunkCoordinate chunkCoord = ChunkCoordinate.fromBlockCoords(x, z); - - Chunk chunk = null; - synchronized(this.chunkCacheLock) - { - if(lastUsedChunk1 != null && lastUsedChunk1.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk1.getValue(); - } - else if(lastUsedChunk2 != null && lastUsedChunk2.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk2.getValue(); - } - else if(lastUsedChunk3 != null && lastUsedChunk3.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk3.getValue(); - } - else if(lastUsedChunk4 != null && lastUsedChunk4.getKey().equals(chunkCoord)) - { - chunk = lastUsedChunk4.getValue(); - } - } - if(chunk == null) - { - // Try to fetch the chunk without populating it. - chunk = this.world.getWorld().getChunkProvider().getLoadedChunk(chunkCoord.getChunkX(), chunkCoord.getChunkZ()); - if(chunk == null) - { - // Request the chunk with a risk of it being populated. - chunk = this.world.getWorld().getChunk(chunkCoord.getChunkX(), chunkCoord.getChunkZ()); - } - if(chunk != null) - { - synchronized(this.chunkCacheLock) - { - lastUsedChunk4 = lastUsedChunk3; - lastUsedChunk3 = lastUsedChunk2; - lastUsedChunk2 = lastUsedChunk1; - lastUsedChunk1 = new AbstractMap.SimpleEntry(chunkCoord, chunk); - } - } - } - return chunk; + return this.world.world.getChunk(x >> 4, z >> 4); } // Blocks - private Chunk getBlocks(int chunkX, int chunkZ, boolean provideChunk) - { - Chunk chunk = unloadedChunksCache.get(ChunkCoordinate.fromChunkCoords(chunkX,chunkZ)); - if(chunk == null) - { - chunk = new Chunk(this.world.getWorld(), chunkX, chunkZ); - - ChunkCoordinate chunkCoord = ChunkCoordinate.fromChunkCoords(chunkX, chunkZ); - synchronized(chunkBufferLock) - { - chunkBuffer = new ForgeChunkBuffer(chunkCoord); - this.chunkProviderOTG.generate(chunkBuffer); - chunk = chunkBuffer.toChunk(this.world.getWorld()); - chunkBuffer = null; - } - fillBiomeArray(chunk); - //if(world.getConfigs().getWorldConfig().ModeTerrain == TerrainMode.TerrainTest) - //{ - chunk.generateSkylightMap(); // Normally chunks are lit in the ObjectSpawner after finishing their population step, TerrainTest skips the population step though so light blocks here. - //} - } else { - fillBiomeArray(chunk); - //if(world.getConfigs().getWorldConfig().ModeTerrain == TerrainMode.TerrainTest) - { - chunk.generateSkylightMap(); // Normally chunks are lit in the ObjectSpawner after finishing their population step, TerrainTest skips the population step though so light blocks here. - } - } - - return chunk; - } - /** * Fills the biome array of a chunk with the proper saved ids (no * generation ids). @@ -299,60 +200,32 @@ private void fillBiomeArray(Chunk chunk) public LocalMaterialData[] getBlockColumnInUnloadedChunk(int x, int z) { - BlockPos2D blockPos = new BlockPos2D(x, z); - ChunkCoordinate chunkCoord = ChunkCoordinate.fromBlockCoords(x, z); - int chunkX = chunkCoord.getChunkX(); - int chunkZ = chunkCoord.getChunkZ(); - - // Get internal coordinates for block in chunk - byte blockX = (byte)(x &= 0xF); - byte blockZ = (byte)(z &= 0xF); - - LocalMaterialData[] cachedColumn = this.unloadedBlockColumnsCache.get(blockPos); - - if(cachedColumn != null) - { - return cachedColumn; - } - - Chunk chunk = this.world.getWorld().getChunkProvider().getLoadedChunk(chunkX, chunkZ); - if(chunk == null) - { - chunk = this.unloadedChunksCache.get(chunkCoord); - } else { - this.unloadedChunksCache.remove(chunkCoord); - } - if(chunk == null) - { - // Generate a chunk without populating it - chunk = new Chunk(this.world.getWorld(), chunkX, chunkZ); - synchronized(chunkBufferLock) - { - chunkBuffer = new ForgeChunkBuffer(chunkCoord); - this.chunkProviderOTG.generate(chunkBuffer); - chunk = chunkBuffer.toChunk(this.world.getWorld()); - chunkBuffer = null; - } - unloadedChunksCache.put(chunkCoord, chunk); - } - - cachedColumn = new LocalMaterialData[256]; + Chunk chunk = this.world.world.getChunkProvider().getLoadedChunk(x >> 4, z >> 4); + if(chunk == null) + { + chunk = this.generateRawChunk(x >> 4, z >> 4); + } - LocalMaterialData[] blocksInColumn = new LocalMaterialData[256]; - IBlockState blockInChunk; - for(short y = 0; y < 256; y++) + LocalMaterialData[] blockColumn = new LocalMaterialData[256]; + int y = 0; + for(ExtendedBlockStorage section : chunk.getBlockStorageArray()) { - blockInChunk = chunk.getBlockState(new BlockPos(blockX, y, blockZ)); - if(blockInChunk != null) - { - blocksInColumn[y] = ForgeMaterialData.ofMinecraftBlockState(blockInChunk); - } else { - break; - } + if(section != null) + { + for(int i = 0; i < 16; i++) + { + blockColumn[y++] = ForgeMaterialData.ofMinecraftBlockState(section.get(x & 15, i, z & 15)); + } + } + else + { + for(int i = 0; i < 16; i++) + { + blockColumn[y++] = ForgeMaterialData.AIR; + } + } } - unloadedBlockColumnsCache.put(blockPos, cachedColumn); - - return blocksInColumn; + return blockColumn; } public double getBiomeBlocksNoiseValue(int blockX, int blockZ) @@ -394,76 +267,43 @@ public int getHighestBlockYInUnloadedChunk(int x, int z, boolean findSolid, bool } return height; } - + public void setBlock(int x, int y, int z, LocalMaterialData material, NamedBinaryTag metaDataTag) { - if (y < PluginStandardValues.WORLD_DEPTH || y >= PluginStandardValues.WORLD_HEIGHT) + if(y < PluginStandardValues.WORLD_DEPTH || y >= PluginStandardValues.WORLD_HEIGHT) { return; } - - IBlockState newState = ((ForgeMaterialData) material).internalBlock(); - + BlockPos pos = new BlockPos(x, y, z); - // Get chunk from (faster) custom cache - Chunk chunk = this.getChunk(x, z); - if (chunk == null) - { - throw new RuntimeException("Could not provide chunk."); - } - - // Disable nearby block physics - //IBlockState iblockstate = setBlockState(chunk, pos, newState); - - // Disable nearby block physics (except for tile entities) and set block - boolean oldCaptureBlockStates = this.world.getWorld().captureBlockSnapshots; - this.world.getWorld().captureBlockSnapshots = !(newState.getBlock().hasTileEntity(newState)); - //this.world.getWorld().captureBlockSnapshots = true; - IBlockState iblockstate = chunk.setBlockState(pos, newState); - this.world.getWorld().captureBlockSnapshots = oldCaptureBlockStates; - - if (iblockstate == null) + this.world.getWorld().setBlockState(pos, ((ForgeMaterialData) material).getBlockState(), BlockFlags.SEND_TO_CLIENTS | BlockFlags.NO_OBSERVERS); + + if(metaDataTag != null) { - return; // Happens when block to place is the same as block being placed? TODO: Is that the only time this happens? + TileEntity tileEntity = this.world.getWorld().getTileEntity(pos); + if(tileEntity != null) + { + NBTTagCompound nbtTag = NBTHelper.getNMSFromNBTTagCompound(metaDataTag); + nbtTag.setInteger("x", x); + nbtTag.setInteger("y", y); + nbtTag.setInteger("z", z); + // Update to current Minecraft format (maybe we want to do this at + // server startup instead, and then save the result?) + // TODO: Use datawalker instead + nbtTag = this.dataFixer.process(FixTypes.BLOCK_ENTITY, nbtTag); + tileEntity.readFromNBT(nbtTag); + } + else + { + if(OTG.getPluginConfig().spawnLog) + { + OTG.log(LogMarker.WARN, "Skipping tile entity with id {}, cannot be placed at {},{},{}", Optional.ofNullable(metaDataTag.getTag("id")).map(NamedBinaryTag::getValue).orElse(null), x, y, z); + } + } } - - if (metaDataTag != null) - { - attachMetadata(x, y, z, metaDataTag); - } - - // Notify world: (2 | 16) == update client, don't update observers - this.world.getWorld().markAndNotifyBlock(pos, chunk, iblockstate, newState, 2 | 16); } - private void attachMetadata(int x, int y, int z, NamedBinaryTag tag) - { - // Convert Tag to a native nms tag - NBTTagCompound nmsTag = NBTHelper.getNMSFromNBTTagCompound(tag); - // Add the x, y and z position to it - nmsTag.setInteger("x", x); - nmsTag.setInteger("y", y); - nmsTag.setInteger("z", z); - // Update to current Minecraft format (maybe we want to do this at - // server startup instead, and then save the result?) - // TODO: Use datawalker instead - //nmsTag = this.dataFixer.process(FixTypes.BLOCK_ENTITY, nmsTag, -1); - nmsTag = this.dataFixer.process(FixTypes.BLOCK_ENTITY, nmsTag); - - // Add that data to the current tile entity in the world - TileEntity tileEntity = this.world.getWorld().getTileEntity(new BlockPos(x, y, z)); - if (tileEntity != null) - { - tileEntity.readFromNBT(nmsTag); - } else { - if(OTG.getPluginConfig().spawnLog) - { - OTG.log(LogMarker.WARN, "Skipping tile entity with id {}, cannot be placed at {},{},{}", nmsTag.getString("id"), x, y, z); - } - } - } - // Structures @Override @@ -499,7 +339,7 @@ public int getHighestBlockInCurrentlyPopulatingChunk(int x, int z) for(int i = PluginStandardValues.WORLD_HEIGHT - 1; i > PluginStandardValues.WORLD_DEPTH; i--) { material = chunkBuffer.getBlock(x, i, z); - if(material != null && !material.isEmptyOrAir()) + if(material != null && !material.isAir()) { return i; }; diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/generator/structure/OTGMineshaftGen.java b/platforms/forge/src/main/java/com/pg85/otg/forge/generator/structure/OTGMineshaftGen.java index e0a75f675..a6029e571 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/generator/structure/OTGMineshaftGen.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/generator/structure/OTGMineshaftGen.java @@ -8,7 +8,7 @@ import com.pg85.otg.forge.ForgeEngine; import com.pg85.otg.forge.world.ForgeWorld; import com.pg85.otg.util.ChunkCoordinate; -import com.pg85.otg.util.FifoMap; +import com.pg85.otg.util.LRUCache; import com.pg85.otg.util.minecraft.defaults.StructureNames; import net.minecraft.util.math.BlockPos; @@ -37,7 +37,7 @@ public CachedCoord(boolean canSpawnMineShaft2, double mineshaftsRarity2) } } - FifoMap cachedCoordsByChunk = new FifoMap(256); + LRUCache cachedCoordsByChunk = new LRUCache(256); @Override diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/materials/BlockMaterialData.java b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/BlockMaterialData.java new file mode 100644 index 000000000..09f129e30 --- /dev/null +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/BlockMaterialData.java @@ -0,0 +1,96 @@ +package com.pg85.otg.forge.materials; + +import com.pg85.otg.util.bo3.Rotation; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; + +class BlockMaterialData extends ForgeMaterialData +{ + private static final Reference2ReferenceMap INSTANCES = new Reference2ReferenceOpenHashMap<>(); + private StateMaterialData withDefaultBlockData; + private final Block block; + + private BlockMaterialData(Block block) + { + super(Block.getIdFromBlock(block)); + this.block = block; + } + + static BlockMaterialData of(Block block) + { + return INSTANCES.computeIfAbsent(block, BlockMaterialData::new); + } + + @Override + public BlockMaterialData withoutBlockData() + { + return this; + } + + @Override + public StateMaterialData withBlockData() + { + return withDefaultBlockData(); + } + + @Override + public StateMaterialData withDefaultBlockData() + { + StateMaterialData withDefaultBlockData; + if((withDefaultBlockData = this.withDefaultBlockData) == null) + { + this.withDefaultBlockData = withDefaultBlockData = StateMaterialData.of(this.getBlockState()); + } + return withDefaultBlockData; + } + + @SuppressWarnings("deprecation") + @Override + public StateMaterialData withBlockData(int newData) + { + return ForgeMaterialData.ofMinecraftBlockState(this.block.getStateFromMeta(newData)); + } + + @Override + public String getName() + { + if(this.defaultMaterial != null) + { + return this.defaultMaterial.name(); + } + return Block.REGISTRY.getNameForObject(this.block).toString(); + } + + @Override + public Block getBlock() + { + return this.block; + } + + @Override + public IBlockState getBlockState() + { + return this.block.getDefaultState(); + } + + @Override + public byte getBlockData() + { + return 0; + } + + @Override + public int getBlockDataMask() + { + return -1; + } + + @Override + public BlockMaterialData rotate(Rotation rotation) + { + return this; + } +} \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/materials/ForgeMaterialData.java b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/ForgeMaterialData.java index bbd8bbe9c..7e2e52040 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/materials/ForgeMaterialData.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/ForgeMaterialData.java @@ -1,527 +1,172 @@ package com.pg85.otg.forge.materials; +import java.util.Set; + +import com.google.common.collect.ImmutableSet; import com.pg85.otg.common.LocalMaterialData; import com.pg85.otg.common.LocalWorld; -import com.pg85.otg.configuration.standard.PluginStandardValues; import com.pg85.otg.exception.InvalidConfigException; -import com.pg85.otg.forge.blocks.portal.BlockPortalOTG; import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; import net.minecraft.block.Block; import net.minecraft.block.BlockFalling; +import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; -//TODO: Make this class unmodifiable (parseForWorld modifies atm), -//implement a world-specific materials cache and ensure only one -//instance of each unique material (id+metadata) exists in memory. -//TODO: Do creation of new material instances in one place only? - -/** - * Implementation of LocalMaterial that wraps one of Minecraft's Blocks. - * - */ -public class ForgeMaterialData extends LocalMaterialData +public abstract class ForgeMaterialData extends LocalMaterialData { - private IBlockState blockData; - private boolean metaIdSet = false; - private byte metaId; - private boolean materialIdSet = false; - private int materialId; - // TODO: Clean up the constructors, hasData is only used for blocks parsed from configs, - // so we can write them back properly, so may produce unexpected results when used differently. - private boolean hasData; - - private ForgeMaterialData(IBlockState blockData, int blockId, int blockMetaData, boolean hasData) - { - this.blockData = blockData; - this.materialIdSet = true; - this.materialId = blockId; - this.metaIdSet = true; - this.metaId = (byte)blockMetaData; - this.hasData = hasData; - } - - private ForgeMaterialData(IBlockState blockData, String raw, boolean hasData) - { - this.blockData = blockData; - this.rawEntry = raw; - this.hasData = hasData; - } - - private ForgeMaterialData(String raw, boolean hasData) - { - this.blockData = null; - this.rawEntry = raw; - this.hasData = hasData; - } - - public static ForgeMaterialData getBlank() + private static final Set PROBLEMATIC_BLOCKS = ImmutableSet.of(Blocks.PORTAL, Blocks.DISPENSER, Blocks.ACACIA_STAIRS, Blocks.BIRCH_STAIRS, Blocks.BRICK_STAIRS, Blocks.DARK_OAK_STAIRS, Blocks.JUNGLE_STAIRS, Blocks.NETHER_BRICK_STAIRS, Blocks.OAK_STAIRS, Blocks.PURPUR_STAIRS, Blocks.QUARTZ_STAIRS, Blocks.RED_SANDSTONE_STAIRS, Blocks.SANDSTONE_STAIRS, Blocks.SPRUCE_STAIRS, Blocks.STONE_BRICK_STAIRS, Blocks.STONE_STAIRS); + private static final ForgeMaterialData BLANK = UnknownMaterialData.of(BLANK_NAME); + public static final StateMaterialData AIR = ofMinecraftBlockState(Blocks.AIR.getDefaultState()); + + ForgeMaterialData(int blockId) { - ForgeMaterialData material = new ForgeMaterialData((IBlockState)null, null, false); - material.isBlank = true; - return material; + super(blockId); } public static ForgeMaterialData ofString(String input) throws InvalidConfigException { - // Try parsing as an internal Minecraft name - // This is so that things like "minecraft:stone" aren't parsed - // as the block "minecraft" with data "stone", but instead as the - // block "minecraft:stone" with no block data. - - // Used in BO4's as placeholder/detector block. - if(input.toLowerCase().equals("blank")) - { - return ForgeMaterialData.getBlank(); - } - - // Try blockname / minecraft:blockname syntax - String newInput = input; - - net.minecraft.block.Block block = net.minecraft.block.Block.getBlockFromName(newInput); - if (block != null) + if(input.equalsIgnoreCase(BLANK_NAME)) { - // Some old apps exported schematics/bo3's exported "STAIRS" without metadata (for instance "STAIRS:0"). - // However, the default rotation has changed so fix this by adding the correct metadata. - - if( - block == Blocks.PORTAL || - block == Blocks.DISPENSER || - block == Blocks.ACACIA_STAIRS || - block == Blocks.BIRCH_STAIRS || - block == Blocks.BRICK_STAIRS || - block == Blocks.DARK_OAK_STAIRS || - block == Blocks.JUNGLE_STAIRS || - block == Blocks.NETHER_BRICK_STAIRS || - block == Blocks.OAK_STAIRS || - block == Blocks.PURPUR_STAIRS || - block == Blocks.QUARTZ_STAIRS || - block == Blocks.RED_SANDSTONE_STAIRS || - block == Blocks.SANDSTONE_STAIRS || - block == Blocks.SPRUCE_STAIRS || - block == Blocks.STONE_BRICK_STAIRS || - block == Blocks.STONE_STAIRS - ) - { - newInput = input + ":0"; // TODO: Shouldn't this be 3? This appears to fix the problem for the dungeon dimension but I still see it in BB, double check? - } else { - return ForgeMaterialData.ofMinecraftBlock(block, input, false); - } + return BLANK; } - // Try block(:data) syntax - - String blockName = newInput; - int blockData = -1; - - // When there is a . or a : in the name, extract block data - int splitIndex = newInput.lastIndexOf(":"); - if (splitIndex == -1) - { - splitIndex = newInput.lastIndexOf("."); - } - if (splitIndex != -1) + Block block = parseBlock(input); + int meta = -1; + if(block == null) { - blockName = newInput.substring(0, splitIndex); - try + int i = input.lastIndexOf(':'); + if(i < 0) { - blockData = Integer.parseInt(newInput.substring(splitIndex + 1)); + i = input.lastIndexOf('.'); } - catch (NumberFormatException e) + if(i >= 0) { - blockName = newInput; + try + { + meta = Integer.parseInt(input.substring(i + 1)); + block = parseBlock(input.substring(0, i)); + } + catch(NumberFormatException e) + { + // ignore + } } } - // Try block name without data - block = Block.getBlockFromName(blockName); - if (block == null) + if(block != null) { - DefaultMaterial defaultMaterial = DefaultMaterial.getMaterial(blockName); - if (defaultMaterial != null) + if(meta < 0 && PROBLEMATIC_BLOCKS.contains(block)) { - block = Block.getBlockById(defaultMaterial.id); - - // Some old apps exported schematics/bo3's exported "STAIRS" without metadata (for instance "STAIRS:0"). - // However, the default rotation has changed so fix this by adding the correct metadata. - - // TODO: Check if the block uses the Facing property instead of checking a list of known blocks? - if( - blockData == -1 && - ( - block == Blocks.PORTAL || - block == Blocks.DISPENSER || - block == Blocks.ACACIA_STAIRS || - block == Blocks.BIRCH_STAIRS || - block == Blocks.BRICK_STAIRS || - block == Blocks.DARK_OAK_STAIRS || - block == Blocks.JUNGLE_STAIRS || - block == Blocks.NETHER_BRICK_STAIRS || - block == Blocks.OAK_STAIRS || - block == Blocks.PURPUR_STAIRS || - block == Blocks.QUARTZ_STAIRS || - block == Blocks.RED_SANDSTONE_STAIRS || - block == Blocks.SANDSTONE_STAIRS || - block == Blocks.SPRUCE_STAIRS || - block == Blocks.STONE_BRICK_STAIRS || - block == Blocks.STONE_STAIRS - ) - ) - { - blockData = 0; // TODO: Shouldn't this be 3? This appears to fix the problem for the dungeon dimension but I still see it in BB, double check? - } + return ofMinecraftBlockState(block.getDefaultState()); } - } - // Get the block - if (block != null) - { - if (blockData == -1) + if(meta < 0) + { + return ofMinecraftBlock(block); + } + else { - // Use default - return ForgeMaterialData.ofMinecraftBlock(block, newInput, false); - } else { - // Use specified data try { - return ForgeMaterialData.ofMinecraftBlockState(block.getStateFromMeta(blockData), newInput, true); + return ofMinecraftBlockState(block, meta); } - catch(java.lang.ArrayIndexOutOfBoundsException e) + catch(ArrayIndexOutOfBoundsException e) { - throw new InvalidConfigException("Illegal meta data for the block type, cannot use " + newInput); + throw new InvalidConfigException("Illegal meta data for the block type, cannot use " + input); } - catch (IllegalArgumentException e) + catch(IllegalArgumentException e) { - throw new InvalidConfigException("Illegal block data for the block type, cannot use " + newInput); + throw new InvalidConfigException("Illegal block data for the block type, cannot use " + input); } } } - - // Failed, try parsing later as a fallback. - return new ForgeMaterialData(newInput, false); // TODO: Assuming all fallback blocks contain data atm. - } - - /** - * Gets a {@code ForgeMaterialData} of the given id and data. - * @param id The block id. - * @param data The block data. - * @return The {@code BukkitMateialData} instance. - */ - @SuppressWarnings("deprecation") - private static ForgeMaterialData ofIds(int id, int data) - { - Block block = Block.getBlockById(id); - IBlockState blockData = block.getStateFromMeta(data); - return new ForgeMaterialData(blockData, id, data, true); + return UnknownMaterialData.of(input); } - /** - * Gets a {@code ForgeMaterialData} of the given material and data. - * @param material The material. - * @param data The block data. - * @return The {@code ForgeMaterialData} instance. - */ - public static ForgeMaterialData ofDefaultMaterial(DefaultMaterial material, int data) + private static Block parseBlock(String input) { - return ofIds(material.id, data); + Block block = Block.getBlockFromName(input); + if(block == null) + { + DefaultMaterial defaultMaterial = DefaultMaterial.getMaterial(input); + if(defaultMaterial != null) + { + block = Block.getBlockById(defaultMaterial.id); + } + } + return block; } - @Override - protected ForgeMaterialData ofDefaultMaterialPrivate(DefaultMaterial material, int data) - { - return ofDefaultMaterial(material, data); - } - - /** - * Gets a {@code BukkitMaterialData} of the given Minecraft block. The - * default block data (usually 0) will be used. - * @param block The material. - * @return The {@code BukkitMateialData} instance. - */ - public static ForgeMaterialData ofMinecraftBlock(Block block, String raw, boolean hasData) + public static StateMaterialData ofDefaultMaterial(DefaultMaterial defaultMaterial, int blockData) { - return ofMinecraftBlockState(block.getDefaultState(), raw, hasData); + return ofMinecraftBlockState(Block.getBlockById(defaultMaterial.id), blockData); } - /** - * Gets a {@code ForgeMaterialData} of the given Minecraft blockData. - * @param blockData The material an data. - * @return The {@code BukkitMateialData} instance. - */ - public static ForgeMaterialData ofMinecraftBlockState(IBlockState blockData) + public static BlockMaterialData ofMinecraftBlock(Block block) { - return new ForgeMaterialData(blockData, null, true); + return BlockMaterialData.of(block); } - - /** - * Gets a {@code ForgeMaterialData} of the given Minecraft blockData. - * @param blockData The material an data. - * @return The {@code BukkitMateialData} instance. - */ - public static ForgeMaterialData ofMinecraftBlockState(IBlockState blockData, String raw, boolean hasData) - { - return new ForgeMaterialData(blockData, raw, hasData); - } - + @SuppressWarnings("deprecation") - @Override - public LocalMaterialData withBlockData(int i) + public static StateMaterialData ofMinecraftBlockState(Block block, int blockData) { - if(this.blockData == null) - { - return this; - } - if (i == getBlockData()) - { - return this; - } - - Block block = this.blockData.getBlock(); - return ofMinecraftBlockState(block.getStateFromMeta(i), this.rawEntry, true); + return ofMinecraftBlockState(block.getStateFromMeta(blockData)); } - @Override - public LocalMaterialData withDefaultBlockData() - { - if(this.blockData == null) - { - return this; - } - Block block = this.blockData.getBlock(); - return this.withBlockData(block.getMetaFromState(block.getDefaultState())); - } - - @Override - public byte getBlockData() + public static StateMaterialData ofMinecraftBlockState(IBlockState state) { - if(!this.metaIdSet) - { - this.metaIdSet = true; - this.metaId = this.blockData == null ? 0 : (byte) this.blockData.getBlock().getMetaFromState(this.blockData); - } - return this.metaId; + return StateMaterialData.of(state); } - @Override - public int getBlockId() - { - if(!this.materialIdSet) - { - this.materialIdSet = true; - this.materialId = this.blockData == null ? 0 : Block.getIdFromBlock(this.blockData.getBlock()); - } - return this.materialId; - } + public abstract Block getBlock(); - @Override - public String getName() - { - if(isBlank) - { - return "BLANK"; - } - else if(this.blockData == null) - { - if(this.rawEntry != null) - { - return this.rawEntry; - } else { - return "Unknown"; - } - } else { - Block block = this.blockData.getBlock(); - byte data = getBlockData(); - // Note that the above line is not equivalent to data != 0, as for - // example pumpkins have a default data value of 2 - - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial == null) - { - boolean nonDefaultData = !block.getDefaultState().equals(this.blockData); - - // Use Minecraft's name - if (nonDefaultData) - { - return Block.REGISTRY.getNameForObject(block) + (!this.hasData ? "" : ":" + data); - } else { - return Block.REGISTRY.getNameForObject(block).toString(); - } - } else { - // Use our name - return defaultMaterial.name() + (!this.hasData ? "" : ":" + data); - } - } - } - - public IBlockState internalBlock() - { - return this.blockData; - } + public abstract IBlockState getBlockState(); - @Override - public boolean isMaterial(DefaultMaterial material) - { - return material.id == getBlockId(); - } - @Override public boolean isLiquid() { - // For some reason, .isLiquid() appears to be - // really slow, so use defaultMaterial instead. - - // Let us override whether materials are solid - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - return defaultMaterial.isLiquid(); - } - - return this.blockData == null ? false : this.blockData.getMaterial().isLiquid(); + return this.getBlockState().getMaterial().isLiquid(); } @Override public boolean isSolid() - { - // Let us override whether materials are solid - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - return defaultMaterial.isSolid(); - } - - return this.blockData == null ? false : this.blockData.getMaterial().isSolid(); - } - - @Override - public boolean isEmptyOrAir() { - return this.blockData == null ? true : this.blockData.getBlock() == Blocks.AIR; + return this.getBlockState().getMaterial().isSolid() && this.getBlockState().causesSuffocation(); } - + @Override public boolean isAir() { - return this.blockData != null && this.blockData.getBlock() == Blocks.AIR; - } - - @Override - public boolean isEmpty() - { - return this.blockData == null; + return this.getBlock() == Blocks.AIR; } @Override public boolean canFall() { - return this.blockData == null ? false : this.blockData.getBlock() instanceof BlockFalling; + return this.getBlock() instanceof BlockFalling; } @Override - public boolean canSnowFallOn() + public boolean isLeaves() { - DefaultMaterial defaultMaterial = toDefaultMaterial(); - if (defaultMaterial != null) - { - return defaultMaterial.canSnowFallOn(); - } - - return this.blockData == null ? false : this.blockData.getMaterial().isSolid(); + return this.getBlockState().getMaterial() == Material.LEAVES; } - // TODO: Caching result means fallbacks will only work for one world, fix this! - // TODO: Not returning a copy means that any block parsed is modified, this may - // unintentionally include things like MaterialHelper blocks. - // Redesign this, or at least make sure this doesn't cause problems. - // TODO: This is only applied for settings using a materialset and (non-bo) resources atm, - // fix this for bo's and any other material settings (do all use materialset?), will - // need a world-specific materials cache for readMaterial, should probably just parse - // materials immediately when reading settings, so pass world info to configs loading - // code? - @Override - public LocalMaterialData parseForWorld(LocalWorld world) - { - if (!this.checkedFallbacks && this.isEmpty() && this.rawEntry != null) - { - this.checkedFallbacks = true; - ForgeMaterialData newMaterialData = ((ForgeMaterialData)world.getConfigs().getWorldConfig().parseFallback(this.rawEntry)); - if(newMaterialData != null && newMaterialData.blockData != null) - { - // TODO: Should blockData be a clone? - this.blockData = newMaterialData.blockData; - this.metaIdSet = newMaterialData.metaIdSet; - this.materialIdSet = newMaterialData.materialIdSet; - this.rawEntry = newMaterialData.rawEntry; - this.defaultMaterial = newMaterialData.defaultMaterial; - } - } - return this; - } - - @Override - public DefaultMaterial toDefaultMaterial() - { - if(this.defaultMaterial == null && !parsedDefaultMaterial) - { - parsedDefaultMaterial = true; - if(this.blockData != null) - { - this.defaultMaterial = DefaultMaterial.getMaterial(getBlockId()); - if(this.defaultMaterial == null) - { - if(this.blockData.getBlock() instanceof BlockPortalOTG) // TODO: avoid using instanceof so much? - { - this.defaultMaterial = DefaultMaterial.PORTAL; - } - } - } - } - return defaultMaterial; - } - @Override - public boolean equals(Object obj) + public boolean isSmoothAreaAnchor(boolean allowWood, boolean ignoreWater) { - if (this == obj) - { - return true; - } - if (!(obj instanceof ForgeMaterialData)) - { - return false; - } - ForgeMaterialData other = (ForgeMaterialData) obj; - if(this.isBlank != other.isBlank) + if(this.isSolid()) { - return false; + return allowWood || this.getBlockState().getMaterial() != Material.WOOD; } - else if(this.isBlank) - { - return true; - } - - return - (this.blockData != null && other.blockData != null && this.blockData.equals(other.blockData)) || - (this.rawEntry != null && other.rawEntry != null && this.rawEntry.equals(other.rawEntry)) - ; + return !ignoreWater && this.getBlockState().getMaterial().isLiquid(); } - - /** - * Gets the hashCode of the material, based on the block id and block data. - * The hashCode must be unique, which is possible considering that there are - * only 4096 * 16 possible materials. - * - * @return The unique hashCode. - */ - public int hashCode() + + @Override + public LocalMaterialData parseForWorld(LocalWorld world) { - return PluginStandardValues.SUPPORTED_BLOCK_IDS + (getBlockId() * 16) + getBlockData(); + return this; } - - @Override - public boolean hasData() - { - return this.hasData; - } -} +} \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/materials/StateMaterialData.java b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/StateMaterialData.java new file mode 100644 index 000000000..362e6aea0 --- /dev/null +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/StateMaterialData.java @@ -0,0 +1,127 @@ +package com.pg85.otg.forge.materials; + +import com.pg85.otg.util.bo3.Rotation; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; + +class StateMaterialData extends ForgeMaterialData +{ + private static final Reference2ReferenceMap INSTANCES = new Reference2ReferenceOpenHashMap<>(); + private BlockMaterialData withoutBlockData; + private final IBlockState state; + private final byte blockData; + + private StateMaterialData(IBlockState state) + { + super(Block.getIdFromBlock(state.getBlock())); + this.state = state; + this.blockData = (byte) state.getBlock().getMetaFromState(state); + } + + static StateMaterialData of(IBlockState state) + { + return INSTANCES.computeIfAbsent(state, StateMaterialData::new); + } + + @Override + public BlockMaterialData withoutBlockData() + { + BlockMaterialData withoutBlockData; + if((withoutBlockData = this.withoutBlockData) == null) + { + this.withoutBlockData = withoutBlockData = BlockMaterialData.of(this.getBlock()); + } + return withoutBlockData; + } + + @Override + public StateMaterialData withBlockData() + { + return this; + } + + @Override + public StateMaterialData withDefaultBlockData() + { + if(this.blockData == 0) + { + return this; + } + return ForgeMaterialData.ofMinecraftBlockState(this.getBlock().getDefaultState()); + } + + @SuppressWarnings("deprecation") + @Override + public StateMaterialData withBlockData(int newData) + { + if(this.blockData == newData) + { + return this; + } + return ForgeMaterialData.ofMinecraftBlockState(this.getBlock().getStateFromMeta(newData)); + } + + @Override + public String getName() + { + if(this.defaultMaterial != null) + { + return this.defaultMaterial.name() + ":" + this.blockData; + } + return Block.REGISTRY.getNameForObject(this.getBlock()) + ":" + this.blockData; + } + + @Override + public Block getBlock() + { + return this.state.getBlock(); + } + + @Override + public IBlockState getBlockState() + { + return this.state; + } + + @Override + public byte getBlockData() + { + return this.blockData; + } + + @Override + public int getBlockDataMask() + { + return 1 << this.blockData; + } + + @Override + public StateMaterialData rotate(Rotation rotation) + { + IBlockState newState; + switch(rotation) + { + case NORTH: + return this; + case WEST: + newState = this.state.withRotation(net.minecraft.util.Rotation.CLOCKWISE_90); + break; + case SOUTH: + newState = this.state.withRotation(net.minecraft.util.Rotation.CLOCKWISE_180); + break; + case EAST: + newState = this.state.withRotation(net.minecraft.util.Rotation.COUNTERCLOCKWISE_90); + break; + default: + throw new IllegalArgumentException(); + } + if(newState == this.state) + { + return this; + } + return ForgeMaterialData.ofMinecraftBlockState(newState); + } +} \ No newline at end of file diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/materials/UnknownMaterialData.java b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/UnknownMaterialData.java new file mode 100644 index 000000000..e0c281f19 --- /dev/null +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/materials/UnknownMaterialData.java @@ -0,0 +1,112 @@ +package com.pg85.otg.forge.materials; + +import com.pg85.otg.common.LocalMaterialData; +import com.pg85.otg.common.LocalWorld; +import com.pg85.otg.util.bo3.Rotation; +import com.pg85.otg.util.minecraft.defaults.DefaultMaterial; + +import it.unimi.dsi.fastutil.objects.Object2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; + +class UnknownMaterialData extends ForgeMaterialData +{ + private static final Object2ReferenceMap INSTANCES = new Object2ReferenceOpenHashMap<>(); + private final String raw; + private LocalMaterialData replacement; + + private UnknownMaterialData(String raw) + { + super(DefaultMaterial.AIR.id); + this.raw = raw; + } + + static UnknownMaterialData of(String raw) + { + return INSTANCES.computeIfAbsent(raw, UnknownMaterialData::new); + } + + @Override + public boolean isEmpty() + { + return true; + } + + @Override + public LocalMaterialData parseForWorld(LocalWorld world) + { + LocalMaterialData result; + if((result = this.replacement) == null) + { + result = world.getConfigs().getWorldConfig().parseFallback(this.raw); + if(result == null) + { + result = this; + } + this.replacement = result; + } + return result; + } + + @Override + public LocalMaterialData withoutBlockData() + { + return this; + } + + @Override + public LocalMaterialData withBlockData() + { + return this; + } + + @Override + public LocalMaterialData withDefaultBlockData() + { + return this; + } + + @Override + public LocalMaterialData withBlockData(int newData) + { + return this; + } + + @Override + public String getName() + { + return this.raw; + } + + @Override + public Block getBlock() + { + return Blocks.AIR; + } + + @Override + public IBlockState getBlockState() + { + return Blocks.AIR.getDefaultState(); + } + + @Override + public byte getBlockData() + { + return 0; + } + + @Override + public int getBlockDataMask() + { + return 0; + } + + @Override + public LocalMaterialData rotate(Rotation rotation) + { + return this; + } +} diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/network/server/packets/ParticlesPacket.java b/platforms/forge/src/main/java/com/pg85/otg/forge/network/server/packets/ParticlesPacket.java index eca602353..597af3b0f 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/network/server/packets/ParticlesPacket.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/network/server/packets/ParticlesPacket.java @@ -119,9 +119,9 @@ public IMessage handleClientMessage(EntityPlayer player, ParticlesPacket message } else { particle = new BO3ParticleFunction(); } - particle.x = Integer.parseInt(parameters[0]); - particle.y = Integer.parseInt(parameters[1]); - particle.z = Integer.parseInt(parameters[2]); + particle.x(Integer.parseInt(parameters[0])); + particle.y(Integer.parseInt(parameters[1])); + particle.z(Integer.parseInt(parameters[2])); particle.particleName = parameters[3]; particle.interval = Double.parseDouble(parameters[4]); particle.velocityX = Double.parseDouble(parameters[5]); @@ -150,9 +150,9 @@ public IMessage handleClientMessage(EntityPlayer player, ParticlesPacket message for(ParticleFunction existingParticleFunction : existingParticleFunctions) { if( - particleFunction.x == existingParticleFunction.x && - particleFunction.y == existingParticleFunction.y && - particleFunction.z == existingParticleFunction.z + particleFunction.x() == existingParticleFunction.x() && + particleFunction.y() == existingParticleFunction.y() && + particleFunction.z() == existingParticleFunction.z() ) { bFound = true; diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO3Creator.java b/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO3Creator.java index 109034719..1db7cb5db 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO3Creator.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO3Creator.java @@ -1,7 +1,6 @@ package com.pg85.otg.forge.util; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -87,7 +86,7 @@ public boolean create(Region selection, World world, String blockName, boolean b ForgeMaterialData data = ForgeMaterialData.ofMinecraftBlockState(block); - if (centerBlock != null && centerBlock.equals(data)) + if (centerBlock != null && centerBlock.matches(data)) { centerPointX = x + start.getBlockX(); centerPointY = y + start.getBlockY(); @@ -203,13 +202,8 @@ public boolean create(Region selection, World world, String blockName, boolean b tileEntityCount++; try { - tileEntityFile.createNewFile(); - FileOutputStream fos = new FileOutputStream(tileEntityFile); - tag.writeTo(fos); - fos.flush(); - fos.close(); - blockFunction.metaDataTag = tag; - blockFunction.metaDataName = name + "/" + tileEntityName; + tag.writeTo(tileEntityFile.toPath()); + blockFunction.blockContainer = material.blockContainer(tileEntitiesFolder.getParentFile().toPath(), name + "/" + tileEntityName); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO4Creator.java b/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO4Creator.java index b84cbb848..9b3e741cf 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO4Creator.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/util/BO4Creator.java @@ -1,7 +1,6 @@ package com.pg85.otg.forge.util; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -87,7 +86,7 @@ public boolean create(Region selection, World world, String blockName, boolean b data = ForgeMaterialData.ofMinecraftBlockState(block); // If we have a match for the center block, update values and end the loop - if (centerBlock.equals(data)) { + if (centerBlock.matches(data)) { centerPointX = x + start.getBlockX(); centerPointY = y + start.getBlockY(); centerPointZ = z + start.getBlockZ(); @@ -117,7 +116,6 @@ public boolean create(Region selection, World world, String blockName, boolean b NamedBinaryTag tag; String tileEntityName; File tileEntityFile; - FileOutputStream fos; // Loops through all blocks in selection for (int x = start.getBlockX(); x <= end.getBlockX(); x++) { @@ -178,13 +176,8 @@ public boolean create(Region selection, World world, String blockName, boolean b tileEntityCount++; try { - tileEntityFile.createNewFile(); - fos = new FileOutputStream(tileEntityFile); - tag.writeTo(fos); - fos.flush(); - fos.close(); - blockFunction.metaDataTag = tag; - blockFunction.metaDataName = name + "/" + tileEntityName; + tag.writeTo(tileEntityFile.toPath()); + blockFunction.blockContainer = material.blockContainer(tileEntitiesFolder.getParentFile().toPath(), name + "/" + tileEntityName); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/util/NBTHelper.java b/platforms/forge/src/main/java/com/pg85/otg/forge/util/NBTHelper.java index 2a2907fd2..b8ea22641 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/util/NBTHelper.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/util/NBTHelper.java @@ -17,6 +17,7 @@ import net.minecraft.nbt.NBTTagIntArray; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagLong; +import net.minecraft.nbt.NBTTagLongArray; import net.minecraft.nbt.NBTTagShort; import net.minecraft.nbt.NBTTagString; import net.minecraft.tileentity.TileEntity; @@ -69,6 +70,7 @@ public static NamedBinaryTag getNBTFromNMSTagCompound(String name, NBTTagCompoun case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: compoundTag.addTag(new NamedBinaryTag(type, entry.getKey(), getValueFromNms(nmsChildTag))); break; case TAG_List: @@ -125,6 +127,7 @@ private static NamedBinaryTag getNBTFromNMSTagList(String name, NBTTagList nmsLi case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: listTag.addTag(new NamedBinaryTag(listType, null, getValueFromNms(nmsChildTag))); break; case TAG_List: @@ -177,6 +180,8 @@ private static Object getValueFromNms(NBTBase nmsTag) return ((NBTTagString) nmsTag).getString(); case TAG_Int_Array: return ((NBTTagIntArray) nmsTag).getIntArray(); + case TAG_Long_Array: + return new long[0]; default: // Cannot read this from a tag throw new IllegalArgumentException(type + "doesn't have a simple value!"); @@ -209,6 +214,7 @@ public static NBTTagCompound getNMSFromNBTTagCompound(NamedBinaryTag compoundTag case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: nmsTag.setTag(tag.getName(), createTagNms(tag.getType(), tag.getValue())); break; case TAG_List: @@ -250,6 +256,7 @@ private static NBTTagList getNMSFromNBTTagList(NamedBinaryTag listTag) case TAG_Byte_Array: case TAG_String: case TAG_Int_Array: + case TAG_Long_Array: nmsTag.appendTag(createTagNms(tag.getType(), tag.getValue())); break; case TAG_List: @@ -295,6 +302,8 @@ private static NBTBase createTagNms(NamedBinaryTag.Type type, Object value) return new NBTTagString((String) value); case TAG_Int_Array: return new NBTTagIntArray((int[]) value); + case TAG_Long_Array: + return new NBTTagLongArray((long[]) value); default: // Cannot make this into a tag throw new IllegalArgumentException(type + "doesn't have a simple value!"); diff --git a/platforms/forge/src/main/java/com/pg85/otg/forge/world/ForgeWorld.java b/platforms/forge/src/main/java/com/pg85/otg/forge/world/ForgeWorld.java index f08ff698c..f0b299e90 100644 --- a/platforms/forge/src/main/java/com/pg85/otg/forge/world/ForgeWorld.java +++ b/platforms/forge/src/main/java/com/pg85/otg/forge/world/ForgeWorld.java @@ -71,13 +71,12 @@ import net.minecraft.world.gen.structure.template.Template; import net.minecraft.world.gen.structure.template.TemplateManager; import net.minecraftforge.fml.common.ObfuscationReflectionHelper; -import net.minecraftforge.fml.relauncher.ReflectionHelper.UnableToFindMethodException; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.apache.commons.lang3.RandomUtils; import java.io.File; -import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; @@ -755,45 +754,30 @@ public int getHeightMapHeight(int x, int z, ChunkCoordinate chunkBeingPopulated) @Override public LocalMaterialData getMaterial(int x, int y, int z, ChunkCoordinate chunkBeingPopulated) { - if (y >= PluginStandardValues.WORLD_HEIGHT || y < PluginStandardValues.WORLD_DEPTH) + if(y >= PluginStandardValues.WORLD_HEIGHT || y < PluginStandardValues.WORLD_DEPTH) { - return null; + return ForgeMaterialData.AIR; } - // If the chunk exists or is inside the area being populated, fetch it normally. - Chunk chunk = null; - if( - (chunkBeingPopulated != null && OTG.IsInAreaBeingPopulated(x, z, chunkBeingPopulated)) - //|| getChunkGenerator().chunkExists(x, z) - ) - { - chunk = getChunkGenerator().getChunk(x, z); - } - - // If the chunk doesn't exist and we're doing something outside the - // population sequence, return the material without loading the chunk. - if(chunk == null && chunkBeingPopulated == null) - { - // If the chunk has already been loaded, no need to use fake chunks. - if(world.isBlockLoaded(new BlockPos(x,255,z))) - { - chunk = getChunkGenerator().getChunk(x, z); - } else { - // Calculate the material without loading the chunk. - return generator.getMaterialInUnloadedChunk(x,y,z); - } - } - - // Tried to query an unloaded chunk outside the area being populated - if(chunk == null) - { - return null; - } - - // Get internal coordinates for block in chunk - int internalX = x & 0xF; - int internalZ = z & 0xF; - return ForgeMaterialData.ofMinecraftBlockState(chunk.getBlockState(internalX, y, internalZ)); + Chunk chunk; + if(chunkBeingPopulated != null) + { + if(!OTG.IsInAreaBeingPopulated(x, z, chunkBeingPopulated)) + { + return ForgeMaterialData.AIR; + } + chunk = this.world.getChunk(x >> 4, z >> 4); + } + else + { + chunk = this.world.getChunkProvider().getLoadedChunk(x >> 4, z >> 4); + if(chunk == null) + { + return this.generator.getMaterialInUnloadedChunk(x, y, z); + } + } + + return ForgeMaterialData.ofMinecraftBlockState(chunk.getBlockState(x & 15, y, z & 15)); } @Override @@ -1053,87 +1037,33 @@ public boolean chunkHasDefaultStructure(Random rand, ChunkCoordinate chunkCoord) (worldConfig.woodLandMansionsEnabled && isStructureInRadius(chunkCoord, this.woodLandMansionGen, 4)) ; } - - - private static boolean inited = false; - private static Method canSpawnStructureAtCoordsMethodObf = null; - private static Method canSpawnStructureAtCoordsMethodDeObf = null; + + private static final Field worldField = ObfuscationReflectionHelper.findField(MapGenBase.class, "field_75039_c"); + private static final Method canSpawnStructureAtCoordsMethod = ObfuscationReflectionHelper.findMethod(MapGenStructure.class, "func_75047_a", boolean.class, int.class, int.class); + public boolean isStructureInRadius(ChunkCoordinate startChunk, MapGenStructure structure, int radiusInChunks) { - if(!inited) - { - inited = true; - try - { - canSpawnStructureAtCoordsMethodObf = ObfuscationReflectionHelper.findMethod(MapGenStructure.class, "func_75047_a", boolean.class, int.class, int.class); - } catch(UnableToFindMethodException ex) { } - try - { - canSpawnStructureAtCoordsMethodDeObf = ObfuscationReflectionHelper.findMethod(MapGenStructure.class, "canSpawnStructureAtCoords", boolean.class, int.class, int.class); - } catch(UnableToFindMethodException ex) { } - - if(canSpawnStructureAtCoordsMethodObf == null && canSpawnStructureAtCoordsMethodDeObf == null) - { - OTG.log(LogMarker.ERROR, "Error, could not reflect MapGenStructure.canSpawnStructureAtCoords, BO4's may not be able to detect default/modded structures. OTG may not fully support your Forge version or modded structures."); - return false; - } - } - - if(canSpawnStructureAtCoordsMethodObf == null && canSpawnStructureAtCoordsMethodDeObf == null) - { - return false; - } - - int chunkX = startChunk.getChunkX(); - int chunkZ = startChunk.getChunkZ(); - for (int cycle = 0; cycle <= radiusInChunks; ++cycle) + try { - for (int xRadius = -cycle; xRadius <= cycle; ++xRadius) + worldField.set(structure, this.world); + for(int x = -radiusInChunks; x <= radiusInChunks; x++) { - for (int zRadius = -cycle; zRadius <= cycle; ++zRadius) + for(int z = -radiusInChunks; z <= radiusInChunks; z++) { - int distance = (int)Math.floor(Math.sqrt(Math.pow (xRadius, 2) + Math.pow (zRadius, 2))); - if (distance == cycle) + if((boolean) canSpawnStructureAtCoordsMethod.invoke(structure, startChunk.getChunkX() + x, startChunk.getChunkZ() + z)) { - boolean canSpawnStructureAtCoords = false; - - try - { - if(canSpawnStructureAtCoordsMethodObf != null) - { - canSpawnStructureAtCoords = (boolean) canSpawnStructureAtCoordsMethodObf.invoke(structure, chunkX + xRadius, chunkZ + zRadius); - if(canSpawnStructureAtCoords) - { - return true; - } - continue; - } - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { } - - try - { - if(canSpawnStructureAtCoordsMethodDeObf != null) - { - canSpawnStructureAtCoords = (boolean) canSpawnStructureAtCoordsMethodDeObf.invoke(structure, chunkX + xRadius, chunkZ + zRadius); - if(canSpawnStructureAtCoords) - { - return true; - } - } - } - catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) - { - OTG.log(LogMarker.ERROR, "Error, could not reflect MapGenStructure.canSpawnStructureAtCoords, BO4's may not be able to detect default/modded structures. OTG may not fully support your Forge version or modded structures."); - return false; - } + return true; } } } + return false; + } + catch(ReflectiveOperationException e) + { + throw new UnsupportedOperationException(e); } - return false; } - + // TODO: No clue what this is used for, leads to some MC advancements "test" code. public boolean isInsideStructure(String structureName, BlockPos pos) { @@ -1323,7 +1253,7 @@ private void replaceBlocks(Chunk rawChunk) } if(instruction.getFrom().getBlockId() == blockId) { - section.set(sectionX, sectionY, sectionZ, ((ForgeMaterialData)instruction.getTo()).internalBlock()); + section.set(sectionX, sectionY, sectionZ, ((ForgeMaterialData)instruction.getTo()).getBlockState()); } } } @@ -1388,9 +1318,9 @@ public void spawnEntity(EntityFunction entityData, ChunkCoordinate chunkBeing { if(OTG.getPluginConfig().spawnLog) { - OTG.log(LogMarker.DEBUG, "Attempting to spawn BO3 Entity() " + entityData.groupSize + " x " + entityData.name + " at " + entityData.x + " " + entityData.y + " " + entityData.z); + OTG.log(LogMarker.DEBUG, "Attempting to spawn BO3 Entity() " + entityData.groupSize + " x " + entityData.name + " at " + entityData.x() + " " + entityData.y() + " " + entityData.z()); } - if (chunkBeingPopulated != null && !OTG.IsInAreaBeingPopulated((int) Math.floor(entityData.x), (int) Math.floor(entityData.z), chunkBeingPopulated)) { + if (chunkBeingPopulated != null && !OTG.IsInAreaBeingPopulated((int) Math.floor(entityData.x()), (int) Math.floor(entityData.z()), chunkBeingPopulated)) { // If outside area being populated, abort and remove entity if(OTG.getPluginConfig().spawnLog) { @@ -1398,7 +1328,7 @@ public void spawnEntity(EntityFunction entityData, ChunkCoordinate chunkBeing } return; } - if (entityData.y < 0 || entityData.y >= 256) { + if (entityData.y() < 0 || entityData.y() >= 256) { if(OTG.getPluginConfig().spawnLog) { OTG.log(LogMarker.ERROR, "Failed to spawn mob "+entityData.name +", spawn position out of bounds"); @@ -1412,9 +1342,9 @@ public void spawnEntity(EntityFunction entityData, ChunkCoordinate chunkBeing if (entity == null) return; // If either the block is a full block, or entity is a fish out of water, then we cancel - if (world.isBlockNormalCube(new BlockPos(entityData.x, entityData.y, entityData.z), false) + if (world.isBlockNormalCube(new BlockPos(entityData.x(), entityData.y(), entityData.z()), false) || ((entity.isCreatureType(EnumCreatureType.WATER_CREATURE, false) || entity instanceof EntityGuardian) - && world.getBlockState(new BlockPos(entityData.x, entityData.y, entityData.z)).getMaterial() != Material.WATER)) + && world.getBlockState(new BlockPos(entityData.x(), entityData.y(), entityData.z())).getMaterial() != Material.WATER)) { world.removeEntity(entity); return; @@ -1500,15 +1430,15 @@ else if (entityData.nameTagOrNBTFileName.toLowerCase().trim().endsWith(".nbt")) } // Spawn entity, with potential passengers - entity = AnvilChunkLoader.readWorldEntityPos(nbttagcompound, world, entityData.x+0.5, entityData.y, entityData.z+0.5, true); + entity = AnvilChunkLoader.readWorldEntityPos(nbttagcompound, world, entityData.x()+0.5, entityData.y(), entityData.z()+0.5, true); if (entity == null) return null; } else { // Create a default entity from the given mob type nbttagcompound.setString("id", entityData.resourceLocation); - entity = AnvilChunkLoader.readWorldEntityPos(nbttagcompound, world, entityData.x + 0.5, entityData.y, entityData.z + 0.5, true); + entity = AnvilChunkLoader.readWorldEntityPos(nbttagcompound, world, entityData.x() + 0.5, entityData.y(), entityData.z() + 0.5, true); if (entity instanceof EntityLiving) { ((EntityLiving)entity).onInitialSpawn(world.getDifficultyForLocation( - new BlockPos(entityData.x, entityData.y, entityData.z)),(IEntityLivingData)null); + new BlockPos(entityData.x(), entityData.y(), entityData.z())),(IEntityLivingData)null); } if (entity == null) return null; @@ -1556,4 +1486,14 @@ public boolean isBo4Enabled() } return isOTGPlus; } + + @Override + public boolean canPlaceSnowAt(int x, int y, int z, ChunkCoordinate chunkBeingPopulated) + { + if(chunkBeingPopulated != null && OTG.IsInAreaBeingPopulated(x, z, chunkBeingPopulated)) + { + return false; + } + return Blocks.SNOW_LAYER.canPlaceBlockAt(world, new BlockPos(x, y, z)); + } }