Attempted at making all things world gen thread proof.
Specifically, caching is tested thread proof.
This commit is contained in:
parent
f20515fd45
commit
98e4265db7
@ -3,15 +3,13 @@ package ca.recrown.islandsurvivalcraft.Types;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Point2 {
|
||||
public int x, y;
|
||||
public final int x, y;
|
||||
private final int hash;
|
||||
|
||||
public Point2(int x, int y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public Point2() {
|
||||
this(0, 0);
|
||||
this.hash = Objects.hash(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -23,8 +21,12 @@ public class Point2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean fastEquals(Point2 point) {
|
||||
return point.hashCode() == this.hash && x == point.x && y == point.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(x, y);
|
||||
return hash;
|
||||
}
|
||||
}
|
@ -2,16 +2,19 @@ package ca.recrown.islandsurvivalcraft.caching;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class Cache<Key, Value> {
|
||||
private final int maxSize;
|
||||
private final ConcurrentHashMap<Key, CacheValue<Value>> data;
|
||||
private final ConcurrentLinkedQueue<Key> occurrenceOrder;
|
||||
private ReentrantLock cleaningLock;
|
||||
|
||||
public Cache(int maxSize) {
|
||||
data = new ConcurrentHashMap<>(maxSize);
|
||||
occurrenceOrder = new ConcurrentLinkedQueue<>();
|
||||
this.maxSize = maxSize;
|
||||
cleaningLock = new ReentrantLock(true);
|
||||
}
|
||||
|
||||
public Cache() {
|
||||
@ -19,18 +22,27 @@ public class Cache<Key, Value> {
|
||||
}
|
||||
|
||||
public void setValue(Key key, Value value) {
|
||||
data.put(key, new CacheValue<>(value));
|
||||
CacheValue<Value> previous = data.get(key);
|
||||
CacheValue<Value> fresh = new CacheValue<>(value);
|
||||
if (previous != null) fresh.occurrence += previous.occurrence;
|
||||
occurrenceOrder.add(key);
|
||||
data.put(key, fresh);
|
||||
|
||||
if (data.size() > maxSize) {
|
||||
int occ = 0;
|
||||
do {
|
||||
Key potentialKey = occurrenceOrder.poll();
|
||||
CacheValue<Value> potential = data.get(potentialKey);
|
||||
potential.decreaseOccurence();
|
||||
occ = potential.getOccurrence();
|
||||
if (occ < 1) {
|
||||
data.remove(potentialKey);
|
||||
}
|
||||
cleaningLock.lock();
|
||||
try {
|
||||
Key potentialKey = occurrenceOrder.poll();
|
||||
CacheValue<Value> potential = data.get(potentialKey);
|
||||
potential.occurrence--;
|
||||
occ = potential.occurrence;
|
||||
if (occ < 1) {
|
||||
data.remove(potentialKey);
|
||||
}
|
||||
} finally {
|
||||
cleaningLock.unlock();
|
||||
}
|
||||
} while (occ > 0);
|
||||
}
|
||||
}
|
||||
@ -43,12 +55,18 @@ public class Cache<Key, Value> {
|
||||
* @return the value associated to the key.
|
||||
*/
|
||||
public Value getValue(Key key) {
|
||||
CacheValue<Value> res = data.get(key);
|
||||
if (res == null) return null;
|
||||
CacheValue<Value> res = null;
|
||||
cleaningLock.lock();
|
||||
try {
|
||||
res = data.get(key);
|
||||
if (res == null) return null;
|
||||
res.occurrence++;
|
||||
occurrenceOrder.add(key);
|
||||
} finally {
|
||||
cleaningLock.unlock();
|
||||
}
|
||||
|
||||
res.increaseOccurrence();
|
||||
occurrenceOrder.add(key);
|
||||
return res.getValue();
|
||||
return res.data;
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
|
@ -1,32 +1,10 @@
|
||||
package ca.recrown.islandsurvivalcraft.caching;
|
||||
|
||||
public class CacheValue <ValueType> {
|
||||
private int occurrence = 1;
|
||||
private final ValueType value;
|
||||
|
||||
/**
|
||||
* @return the occurrence
|
||||
*/
|
||||
public int getOccurrence() {
|
||||
return occurrence;
|
||||
}
|
||||
|
||||
public void increaseOccurrence() {
|
||||
occurrence++;
|
||||
}
|
||||
|
||||
public void decreaseOccurence() {
|
||||
occurrence--;
|
||||
}
|
||||
class CacheValue<ValueType> {
|
||||
public volatile int occurrence = 1;
|
||||
public final ValueType data;
|
||||
|
||||
public CacheValue(ValueType value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value
|
||||
*/
|
||||
public ValueType getValue() {
|
||||
return value;
|
||||
this.data = value;
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
package ca.recrown.islandsurvivalcraft.world;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.bukkit.World;
|
||||
|
||||
@ -13,19 +14,19 @@ import ca.recrown.islandsurvivalcraft.world.generation.IslandWorldGenerator;
|
||||
* Uses IslandWorldGenerator as the container for each world.
|
||||
*/
|
||||
public class IslandWorldGeneratorAlternator {
|
||||
private HashMap<Long, IslandWorldGenerator> chunkGenerator;
|
||||
private IslandBiomeGenerator islandBiomeGenerator;
|
||||
private final ConcurrentHashMap<UUID, IslandWorldGenerator> chunkGenerators;
|
||||
private final IslandBiomeGenerator islandBiomeGenerator;
|
||||
|
||||
public IslandWorldGeneratorAlternator(IslandBiomeGenerator biomeGenerator) {
|
||||
chunkGenerator = new HashMap<>();
|
||||
this.chunkGenerators = new ConcurrentHashMap<>();
|
||||
this.islandBiomeGenerator = biomeGenerator;
|
||||
}
|
||||
|
||||
public synchronized IslandWorldGenerator getIslandChunkGeneratorSystem(World world, Random random) {
|
||||
long tid = Thread.currentThread().getId();
|
||||
if (!chunkGenerator.containsKey(tid)) {
|
||||
chunkGenerator.put(tid, new IslandWorldGenerator(world, islandBiomeGenerator.getInstance(), random));
|
||||
public IslandWorldGenerator getIslandChunkGeneratorSystem(World world, Random random) {
|
||||
UUID wUuid = world.getUID();
|
||||
if (!chunkGenerators.containsKey(wUuid)) {
|
||||
chunkGenerators.put(wUuid, new IslandWorldGenerator(world, islandBiomeGenerator.getInstance(), random));
|
||||
}
|
||||
return chunkGenerator.get(tid);
|
||||
return chunkGenerators.get(wUuid);
|
||||
}
|
||||
}
|
@ -10,9 +10,9 @@ import ca.recrown.islandsurvivalcraft.pathfinding.CoordinateValidatable;
|
||||
import ca.recrown.islandsurvivalcraft.pathfinding.DepthFirstSearch;
|
||||
|
||||
public class IslandWorldMapper implements CoordinateValidatable {
|
||||
private Cache<Point2, Double> blockValueCache;
|
||||
private final Cache<Point2, Double> blockValueCache;
|
||||
|
||||
private SimplexOctaveGenerator noiseGenerator;
|
||||
private final SimplexOctaveGenerator noiseGenerator;
|
||||
private final int noiseOctaves = 4;
|
||||
private final float islandBlockGenerationPercent = 16;
|
||||
private final float exaggerationFactor = 1.2f;
|
||||
@ -28,7 +28,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
dfs = new DepthFirstSearch(this);
|
||||
this.noiseGenerator = new SimplexOctaveGenerator(random, noiseOctaves);
|
||||
noiseGenerator.setScale(scale);
|
||||
blockValueCache = new Cache<>(4096);
|
||||
blockValueCache = new Cache<>(32768);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,6 +113,7 @@ public class IslandWorldMapper implements CoordinateValidatable {
|
||||
*/
|
||||
public double getWorldBlockValue(int worldX, int worldZ) {
|
||||
Point2 p = new Point2(worldX, worldZ);
|
||||
|
||||
Double res = blockValueCache.getValue(p);
|
||||
if (res == null) {
|
||||
double portionSea = 1f - (this.islandBlockGenerationPercent / 100f);
|
||||
|
@ -5,7 +5,7 @@ import java.util.Random;
|
||||
import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
|
||||
public class BedrockGenerator {
|
||||
private SimplexOctaveGenerator noiseGenerator;
|
||||
private final SimplexOctaveGenerator noiseGenerator;
|
||||
private final int maxBedrockHeight;
|
||||
private final int minBedrockHeight;
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
|
||||
@ -16,32 +14,31 @@ 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 IslandBiomeGenerator {
|
||||
private boolean initialized;
|
||||
private volatile boolean initialized;
|
||||
private final TemperatureMapGenerator temperatureMapGenerator;
|
||||
private final Cache<Point2, Biome[][]> chunkBiomesCache;
|
||||
private final Cache<Point2, Boolean> chunkGenStatusCache;
|
||||
private IslandWorldMapper worldIslandMap;
|
||||
private BiomeSelector biomeSelector;
|
||||
private World world;
|
||||
private volatile IslandWorldMapper worldIslandMap;
|
||||
private volatile BiomeSelector biomeSelector;
|
||||
private volatile World world;
|
||||
private final DepthFirstSearch freshCachePropagator;
|
||||
private final DepthFirstSearch existenceChecker;
|
||||
private FreshCachePropagationInfo freshCachePropInfo;
|
||||
private PreviousGenerationInfo existenceInfo;
|
||||
private Point2 currChunkCoords;
|
||||
private final Biome[][] localChunkCache;
|
||||
private final FreshCachePropagationInfo freshCachePropInfo;
|
||||
private final PreviousGenerationInfo existenceInfo;
|
||||
private volatile Point2 currChunkCoords;
|
||||
private volatile Biome[][] localChunkCache;
|
||||
|
||||
float temperature;
|
||||
|
||||
public BiomePerIslandGenerator() {
|
||||
this.temperatureMapGenerator = new TemperatureMapGenerator();
|
||||
chunkBiomesCache = new Cache<>(1024);
|
||||
chunkGenStatusCache = new Cache<>(1024);
|
||||
chunkBiomesCache = new Cache<>(512);
|
||||
chunkGenStatusCache = new Cache<>(512);
|
||||
freshCachePropInfo = new FreshCachePropagationInfo();
|
||||
freshCachePropagator = new DepthFirstSearch(freshCachePropInfo);
|
||||
existenceInfo = new PreviousGenerationInfo();
|
||||
existenceChecker = new DepthFirstSearch(existenceInfo);
|
||||
localChunkCache = new Biome[16][16];
|
||||
currChunkCoords = new Point2();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -61,12 +58,9 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
|
||||
@Override
|
||||
public Biome GenerateBiome(int chunkX, int chunkZ, int localX, int localZ) {
|
||||
if (chunkX != currChunkCoords.x || chunkZ != currChunkCoords.y) {
|
||||
currChunkCoords.x = chunkX;
|
||||
currChunkCoords.y = chunkZ;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
Arrays.fill(localChunkCache[i], null);
|
||||
}
|
||||
if (currChunkCoords == null || chunkX != currChunkCoords.x || chunkZ != currChunkCoords.y) {
|
||||
currChunkCoords = new Point2(chunkX, chunkZ);
|
||||
localChunkCache = new Biome[16][16];
|
||||
}
|
||||
int worldX = Utilities.addMagnitude(16 * chunkX, localX);
|
||||
int worldZ = Utilities.addMagnitude(16 * chunkZ, localZ);
|
||||
@ -96,7 +90,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
int localZ = Math.abs(worldZ % 16);
|
||||
Point2 chunkCoords = new Point2(worldX / 16, worldZ / 16);
|
||||
|
||||
if (chunkCoords.equals(this.currChunkCoords) && localChunkCache[localX][localZ] != null) {
|
||||
if (chunkCoords.fastEquals(this.currChunkCoords) && localChunkCache[localX][localZ] != null) {
|
||||
return localChunkCache[localX][localZ];
|
||||
}
|
||||
|
||||
@ -127,7 +121,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
int localZ = Math.abs(worldZ % 16);
|
||||
Point2 chunkCoords = new Point2(worldX / 16, worldZ / 16);
|
||||
|
||||
if (chunkCoords.equals(this.currChunkCoords)) localChunkCache[localX][localZ] = biome;
|
||||
if (chunkCoords.fastEquals(this.currChunkCoords)) localChunkCache[localX][localZ] = biome;
|
||||
|
||||
Biome[][] chunkBiomes = chunkBiomesCache.getValue(chunkCoords);
|
||||
if (chunkBiomes == null) chunkBiomes = new Biome[16][16];
|
||||
@ -157,7 +151,7 @@ public class BiomePerIslandGenerator implements IslandBiomeGenerator {
|
||||
@Override
|
||||
public boolean validate(int x, int y) {
|
||||
Point2 chunkCoords = new Point2(x / 16, y / 16);
|
||||
return chunkCoords.x == currChunkCoords.x && chunkCoords.y == currChunkCoords.y && worldIslandMap.isIsland(x, y);
|
||||
return chunkCoords.fastEquals(currChunkCoords) && worldIslandMap.isIsland(x, y);
|
||||
}
|
||||
|
||||
public boolean allBiomesAcquired() {
|
||||
|
@ -9,6 +9,7 @@ import org.bukkit.block.Biome;
|
||||
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
|
||||
import org.bukkit.generator.ChunkGenerator.ChunkData;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
import ca.recrown.islandsurvivalcraft.world.shaders.WorldHeightShader;
|
||||
@ -44,8 +45,9 @@ public class IslandWorldGenerator {
|
||||
}
|
||||
|
||||
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;
|
||||
int worldX = Utilities.addMagnitude(16 * chunkX, localX);
|
||||
int worldZ = Utilities.addMagnitude(16 * chunkZ, localZ);
|
||||
|
||||
// gets the bedrock.
|
||||
int bedrockHeight = bedrockGenerator.getBedrockHeight(worldX, worldZ);
|
||||
|
||||
|
@ -12,7 +12,7 @@ class TemperatureMapGenerator {
|
||||
private final double frequency = 0.5D;
|
||||
private final double amplitude = 0.5D;
|
||||
|
||||
private SimplexOctaveGenerator noiseGenerator;
|
||||
private volatile SimplexOctaveGenerator noiseGenerator;
|
||||
|
||||
public TemperatureMapGenerator() {
|
||||
temperatureCache = new Cache<>(1024);
|
||||
|
@ -9,12 +9,12 @@ import org.bukkit.util.noise.SimplexOctaveGenerator;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
|
||||
public class WorldHeightShader {
|
||||
private Random random;
|
||||
private SimplexOctaveGenerator noiseGenerator;
|
||||
private IslandWorldMapper islandLocator;
|
||||
private int seaLevel;
|
||||
private int worldHeight;
|
||||
private int minimumHeight;
|
||||
private final Random random;
|
||||
private final SimplexOctaveGenerator noiseGenerator;
|
||||
private final IslandWorldMapper islandLocator;
|
||||
private final int seaLevel;
|
||||
private final int worldHeight;
|
||||
private final int minimumHeight;
|
||||
private final float probabilityOfAdditive = 0.75f;
|
||||
|
||||
public WorldHeightShader(long seed, IslandWorldMapper islandLocator, int seaLevel, int worldHeight, int minimumHeight) {
|
||||
|
Loading…
Reference in New Issue
Block a user