Current iteration of work.
Attempted at fixing the coordinate converter again. Added more applicable tests for them. Changing to a more stateless design for biome generator. Refactored correcting package name to fit conventions.
This commit is contained in:
parent
59d78c9754
commit
1e8dc8019a
@ -3,15 +3,14 @@ package ca.recrown.islandsurvivalcraft;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.BiomePerIslandGenerator;
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.alternation.AlternatingChunkGenerator;
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.IslandWorldChunkGenerator;
|
||||
|
||||
|
||||
public class IslandSurvivalCraft extends JavaPlugin {
|
||||
AlternatingChunkGenerator generator;
|
||||
private IslandWorldChunkGenerator chunkGenerator = new IslandWorldChunkGenerator();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
generator = new AlternatingChunkGenerator(this, new BiomePerIslandGenerator());
|
||||
super.onEnable();
|
||||
}
|
||||
|
||||
@ -22,6 +21,6 @@ public class IslandSurvivalCraft extends JavaPlugin {
|
||||
|
||||
@Override
|
||||
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
||||
return generator;
|
||||
return chunkGenerator;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ca.recrown.islandsurvivalcraft.Types;
|
||||
package ca.recrown.islandsurvivalcraft.types;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -4,7 +4,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
|
||||
public class Utilities {
|
||||
public final static int CHUNK_SIZE = 16;
|
||||
@ -53,19 +53,17 @@ public class Utilities {
|
||||
public static Point2 worldToLocalChunkCoordinates(int x, int y) {
|
||||
int xRes = 0;
|
||||
if (x < 0) {
|
||||
xRes = CHUNK_SIZE - 1 + (x % CHUNK_SIZE);
|
||||
xRes %= CHUNK_SIZE - 1;
|
||||
xRes = CHUNK_SIZE + (x % CHUNK_SIZE);
|
||||
xRes %= CHUNK_SIZE;
|
||||
} else {
|
||||
xRes = x % CHUNK_SIZE;
|
||||
if (xRes > 0) xRes -= 1;
|
||||
}
|
||||
int yRes = 0;
|
||||
if (y < 0) {
|
||||
yRes = CHUNK_SIZE - 1 + (y % CHUNK_SIZE);
|
||||
yRes %= CHUNK_SIZE - 1;
|
||||
yRes = CHUNK_SIZE + (y % CHUNK_SIZE);
|
||||
yRes %= CHUNK_SIZE;
|
||||
} else {
|
||||
yRes = y % CHUNK_SIZE;
|
||||
if (yRes > 0) yRes -= 1;
|
||||
}
|
||||
return new Point2(xRes, yRes);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package ca.recrown.islandsurvivalcraft.pathfinding;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
|
||||
public class DFSNode extends Point2 implements Comparable<DFSNode> {
|
||||
private DFSNode goal;
|
||||
|
@ -6,7 +6,7 @@ import java.util.Queue;
|
||||
|
||||
import org.apache.commons.lang.NullArgumentException;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
|
||||
public class DepthFirstSearch {
|
||||
private Queue<DFSNode> queue;
|
||||
|
@ -117,6 +117,8 @@ public class BiomeSelector {
|
||||
* @return the Minecraft temperature.
|
||||
*/
|
||||
public float getBiomeTemperature(Biome biome) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
|
||||
String biomeName = biome.name().toLowerCase();
|
||||
if (biomeName.endsWith("hills") || biomeName.endsWith("beach") || biomeName.endsWith("shore")) {
|
||||
String biomeTypeName = biomeName;
|
||||
@ -140,6 +142,8 @@ public class BiomeSelector {
|
||||
* @return the resulting transition biome.
|
||||
*/
|
||||
public Biome getTransitionalBiome(Biome from) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
|
||||
String biomeName = from.name().toLowerCase();
|
||||
if (biomeName.contains("jungle")) {
|
||||
if (biomeName.contains("modified")) {
|
||||
@ -160,6 +164,8 @@ public class BiomeSelector {
|
||||
* @return the shore biome associated with it.
|
||||
*/
|
||||
public Biome getShoreBiome(Biome from, float temperature) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
|
||||
String biomeName = from.name().toLowerCase();
|
||||
if (biomeName.contains("mushroom")) {
|
||||
return Biome.MUSHROOM_FIELD_SHORE;
|
||||
@ -173,10 +179,12 @@ public class BiomeSelector {
|
||||
}
|
||||
|
||||
public boolean isOceanBiome(Biome biome) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
return oceans.containsKey(biome);
|
||||
}
|
||||
|
||||
public boolean isLandBiome(Biome biome) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
return !isOceanBiome(biome);
|
||||
}
|
||||
|
||||
@ -186,6 +194,7 @@ public class BiomeSelector {
|
||||
* @return The randomly selected biome.
|
||||
*/
|
||||
public Biome getLandBiome(float temperature) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
ArrayList<Biome> biomes = null;
|
||||
if (temperature <= 0.05f) {
|
||||
biomes = temperaturePartitionedLandBiomes.get(0.05f);
|
||||
@ -206,6 +215,7 @@ public class BiomeSelector {
|
||||
* @return The randomly selected biome.
|
||||
*/
|
||||
public Biome getOceanBiome(float temperature) {
|
||||
if (!initialized) throw new IllegalStateException("Biome selector is not initialized.");
|
||||
ArrayList<Biome> biomes = null;
|
||||
if (temperature <= 0.00f) {
|
||||
biomes = temperaturePartitionedOceanBiomes.get(0.0f);
|
||||
|
@ -4,7 +4,7 @@ import java.util.Random;
|
||||
|
||||
import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable;
|
||||
import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch;
|
||||
|
@ -1,25 +0,0 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
|
||||
public class BedrockGenerator {
|
||||
private final SimplexOctaveGenerator noiseGenerator;
|
||||
private final int maxBedrockHeight;
|
||||
private final int minBedrockHeight;
|
||||
|
||||
public BedrockGenerator(Random random, int maxBedrockHeight, int minBedrockHeight) {
|
||||
noiseGenerator = new SimplexOctaveGenerator(random, 1);
|
||||
noiseGenerator.setScale(0.1D);
|
||||
this.maxBedrockHeight = maxBedrockHeight;
|
||||
this.minBedrockHeight = minBedrockHeight;
|
||||
}
|
||||
|
||||
private double getNormalizedNoise(int worldX, int worldZ) {
|
||||
return (noiseGenerator.noise(worldX, worldZ, 0.5D, 0.5D, true) + 1D) / 2D;
|
||||
}
|
||||
public int getBedrockHeight(int worldX, int worldZ) {
|
||||
return (int) Math.min(minBedrockHeight, getNormalizedNoise(worldX, worldZ) * maxBedrockHeight);
|
||||
}
|
||||
}
|
@ -2,13 +2,32 @@ package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
|
||||
public interface BiomeGenerator extends Listener {
|
||||
public void initialize(World world, IslandWorldMapper mapGenerator, BiomeSelector biomeSelector);
|
||||
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ);
|
||||
public BiomeGenerator getInstance();
|
||||
}
|
||||
public interface BiomeGenerator {
|
||||
|
||||
/**
|
||||
* Given a biome array, requests for it to be saturated by the IslandWorldChunkGenerator.
|
||||
* The array should store the columns of biomes for the entire chunk.
|
||||
* It doesn't need to be populated on the first call as this method will be called once for every column in the chunk.
|
||||
* However, if some biomes can be set without repetative calls, doing so will prevent this method from being called for those locals.
|
||||
* @param biomesArray The array of biomes that are to be saturated.
|
||||
* @param world The current world.
|
||||
* @param chunkX The X coordinate of the chunk.
|
||||
* @param chunkZ The Z coordinate of the chunk.
|
||||
* @param localX The X coordinate of the column within the chunk.
|
||||
* @param localZ The Z coordinate of the column within the chunk.
|
||||
* @param mapper The island mapper to be used.
|
||||
* @param tempGenerator The temperature generator to be used.
|
||||
*/
|
||||
public void generateBiomeColumn(Biome[][] biomesArray, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMapper mapper, BiomeSelector biomeSelector, TemperatureMapGenerator tempGenerator);
|
||||
|
||||
/**
|
||||
* Called when Bukkit designates a new chunk as generated.
|
||||
* @param chunkX The X coordinate of the chunk.
|
||||
* @param chunkZ The Z coordinate of the chunk.
|
||||
*/
|
||||
public void chunkGenerated(int chunkX, int chunkZ);
|
||||
}
|
@ -1,209 +0,0 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
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;
|
||||
|
||||
//Note: technically, the validators have to be run on land, and so, some condition checks may not be nessecary.
|
||||
public class BiomePerIslandGenerator implements BiomeGenerator {
|
||||
private volatile boolean initialized;
|
||||
private final TemperatureMapGenerator temperatureMapGenerator;
|
||||
private final Cache<Point2, Biome> chunkBiomesCache;
|
||||
private final Cache<Point2, Boolean> chunkGenStatusCache;
|
||||
private volatile IslandWorldMapper worldIslandMap;
|
||||
private volatile BiomeSelector biomeSelector;
|
||||
private volatile World world;
|
||||
|
||||
public BiomePerIslandGenerator() {
|
||||
this.temperatureMapGenerator = new TemperatureMapGenerator();
|
||||
chunkBiomesCache = new Cache<>(65536);
|
||||
chunkGenStatusCache = new Cache<>(65536);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final World world, final IslandWorldMapper mapGenerator, final BiomeSelector biomeSelector) {
|
||||
if (initialized) throw new IllegalStateException("Biome generator already initialized.");
|
||||
initialized = true;
|
||||
this.world = world;
|
||||
this.worldIslandMap = mapGenerator;
|
||||
this.biomeSelector = biomeSelector;
|
||||
this.temperatureMapGenerator.setSeed(world.getSeed());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onChunkLoaded(ChunkLoadEvent event) {
|
||||
if (event.isNewChunk()) {
|
||||
Chunk chunk = event.getChunk();
|
||||
if (chunk.getWorld().getUID().hashCode() == world.getUID().hashCode() && chunk.getWorld().getUID().equals(world.getUID())) {
|
||||
chunkGenStatusCache.setValue(new Point2(chunk.getX(), chunk.getZ()), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BiomeGenerator getInstance() {
|
||||
return new BiomePerIslandGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
|
||||
Point2 currChunkCoords = new Point2(chunkX, chunkZ);
|
||||
|
||||
int worldX = 16 * chunkX + localX;
|
||||
int worldZ = 16 * chunkZ + localZ;
|
||||
|
||||
Point2 worldCoords = new Point2(worldX, worldZ);
|
||||
|
||||
Biome cachedBiome = getStoredBiome(worldCoords);
|
||||
if (cachedBiome != null) return cachedBiome;
|
||||
|
||||
float temperature = temperatureMapGenerator.getTemperature(worldX, worldZ);
|
||||
if (!worldIslandMap.isIsland(worldX, worldZ)) {
|
||||
return biomeSelector.getOceanBiome(temperature);
|
||||
}
|
||||
|
||||
IslandBiomeInfo biomeInfo = new IslandBiomeInfo();
|
||||
DepthFirstSearch prevBiomeFinder = new DepthFirstSearch(biomeInfo);
|
||||
prevBiomeFinder.setStartPosition(worldX, worldZ);
|
||||
if (!prevBiomeFinder.findTarget(biomeInfo)) {
|
||||
if (biomeInfo.main == null) biomeInfo.main = biomeSelector.getLandBiome(temperature);
|
||||
if (biomeInfo.shore == null) biomeInfo.shore = biomeSelector.getShoreBiome(biomeInfo.main, temperature);
|
||||
if (biomeInfo.shallow == null) biomeInfo.shallow = biomeSelector.getOceanBiome(temperature);
|
||||
}
|
||||
PropagatorInfo propInfo = new PropagatorInfo(biomeInfo.main, biomeInfo.shore, biomeInfo.shallow, currChunkCoords);
|
||||
DepthFirstSearch prop = new DepthFirstSearch(propInfo);
|
||||
prop.setStartPosition(worldX, worldZ);
|
||||
prop.findTarget(propInfo);
|
||||
|
||||
if (worldIslandMap.isLand(worldX, worldZ)) {
|
||||
if (worldIslandMap.isShore(worldX, worldZ)) {
|
||||
return biomeInfo.shore;
|
||||
} else {
|
||||
return biomeInfo.main;
|
||||
}
|
||||
} else {
|
||||
return biomeInfo.shallow;
|
||||
}
|
||||
}
|
||||
|
||||
private Biome getStoredBiome(Point2 worldCoords) {
|
||||
Point2 chunkCoords = Utilities.worldToChunkCoordinates(worldCoords);
|
||||
|
||||
//Search our cache first.
|
||||
Biome biome = chunkBiomesCache.getValue(worldCoords);
|
||||
if (biome != null) return biome;
|
||||
|
||||
//Check to see if bukkit has anything to say about it.
|
||||
Boolean chunkGenStat = chunkGenStatusCache.getValue(chunkCoords);
|
||||
if (chunkGenStat == null) {
|
||||
chunkGenStat = world.isChunkGenerated(chunkCoords.x, chunkCoords.y);
|
||||
chunkGenStatusCache.setValue(chunkCoords, chunkGenStat);
|
||||
}
|
||||
|
||||
if (chunkGenStat) {
|
||||
biome = world.getBiome(worldCoords.x, 0, worldCoords.y);
|
||||
chunkBiomesCache.setValue(worldCoords, biome);
|
||||
return biome;
|
||||
}
|
||||
|
||||
//Guess it's not cached or created.
|
||||
return null;
|
||||
}
|
||||
|
||||
private void setCacheBiome(Point2 worldCoords, Biome biome) {
|
||||
//Set it in cache.
|
||||
chunkBiomesCache.setValue(worldCoords, biome);
|
||||
}
|
||||
|
||||
private class PropagatorInfo implements CoordinateTargetValidatable, CoordinateValidatable {
|
||||
public final Biome main, shore, shallow;
|
||||
private final Point2 currentChunkCoords;
|
||||
private boolean hasSet;
|
||||
|
||||
@Override
|
||||
public boolean isCoordinateTarget(int x, int y) {
|
||||
hasSet = false;
|
||||
Point2 worldCoords = new Point2(x, y);
|
||||
if (worldIslandMap.isLand(x, y)) {
|
||||
if (worldIslandMap.isShore(x, y)) {
|
||||
setCacheBiome(worldCoords, shore);
|
||||
hasSet = true;
|
||||
} else {
|
||||
setCacheBiome(worldCoords, main);
|
||||
hasSet = true;
|
||||
}
|
||||
} else {
|
||||
setCacheBiome(worldCoords, shallow);
|
||||
hasSet = true;
|
||||
}
|
||||
if (!hasSet) throw new IllegalStateException();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(int x, int y) {
|
||||
Point2 chunkCoords = Utilities.worldToChunkCoordinates(x, y);
|
||||
return chunkCoords.fastEquals(currentChunkCoords) && worldIslandMap.isIsland(x, y);
|
||||
}
|
||||
|
||||
public PropagatorInfo(Biome main, Biome shore, Biome shallow, Point2 currentChunkCoords) {
|
||||
this.main = main;
|
||||
this.shore = shore;
|
||||
this.shallow = shallow;
|
||||
this.currentChunkCoords = currentChunkCoords;
|
||||
}
|
||||
}
|
||||
|
||||
private class IslandBiomeInfo implements CoordinateTargetValidatable, CoordinateValidatable {
|
||||
private boolean isLand, isShore, isShallow;
|
||||
public Biome main, shore, shallow;
|
||||
|
||||
@Override
|
||||
public boolean validate(int x, int y) {
|
||||
isShore = false;
|
||||
isShallow = false;
|
||||
isLand = false;
|
||||
return worldIslandMap.isIsland(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCoordinateTarget(int x, int y) {
|
||||
Point2 worldCoords = new Point2(x, y);
|
||||
|
||||
if ((isLand = worldIslandMap.isLand(x, y))) {
|
||||
isShore = worldIslandMap.isShore(x, y);
|
||||
} else {
|
||||
isShallow = worldIslandMap.isShallowPortion(x, y);
|
||||
}
|
||||
|
||||
if (main == null && isLand && !isShore) {
|
||||
main = getStoredBiome(worldCoords);
|
||||
} else
|
||||
if (shore == null && isShore) {
|
||||
shore = getStoredBiome(worldCoords);
|
||||
} else
|
||||
if (shallow == null && isShallow) {
|
||||
shallow = getStoredBiome(worldCoords);
|
||||
}
|
||||
return allBiomesAcquired();
|
||||
}
|
||||
|
||||
public boolean allBiomesAcquired() {
|
||||
return shore != null && main != null && shallow != null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.world.ChunkLoadEvent;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
import ca.recrown.islandsurvivalcraft.world.shaders.WorldHeightShader;
|
||||
|
||||
public class IslandWorldChunkGenerator extends ChunkGenerator implements Listener {
|
||||
private final BiomeGenerator biomeGenerator;
|
||||
private final TemperatureMapGenerator temperatureGenerator = new TemperatureMapGenerator();
|
||||
private volatile Random random;
|
||||
private volatile IslandWorldMapper mapper;
|
||||
private volatile BiomeSelector biomeSelector;
|
||||
private volatile World world;
|
||||
private volatile WorldHeightShader heightShader;
|
||||
|
||||
public IslandWorldChunkGenerator(BiomeGenerator biomeGenerator) {
|
||||
this.biomeGenerator = biomeGenerator;
|
||||
}
|
||||
|
||||
public IslandWorldChunkGenerator() {
|
||||
this.biomeGenerator = new UniBiomeIslandGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome) {
|
||||
if (this.random != random) {
|
||||
this.random = random;
|
||||
mapper = new IslandWorldMapper(random);
|
||||
temperatureGenerator.setSeed(world.getSeed());
|
||||
biomeSelector = new BiomeSelector(random);
|
||||
biomeSelector.initialize();
|
||||
this.world = world;
|
||||
this.heightShader = new WorldHeightShader(world.getSeed(), mapper, world.getSeaLevel(), world.getMaxHeight(), 3);
|
||||
}
|
||||
int maxHeight = world.getMaxHeight();
|
||||
ChunkData chunkData = createChunkData(world);
|
||||
|
||||
Biome[][] biomes = new Biome[Utilities.CHUNK_SIZE][Utilities.CHUNK_SIZE];
|
||||
for (int localX = 0; localX < Utilities.CHUNK_SIZE; localX++) {
|
||||
for (int localZ = 0; localZ < Utilities.CHUNK_SIZE; localZ++) {
|
||||
if (biomes[localX][localZ] == null) {
|
||||
biomeGenerator.generateBiomeColumn(biomes, world, x, z, localX, localZ, mapper, biomeSelector, temperatureGenerator);
|
||||
}
|
||||
if (biomes[localX][localZ] == null) throw new IllegalStateException("Biome was null.");
|
||||
for (int y = 0; y < maxHeight; y++) {
|
||||
biome.setBiome(x, y, z, biomes[localX][localZ]);
|
||||
}
|
||||
int worldX = 16 * x + localX;
|
||||
int worldZ = 16 * z + localZ;
|
||||
int height = heightShader.getAltitude(worldX, worldZ, biomes[localX][localZ]);
|
||||
chunkData.setRegion(localX, 1, localZ, localX+1, height, localZ+1, Material.DIAMOND_BLOCK);
|
||||
}
|
||||
}
|
||||
chunkData.setRegion(0, 0, 0, 16, 1, 16, Material.BEDROCK);
|
||||
return super.generateChunkData(world, random, x, z, biome);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onChunkLoaded(ChunkLoadEvent event) {
|
||||
if (event.isNewChunk()) {
|
||||
if (world == event.getWorld()) {
|
||||
Chunk chunk = event.getChunk();
|
||||
biomeGenerator.chunkGenerated(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ import java.util.Random;
|
||||
|
||||
import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
|
||||
class TemperatureMapGenerator {
|
||||
|
@ -0,0 +1,154 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
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 UniBiomeIslandGenerator implements BiomeGenerator {
|
||||
private final Cache<Point2, Biome> biomeCache = new Cache<>(12800);
|
||||
private final Cache<Point2, Boolean> existenceCache = new Cache<>(12800);
|
||||
|
||||
@Override
|
||||
public void generateBiomeColumn(Biome[][] biomes, World world, int chunkX, int chunkZ, int localX, int localZ, IslandWorldMapper mapper, BiomeSelector biomeSelector, TemperatureMapGenerator tempGen) {
|
||||
int worldX = 16 * chunkX + localX;
|
||||
int worldZ = 16 * chunkZ + localZ;
|
||||
Point2 chunkCoords = Utilities.worldToChunkCoordinates(new Point2(worldX, worldZ));
|
||||
existenceCache.setValue(chunkCoords, false);
|
||||
|
||||
//Check if we can just give it something in cache.
|
||||
Biome biome = getSavedBiome(world, worldX, worldZ);
|
||||
if (biome != null) {
|
||||
biomes[localX][localZ] = biome;
|
||||
return;
|
||||
}
|
||||
|
||||
//Fine, check if it's ocean.
|
||||
if (!mapper.isIsland(worldX, worldZ)) {
|
||||
setCacheBiome(worldX, worldZ, biomeSelector.getOceanBiome(tempGen.getTemperature(worldX, worldZ)), biomes);
|
||||
return;
|
||||
}
|
||||
|
||||
//Shoot, looks like it's actually an island.
|
||||
DepthFirstSearch search = new DepthFirstSearch();
|
||||
search.setValidatable(mapper);
|
||||
|
||||
IslandInfo islandInfo = new IslandInfo(mapper, world);
|
||||
search.setStartPosition(worldX, worldZ);
|
||||
if (!search.findTarget(islandInfo)) {
|
||||
float temp = tempGen.getTemperature(worldX, worldZ);
|
||||
if (islandInfo.main == null) islandInfo.main = biomeSelector.getLandBiome(temp);
|
||||
if (islandInfo.shore == null) islandInfo.shore = biomeSelector.getShoreBiome(islandInfo.main, temp);
|
||||
if (islandInfo.shallow == null) islandInfo.shallow = biomeSelector.getOceanBiome(temp);
|
||||
}
|
||||
|
||||
PropagatorInfo propInfo = new PropagatorInfo(islandInfo, biomes, new Point2(chunkX, chunkZ), mapper);
|
||||
search.setValidatable(propInfo);
|
||||
search.setStartPosition(worldX, worldZ);
|
||||
search.findTarget(propInfo);
|
||||
}
|
||||
|
||||
private Biome getSavedBiome(World world, int worldX, int worldZ) {
|
||||
Point2 worldCoords = new Point2(worldX, worldZ);
|
||||
Biome res = null;
|
||||
|
||||
|
||||
res = biomeCache.getValue(worldCoords);
|
||||
if (res != null) return res;
|
||||
|
||||
Point2 chunkCoords = Utilities.worldToChunkCoordinates(worldCoords);
|
||||
Boolean chunkExists = existenceCache.getValue(chunkCoords);
|
||||
if (chunkExists == null) {
|
||||
chunkExists = world.isChunkGenerated(worldX, worldZ);
|
||||
existenceCache.setValue(chunkCoords, chunkExists);
|
||||
}
|
||||
|
||||
if (chunkExists) {
|
||||
res = world.getBiome(worldX, 0, worldZ);
|
||||
biomeCache.setValue(worldCoords, res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public void chunkGenerated(int chunkX, int chunkZ) {
|
||||
existenceCache.setValue(new Point2(chunkX, chunkZ), true);
|
||||
}
|
||||
|
||||
private void setCacheBiome(int worldX, int worldZ, Biome biome, Biome[][] biomes) {
|
||||
Point2 worldCoords = new Point2(worldX, worldZ);
|
||||
if (biomes != null) {
|
||||
Point2 localCoords = Utilities.worldToLocalChunkCoordinates(worldCoords);
|
||||
biomes[localCoords.x][localCoords.y] = biome;
|
||||
}
|
||||
biomeCache.setValue(worldCoords, biome);
|
||||
}
|
||||
|
||||
private class IslandInfo implements CoordinateTargetValidatable {
|
||||
public final IslandWorldMapper mapper;
|
||||
public final World world;
|
||||
public Biome main, shore, shallow;
|
||||
|
||||
public IslandInfo(IslandWorldMapper mapper, World world) {
|
||||
this.mapper = mapper;
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCoordinateTarget(int x, int y) {
|
||||
if (mapper.isLand(x, y)) {
|
||||
if (mapper.isShore(x, y)) {
|
||||
shore = getSavedBiome(world, x, y);
|
||||
} else {
|
||||
main = getSavedBiome(world, x, y);
|
||||
}
|
||||
} else {
|
||||
shallow = getSavedBiome(world, x, y);
|
||||
}
|
||||
return main != null && shore != null && shallow != null;
|
||||
}
|
||||
}
|
||||
|
||||
private class PropagatorInfo implements CoordinateTargetValidatable, CoordinateValidatable {
|
||||
private final Biome shallow, shore, main;
|
||||
private final Biome[][] biomes;
|
||||
private final Point2 chunkCoords;
|
||||
private final IslandWorldMapper mapper;
|
||||
|
||||
public PropagatorInfo(IslandInfo islandInfo, Biome[][] biomes, Point2 chunkCoords, IslandWorldMapper mapper) {
|
||||
this.shallow = islandInfo.shallow;
|
||||
this.shore = islandInfo.shore;
|
||||
this.main = islandInfo.main;
|
||||
this.biomes = biomes;
|
||||
this.chunkCoords = chunkCoords;
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate(int x, int y) {
|
||||
Point2 chunkCoords = Utilities.worldToChunkCoordinates(new Point2(x, y));
|
||||
return this.chunkCoords.fastEquals(chunkCoords) && mapper.validate(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCoordinateTarget(int x, int y) {
|
||||
if (mapper.isLand(x, y)) {
|
||||
if (mapper.isShore(x, y)) {
|
||||
setCacheBiome(x, y, shore, biomes);
|
||||
} else {
|
||||
setCacheBiome(x, y, main, biomes);
|
||||
}
|
||||
} else {
|
||||
setCacheBiome(x, y, shallow, biomes);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
|
||||
import org.bukkit.generator.ChunkGenerator.ChunkData;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
import ca.recrown.islandsurvivalcraft.world.shaders.WorldHeightShader;
|
||||
|
||||
/**
|
||||
* A world generator.
|
||||
*/
|
||||
public class WorldGenerator {
|
||||
private final int maxHeight;
|
||||
private final BedrockGenerator bedrockGenerator;
|
||||
private final BiomeSelector biomeSelector;
|
||||
private final BiomeGenerator biomeGenerator;
|
||||
private final WorldHeightShader heightShader;
|
||||
public final World world;
|
||||
public final IslandWorldMapper islandMapGenerator;
|
||||
public final Random random;
|
||||
|
||||
public WorldGenerator(JavaPlugin plugin, World world, BiomeGenerator islandBiomeGenerator, Random random) {
|
||||
this.world = world;
|
||||
this.maxHeight = world.getMaxHeight();
|
||||
this.random = random;
|
||||
this.biomeSelector = new BiomeSelector(random);
|
||||
this.islandMapGenerator = new IslandWorldMapper(random);
|
||||
this.biomeGenerator = islandBiomeGenerator;
|
||||
int bedrockMaxHeight = 5;
|
||||
int bedrockMinHeight = 1;
|
||||
this.bedrockGenerator = new BedrockGenerator(random, bedrockMaxHeight, bedrockMinHeight);
|
||||
this.heightShader = new WorldHeightShader(world.getSeed(), islandMapGenerator, world.getSeaLevel(), maxHeight,
|
||||
bedrockMaxHeight + 2);
|
||||
biomeSelector.initialize();
|
||||
islandBiomeGenerator.initialize(world, islandMapGenerator, biomeSelector);
|
||||
plugin.getServer().getPluginManager().registerEvents(islandBiomeGenerator, plugin);
|
||||
}
|
||||
|
||||
public void GenerateChunk(int chunkX, int chunkZ, int localX, int localZ, ChunkData chunk, BiomeGrid biomeGrid) {
|
||||
int worldX = 16 * chunkX + localX;
|
||||
int worldZ = 16 * chunkZ + localZ;
|
||||
|
||||
|
||||
//Sets the biome.
|
||||
Biome currentBiome = biomeGenerator.GenerateBiome(chunkX, chunkZ, localX, localZ);
|
||||
if (currentBiome == null) throw new IllegalStateException("Biome generated was null!");
|
||||
for (int y = 0; y < maxHeight; y++) {
|
||||
biomeGrid.setBiome(localX, y, localZ, currentBiome);
|
||||
}
|
||||
|
||||
|
||||
//get height shader.
|
||||
int height = 0;
|
||||
try {
|
||||
height = heightShader.getAltitude(worldX, worldZ, currentBiome);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
e.printStackTrace();
|
||||
height = maxHeight;
|
||||
}
|
||||
if (height == 0) throw new IllegalStateException("Height generated was null!");
|
||||
|
||||
|
||||
// gets the bedrock height.
|
||||
int bedrockHeight = bedrockGenerator.getBedrockHeight(worldX, worldZ);
|
||||
|
||||
//set general shape
|
||||
chunk.setRegion(localX, bedrockHeight, localZ, localX + 1, height, localZ + 1, Material.STONE);
|
||||
|
||||
//set bedrock last
|
||||
chunk.setRegion(localX, 0, localZ, localX + 1, bedrockHeight, localZ + 1, Material.BEDROCK);
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation.alternation;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.BiomeGenerator;
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.WorldGenerator;
|
||||
|
||||
public class AlternatingChunkGenerator extends ChunkGenerator {
|
||||
private final WorldGeneratorAlternator alternator;
|
||||
|
||||
public AlternatingChunkGenerator(JavaPlugin plugin, BiomeGenerator biomeGenerator) {
|
||||
alternator = new WorldGeneratorAlternator(plugin, biomeGenerator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) {
|
||||
WorldGenerator worldGenerator = alternator.getGenerator(world, random);
|
||||
ChunkData chunk = createChunkData(world);
|
||||
|
||||
for (int localX = 0; localX < 16; localX++) {
|
||||
for (int localZ = 0; localZ < 16; localZ++) {
|
||||
worldGenerator.GenerateChunk(chunkX, chunkZ, localX, localZ, chunk, biome);
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation.alternation;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.BiomeGenerator;
|
||||
import ca.recrown.islandsurvivalcraft.world.generation.WorldGenerator;
|
||||
|
||||
/**
|
||||
* Alternates the data used on a per world basis.
|
||||
* Uses IslandWorldGenerator as the container for each world.
|
||||
*/
|
||||
public class WorldGeneratorAlternator {
|
||||
private final HashMap<UUID, WorldGenerator> chunkGenerators;
|
||||
private volatile WorldGenerator lastGenerator;
|
||||
private volatile UUID lastUUID;
|
||||
private final BiomeGenerator islandBiomeGenerator;
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
|
||||
private final ReadLock readLock = lock.readLock();
|
||||
private final WriteLock writeLock = lock.writeLock();
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public WorldGeneratorAlternator(JavaPlugin plugin, BiomeGenerator biomeGenerator) {
|
||||
this.chunkGenerators = new HashMap<>();
|
||||
this.islandBiomeGenerator = biomeGenerator;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public WorldGenerator getGenerator(World world, Random random) {
|
||||
if (lastUUID == null || (lastUUID.hashCode() != world.getUID().hashCode() && lastUUID.equals(world.getUID()))) {
|
||||
System.out.println("Alternating generator for world: " + world.getName());
|
||||
lastUUID = world.getUID();
|
||||
readLock.lock();
|
||||
try {
|
||||
lastGenerator = chunkGenerators.get(lastUUID);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
if (lastGenerator == null) {
|
||||
lastGenerator = new WorldGenerator(plugin, world, islandBiomeGenerator, random);
|
||||
writeLock.lock();
|
||||
try {
|
||||
chunkGenerators.put(lastUUID, lastGenerator);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastGenerator;
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.shaders;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.block.Biome;
|
||||
@ -27,7 +26,7 @@ public class WorldHeightShader {
|
||||
this.minimumHeight = minimumHeight;
|
||||
}
|
||||
|
||||
public int getAltitude(int worldX, int worldZ, Biome biome) throws InvalidAlgorithmParameterException {
|
||||
public int getAltitude(int worldX, int worldZ, Biome biome) {
|
||||
double modifier = getHeightModifier(worldX, worldZ);
|
||||
int baseValue = calculateTerrainHeight(worldX, worldZ);
|
||||
int height = 0;
|
||||
@ -50,13 +49,13 @@ public class WorldHeightShader {
|
||||
height = (int) (getNormalizedHeightModifier(worldX, worldZ) * 5D + baseValue);
|
||||
}
|
||||
|
||||
if (height > worldHeight) throw new InvalidAlgorithmParameterException("Resulting height is greater than world height! Biome this occurred on: " + biomeName);
|
||||
if (height > worldHeight) throw new IllegalStateException("Resulting height is greater than world height! Biome this occurred on: " + biomeName);
|
||||
return height;
|
||||
}
|
||||
|
||||
private int calculateTerrainHeight(int worldX, int worldZ) {
|
||||
double islandValue = islandLocator.getWorldBlockValue(worldX, worldZ) + 1D;
|
||||
islandValue *= worldHeight/2;
|
||||
islandValue *= worldHeight/4;
|
||||
return (int) Math.max(Math.min(seaLevel, islandValue), minimumHeight);
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
@ -85,8 +85,8 @@ public class UtilitiesTest {
|
||||
|
||||
@Test
|
||||
public void testWorldToLocalCoordinatesPositive() {
|
||||
assertEquals(new Point2(6, 2), Utilities.worldToLocalChunkCoordinates(39, 83));
|
||||
assertEquals(new Point2(6, 2), Utilities.worldToLocalChunkCoordinates(new Point2(39, 83)));
|
||||
assertEquals(new Point2(7, 3), Utilities.worldToLocalChunkCoordinates(39, 83));
|
||||
assertEquals(new Point2(7, 3), Utilities.worldToLocalChunkCoordinates(new Point2(39, 83)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -94,4 +94,31 @@ public class UtilitiesTest {
|
||||
assertEquals(new Point2(0, 0), Utilities.worldToLocalChunkCoordinates(1024, 32));
|
||||
assertEquals(new Point2(0, 0), Utilities.worldToLocalChunkCoordinates(new Point2(16, 80)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorldToLocalCoordinatesPositiveEntirety() {
|
||||
int chunkX = 4;
|
||||
int chunkZ = 3;
|
||||
for (int x = 0; x < Utilities.CHUNK_SIZE; x++) {
|
||||
for (int z = 0; z < Utilities.CHUNK_SIZE; z++) {
|
||||
int worldX = Utilities.CHUNK_SIZE * chunkX + x;
|
||||
int worldZ = Utilities.CHUNK_SIZE * chunkZ + z;
|
||||
assertEquals(new Point2(x, z), Utilities.worldToLocalChunkCoordinates(new Point2(worldX, worldZ)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorldToLocalCoordinatesNegativeEntirety() {
|
||||
int chunkX = -42;
|
||||
int chunkZ = -3;
|
||||
for (int x = 0; x < Utilities.CHUNK_SIZE; x++) {
|
||||
for (int z = 0; z < Utilities.CHUNK_SIZE; z++) {
|
||||
int worldX = Utilities.CHUNK_SIZE * chunkX + x;
|
||||
int worldZ = Utilities.CHUNK_SIZE * chunkZ + z;
|
||||
assertEquals(new Point2(x, z), Utilities.worldToLocalChunkCoordinates(new Point2(worldX, worldZ)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user