From 8517a20f9fe4e482895b398999f2cd75bae98a3a Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 24 Apr 2020 15:11:42 -0500 Subject: [PATCH] Reworked biome per island generator. --- .../generation/BiomePerIslandGenerator.java | 217 ++++++++++-------- 1 file changed, 127 insertions(+), 90 deletions(-) diff --git a/src/main/java/ca/recrown/islandsurvivalcraft/world/generation/BiomePerIslandGenerator.java b/src/main/java/ca/recrown/islandsurvivalcraft/world/generation/BiomePerIslandGenerator.java index dc3f000..206ac89 100644 --- a/src/main/java/ca/recrown/islandsurvivalcraft/world/generation/BiomePerIslandGenerator.java +++ b/src/main/java/ca/recrown/islandsurvivalcraft/world/generation/BiomePerIslandGenerator.java @@ -1,122 +1,159 @@ package ca.recrown.islandsurvivalcraft.world.generation; -import java.util.Arrays; import org.bukkit.World; import org.bukkit.block.Biome; +import ca.recrown.islandsurvivalcraft.caching.Cache; +import ca.recrown.islandsurvivalcraft.caching.CacheValue; +import ca.recrown.islandsurvivalcraft.caching.CoordinateIdentifier; +import ca.recrown.islandsurvivalcraft.caching.Identifier; import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable; +import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable; import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch; import ca.recrown.islandsurvivalcraft.world.BiomeSelector; import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper; -public class BiomePerIslandGenerator implements IslandBiomeGenerator, CoordinateTargetValidatable { +//Note: technically, the validators have to be run on land, and so, some condition checks may not be nessecary. +public class BiomePerIslandGenerator implements IslandBiomeGenerator, CoordinateValidatable, CoordinateTargetValidatable { + private final Cache chunkBiomesCache; + private final Cache chunkGenStatusCache; + private final TemperatureMapGenerator temperatureMapGenerator; private boolean initialized; - private IslandWorldMapper islandLocator; + private IslandWorldMapper worldIslandMap; private BiomeSelector biomeSelector; - private TemperatureMapGenerator temperatureMapGenerator; private World world; - private DepthFirstSearch dfs; - private Biome mainBiome; - private Biome shoreBiome; - private float temperature; - - private int chunkX, chunkZ; - private Biome[][] chunkCache; + private DepthFirstSearch mapWideDFS; + private DepthFirstSearch propagator; + private LocalIslandPropagator propagatorInfo; + CoordinateIdentifier chunkCoords; + ChunkBiomes localChunkBiomes; + + float temperature; public BiomePerIslandGenerator() { this.temperatureMapGenerator = new TemperatureMapGenerator(); - chunkCache = new Biome[16][16]; + chunkBiomesCache = new Cache<>(1024); + chunkGenStatusCache = new Cache<>(1024); + propagatorInfo = new LocalIslandPropagator(); } @Override - public void initialize(World world, IslandWorldMapper mapGenerator, BiomeSelector biomeSelector) { + public void initialize(final World world, final IslandWorldMapper mapGenerator, final BiomeSelector biomeSelector) { if (initialized) throw new IllegalStateException("Biome generator already initialized."); initialized = true; - this.islandLocator = mapGenerator; + this.worldIslandMap = mapGenerator; this.biomeSelector = biomeSelector; this.world = world; this.temperatureMapGenerator.setSeed(world.getSeed()); - dfs = new DepthFirstSearch(2048, islandLocator); - } - - @Override - public boolean isCoordinateTarget(int x, int y) { - //Should be ran for a island until finding biome info or until it looked for it's entirety and couldn't find it. This is one island. - - //First check if the chunk we're looking at is generated. - if (world.isChunkGenerated(x/16, y/16)) { - //If so, we know that the biomes have already been assigned, and can therefore use it. - Biome foundBiome = world.getBiome(x, 0, y); - if (islandLocator.isShore(x, y)) { - this.shoreBiome = foundBiome; - } else { - this.mainBiome = foundBiome; - } - if (mainBiome != null && shoreBiome != null) { - return true; - } - } else - - //If the same chunk, then do the same thing, except use the chunk biome cache instead to look for it. - if (x/16 == chunkX && y/16 == chunkZ) { - Biome foundBiome = chunkCache[Math.abs(x % 16)][Math.abs(y % 16)]; - if (foundBiome == null) return false; - if (islandLocator.isShore(x, y)) { - this.shoreBiome = foundBiome; - } else { - this.mainBiome = foundBiome; - } - if (mainBiome != null && shoreBiome != null) { - return true; - } - } - return false; - } - - - @Override - public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) { - int worldX = chunkX * 16 + localX; - int worldZ = chunkZ * 16 + localZ; - - //Clear chunk cache if different chunk - if (chunkX != this.chunkX || chunkZ != this.chunkZ) { - this.chunkX = chunkX; - this.chunkZ = chunkZ; - for (int i = 0; i < chunkCache.length; i++) { - Arrays.fill(chunkCache[i], null); - } - } - - if (islandLocator.isIsland(worldX, worldZ)) { - if (mainBiome == null && shoreBiome == null) { - dfs.setStartPosition(worldX, worldZ); - if (!dfs.findTarget(this)) { - temperature = temperatureMapGenerator.getTemperature(worldX, worldZ); - mainBiome = biomeSelector.getLandBiome(temperature); - shoreBiome = biomeSelector.getShoreBiome(mainBiome, temperature); - } - } - } else { - mainBiome = null; - shoreBiome = null; - } - - - Biome designatedBiome = mainBiome; - if (mainBiome == null) { - designatedBiome = biomeSelector.getOceanBiome(temperatureMapGenerator.getTemperature(worldX, worldZ)); - } else if (islandLocator.isShore(worldX, worldZ)) { - designatedBiome = shoreBiome; - } - - chunkCache[localX][localZ] = designatedBiome; - return designatedBiome; + mapWideDFS = new DepthFirstSearch(worldIslandMap); + mapWideDFS.setEndPosition(0, 0); + propagator = new DepthFirstSearch(256, this); } @Override public IslandBiomeGenerator getInstance() { return new BiomePerIslandGenerator(); } + + @Override + public Biome GenerateBiome(final int chunkX, final int chunkZ, final int localX, final int localZ) { + if (chunkCoords == null || chunkCoords.getX() != chunkX || chunkCoords.getY() != chunkZ) { + chunkCoords = new CoordinateIdentifier(chunkX, chunkZ); + chunkGenStatusCache.retrieveCache(chunkCoords).setValue(true); + localChunkBiomes = new ChunkBiomes(); + chunkBiomesCache.retrieveCache(chunkCoords).setValue(localChunkBiomes); + } + int worldX = 16 * chunkX + localX; + int worldZ = 16 * chunkZ + localZ; + + this.temperature = temperatureMapGenerator.getTemperature(worldX, worldZ); + if (worldIslandMap.isIsland(worldX, worldZ) && localChunkBiomes.biomes[localX][localZ] == null) { + mapWideDFS.setStartPosition(worldX, worldZ); + if (!mapWideDFS.findTarget(this)) { + if (propagatorInfo.mainBiome == null) propagatorInfo.mainBiome = biomeSelector.getLandBiome(temperature); + if (propagatorInfo.shoreBiome == null) propagatorInfo.shoreBiome = biomeSelector.getShoreBiome(propagatorInfo.mainBiome, temperature); + if (propagatorInfo.shallowBiome == null) propagatorInfo.shallowBiome = biomeSelector.getOceanBiome(temperature); + } + propagator.setStartPosition(worldX, worldZ); + propagator.findTarget(propagatorInfo); + } + if (localChunkBiomes.biomes[localX][localZ] == null) { + localChunkBiomes.biomes[localX][localZ] = biomeSelector.getOceanBiome(temperature); + return biomeSelector.getOceanBiome(temperature); + } + return localChunkBiomes.biomes[localX][localZ]; + } + + @Override + public boolean validate(int x, int y) { + return (x / 16) == chunkCoords.getX() && (y / 16) == chunkCoords.getY() && worldIslandMap.isIsland(x, y); + } + + @Override + public boolean isCoordinateTarget(int x, int y) { + if ((propagatorInfo.shoreBiome == null || propagatorInfo.mainBiome == null) && worldIslandMap.isLand(x, y)) { + if (propagatorInfo.shoreBiome == null && worldIslandMap.isShore(x, y)) { + propagatorInfo.shoreBiome = getBiomeAt(x, y); + } else if (propagatorInfo.mainBiome != null) { + propagatorInfo.mainBiome = getBiomeAt(x, y); + } + } else if (propagatorInfo.shallowBiome == null) { + propagatorInfo.shallowBiome = getBiomeAt(x, y); + } + return (propagatorInfo.shallowBiome != null && propagatorInfo.mainBiome != null && propagatorInfo.shoreBiome != null); + } + + private Biome getBiomeAt(int worldX, int worldZ) { + int chunkX = worldX / 16; + int chunkZ = worldZ / 16; + int localX = Math.abs(worldX % 16); + int localZ = Math.abs(worldZ % 16); + + Identifier chunkBiomeID = new CoordinateIdentifier(chunkX, chunkZ); + Identifier chunkGenID = new CoordinateIdentifier(chunkX, chunkZ); + + CacheValue chunkBiomeVal = chunkBiomesCache.retrieveCache(chunkBiomeID); + CacheValue chunkGenVal = chunkGenStatusCache.retrieveCache(chunkGenID); + + if (chunkGenVal.isEmpty()) chunkGenVal.setValue(world.isChunkGenerated(worldX / 16, worldZ / 16)); + + if (chunkGenVal.getValue()) { + if (chunkBiomeVal.isEmpty()) { + chunkBiomeVal.setValue(new ChunkBiomes()); + } + + if (chunkBiomeVal.getValue().biomes[localX][localZ] == null) { + chunkBiomeVal.getValue().biomes[localX][localZ] = world.getBiome(worldX, 0, worldZ); + } + return chunkBiomeVal.getValue().biomes[localX][localZ] = world.getBiome(worldX, 0, worldZ); + } + return null; + } + + private class ChunkBiomes { + public final Biome[][] biomes = new Biome[16][16]; + } + + private class LocalIslandPropagator implements CoordinateTargetValidatable { + public Biome mainBiome = null; + public Biome shoreBiome = null; + public Biome shallowBiome = null; + + @Override + public boolean isCoordinateTarget(int x, int y) { + int localX = Math.abs(x % 16); + int localZ = Math.abs(y % 16); + if (worldIslandMap.isLand(x, y)) { + if (worldIslandMap.isShore(x, y)) { + localChunkBiomes.biomes[localX][localZ] = shoreBiome; + } else { + localChunkBiomes.biomes[localX][localZ] = mainBiome; + } + } else if (worldIslandMap.isShallowPortion(x, y)) { + localChunkBiomes.biomes[localX][localZ] = shallowBiome; + } + return false; + } + } } \ No newline at end of file