Attempted at making all things world gen thread proof.

Specifically, caching is tested thread proof.
This commit is contained in:
Harrison Deng 2020-04-26 01:28:45 -05:00
parent f20515fd45
commit 98e4265db7
10 changed files with 85 additions and 89 deletions

View File

@ -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;
}
}

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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() {

View File

@ -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);

View File

@ -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);

View File

@ -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) {