New iteration of biome generator.

Untested.
This commit is contained in:
Harrison Deng 2020-04-25 00:18:53 -05:00
parent e93f61c055
commit a412986044

View File

@ -1,13 +1,13 @@
package ca.recrown.islandsurvivalcraft.world.generation; package ca.recrown.islandsurvivalcraft.world.generation;
import org.bukkit.Chunk;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import ca.recrown.islandsurvivalcraft.caching.Cache; import ca.recrown.islandsurvivalcraft.caching.Cache;
import ca.recrown.islandsurvivalcraft.caching.CacheValue; import ca.recrown.islandsurvivalcraft.caching.CacheValue;
import ca.recrown.islandsurvivalcraft.caching.CoordinateIdentifier; import ca.recrown.islandsurvivalcraft.caching.CoordinateIdentifier;
import ca.recrown.islandsurvivalcraft.caching.Identifier;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable; import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateTargetValidatable;
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable; import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable;
import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch; import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch;
@ -15,19 +15,19 @@ import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper; import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
//Note: technically, the validators have to be run on land, and so, some condition checks may not be nessecary. //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 { public class BiomePerIslandGenerator implements IslandBiomeGenerator {
private final Cache<ChunkBiomes> chunkBiomesCache;
private final Cache<Boolean> chunkGenStatusCache;
private final TemperatureMapGenerator temperatureMapGenerator;
private boolean initialized; private boolean initialized;
private final TemperatureMapGenerator temperatureMapGenerator;
private final Cache<Biome[][]> chunkBiomesCache;
private final Cache<Boolean> chunkGenStatusCache;
private IslandWorldMapper worldIslandMap; private IslandWorldMapper worldIslandMap;
private BiomeSelector biomeSelector; private BiomeSelector biomeSelector;
private World world; private World world;
private DepthFirstSearch mapWideDFS; private final DepthFirstSearch freshCachePropagator;
private DepthFirstSearch propagator; private final DepthFirstSearch existenceChecker;
private LocalIslandPropagator propagatorInfo; private FreshCachePropagationInfo freshCachePropInfo;
CoordinateIdentifier chunkCoords; private PreviousGenerationInfo existenceInfo;
ChunkBiomes localChunkBiomes; CoordinateIdentifier currChunkCoords;
float temperature; float temperature;
@ -35,20 +35,20 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator, Coordinate
this.temperatureMapGenerator = new TemperatureMapGenerator(); this.temperatureMapGenerator = new TemperatureMapGenerator();
chunkBiomesCache = new Cache<>(1024); chunkBiomesCache = new Cache<>(1024);
chunkGenStatusCache = new Cache<>(1024); chunkGenStatusCache = new Cache<>(1024);
propagatorInfo = new LocalIslandPropagator(); freshCachePropInfo = new FreshCachePropagationInfo();
freshCachePropagator = new DepthFirstSearch(freshCachePropInfo);
existenceInfo = new PreviousGenerationInfo();
existenceChecker = new DepthFirstSearch(existenceInfo);
} }
@Override @Override
public void initialize(final World world, final IslandWorldMapper mapGenerator, final BiomeSelector biomeSelector) { public void initialize(final World world, final IslandWorldMapper mapGenerator, final BiomeSelector biomeSelector) {
if (initialized) throw new IllegalStateException("Biome generator already initialized."); if (initialized) throw new IllegalStateException("Biome generator already initialized.");
initialized = true; initialized = true;
this.world = world;
this.worldIslandMap = mapGenerator; this.worldIslandMap = mapGenerator;
this.biomeSelector = biomeSelector; this.biomeSelector = biomeSelector;
this.world = world;
this.temperatureMapGenerator.setSeed(world.getSeed()); this.temperatureMapGenerator.setSeed(world.getSeed());
mapWideDFS = new DepthFirstSearch(worldIslandMap);
mapWideDFS.setEndPosition(0, 0);
propagator = new DepthFirstSearch(256, this);
} }
@Override @Override
@ -57,102 +57,121 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator, Coordinate
} }
@Override @Override
public Biome GenerateBiome(final int chunkX, final int chunkZ, final int localX, final int localZ) { public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
if (chunkCoords == null || chunkCoords.getX() != chunkX || chunkCoords.getY() != chunkZ) { if (currChunkCoords == null || chunkX != currChunkCoords.getX() || chunkZ != currChunkCoords.getY()) {
chunkCoords = new CoordinateIdentifier(chunkX, chunkZ); currChunkCoords = new CoordinateIdentifier(chunkX, chunkZ);
chunkGenStatusCache.retrieveCache(chunkCoords).setValue(true);
localChunkBiomes = new ChunkBiomes();
chunkBiomesCache.retrieveCache(chunkCoords).setValue(localChunkBiomes);
} }
int worldX = 16 * chunkX + localX; int worldX = localX + 16 * chunkX;
int worldZ = 16 * chunkZ + localZ; int worldZ = localZ + 16 * chunkZ;
this.temperature = temperatureMapGenerator.getTemperature(worldX, worldZ); Biome cachedBiome = getBiome(worldX, worldZ);
if (worldIslandMap.isIsland(worldX, worldZ) && localChunkBiomes.biomes[localX][localZ] == null) { if (cachedBiome != null) return cachedBiome;
mapWideDFS.setStartPosition(worldX, worldZ); temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
if (!mapWideDFS.findTarget(this)) {
if (propagatorInfo.mainBiome == null) propagatorInfo.mainBiome = biomeSelector.getLandBiome(temperature); if (!worldIslandMap.isIsland(worldX, worldZ)) {
if (propagatorInfo.shoreBiome == null) propagatorInfo.shoreBiome = biomeSelector.getShoreBiome(propagatorInfo.mainBiome, temperature); return biomeSelector.getOceanBiome(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); freshCachePropInfo.clear();
existenceChecker.setStartPosition(worldX, worldZ);
if (!existenceChecker.findTarget(existenceInfo)) {
if (freshCachePropInfo.main == null) freshCachePropInfo.main = biomeSelector.getLandBiome(temperature);
if (freshCachePropInfo.shore == null) freshCachePropInfo.shore = biomeSelector.getShoreBiome(freshCachePropInfo.main, temperature);
if (freshCachePropInfo.shallow == null) freshCachePropInfo.shallow = biomeSelector.getOceanBiome(temperature);
} }
return localChunkBiomes.biomes[localX][localZ]; freshCachePropagator.setStartPosition(worldX, worldZ);
freshCachePropagator.findTarget(freshCachePropInfo);
return getBiome(worldX, worldZ);
} }
@Override private Biome getBiome(int worldX, int worldZ) {
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 localX = Math.abs(worldX % 16);
int localZ = Math.abs(worldZ % 16); int localZ = Math.abs(worldZ % 16);
CoordinateIdentifier chunkCoords = new CoordinateIdentifier(worldX / 16, worldZ / 16);
Identifier chunkBiomeID = new CoordinateIdentifier(chunkX, chunkZ); CacheValue<Biome[][]> chunkBiomes = chunkBiomesCache.retrieveCache(chunkCoords);
Identifier chunkGenID = new CoordinateIdentifier(chunkX, chunkZ); if (!chunkBiomes.isEmpty()) {
Biome biome = chunkBiomes.getValue()[localX][localZ];
if (biome != null) return biome;
}
CacheValue<ChunkBiomes> chunkBiomeVal = chunkBiomesCache.retrieveCache(chunkBiomeID); CacheValue<Boolean> chunkGenStatus = chunkGenStatusCache.retrieveCache(chunkCoords);
CacheValue<Boolean> chunkGenVal = chunkGenStatusCache.retrieveCache(chunkGenID); if (chunkGenStatus.isEmpty())
chunkGenStatus.setValue(world.isChunkGenerated(chunkCoords.getX(), chunkCoords.getY()));
if (chunkGenVal.isEmpty()) chunkGenVal.setValue(world.isChunkGenerated(worldX / 16, worldZ / 16));
if (chunkGenStatus.getValue()) {
if (chunkGenVal.getValue()) { if (chunkBiomes.isEmpty()) chunkBiomes.setValue(new Biome[16][16]);
if (chunkBiomeVal.isEmpty()) { chunkBiomes.getValue()[localX][localZ] = world.getBiome(worldX, 0, worldZ);
chunkBiomeVal.setValue(new ChunkBiomes()); return chunkBiomes.getValue()[localX][localZ];
}
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; return null;
} }
private class ChunkBiomes { private void setCacheBiome(int worldX, int worldZ, Biome biome) {
public final Biome[][] biomes = new Biome[16][16]; int localX = Math.abs(worldX % 16);
int localZ = Math.abs(worldZ % 16);
CoordinateIdentifier chunkCoords = new CoordinateIdentifier(worldX / 16, worldZ / 16);
CacheValue<Biome[][]> chunkBiomes = chunkBiomesCache.retrieveCache(chunkCoords);
if (chunkBiomes.isEmpty()) chunkBiomes.setValue(new Biome[16][16]);
chunkBiomes.getValue()[localX][localZ] = biome;
} }
private class LocalIslandPropagator implements CoordinateTargetValidatable { private class FreshCachePropagationInfo implements CoordinateTargetValidatable, CoordinateValidatable {
public Biome mainBiome = null; public Biome shore;
public Biome shoreBiome = null; public Biome main;
public Biome shallowBiome = null; public Biome shallow;
@Override @Override
public boolean isCoordinateTarget(int x, int y) { 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.isLand(x, y)) {
if (worldIslandMap.isShore(x, y)) { if (worldIslandMap.isShore(x, y)) {
localChunkBiomes.biomes[localX][localZ] = shoreBiome; setCacheBiome(x, y, shore);
} else { } else {
localChunkBiomes.biomes[localX][localZ] = mainBiome; setCacheBiome(x, y, main);
} }
} else if (worldIslandMap.isShallowPortion(x, y)) { } else {
localChunkBiomes.biomes[localX][localZ] = shallowBiome; setCacheBiome(x, y, shallow);
} }
return false; return false;
} }
@Override
public boolean validate(int x, int y) {
return x / 16 == currChunkCoords.getX() && y / 16 == currChunkCoords.getY() && worldIslandMap.isIsland(x, y);
}
public boolean allBiomesAcquired() {
return shore != null && main != null && shallow != null;
}
public void clear() {
main = null;
shore = null;
shallow = null;
}
}
private class PreviousGenerationInfo implements CoordinateTargetValidatable, CoordinateValidatable {
@Override
public boolean validate(int x, int y) {
return worldIslandMap.isIsland(x, y);
}
@Override
public boolean isCoordinateTarget(int x, int y) {
if (freshCachePropInfo.main == null && worldIslandMap.isLand(x, y) && !worldIslandMap.isShore(x, y)) {
freshCachePropInfo.main = getBiome(x, y);
}
if (freshCachePropInfo.shore == null && worldIslandMap.isShore(x, y)) {
freshCachePropInfo.shore = getBiome(x, y);
}
if (freshCachePropInfo.shallow == null && worldIslandMap.isShallowPortion(x, y)) {
freshCachePropInfo.shallow = getBiome(x, y);
}
return freshCachePropInfo.allBiomesAcquired();
}
} }
} }