Attempted at making all things world gen thread proof.
Specifically, caching is tested thread proof.
This commit is contained in:
		@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user