Use Faster XoRoShiRo128PlusRandom

XoRoShiRo128PlusRandom is a Faster Random Class that is many times faster than standard Java Random. It also features better random distribution and the ability to be split and retain the exact same noise output like Splitable random.

http://dsiutils.di.unimi.it/docs/it/unimi/dsi/util/XoRoShiRo128PlusRandom.html
This commit is contained in:
Bud Gidiere
2020-11-19 17:23:08 -06:00
parent 700d1d0a6c
commit 094b421f97
15 changed files with 35 additions and 18 deletions

View File

@@ -55,6 +55,8 @@ dependencies {
compileOnly("org.spigotmc:spigot-api:1.16.2-R0.1-SNAPSHOT")
implementation("io.papermc:paperlib:1.0.5")
implementation("it.unimi.dsi:dsiutils:2.6.14")
// JUnit.
testImplementation("org.junit.jupiter:junit-jupiter-api:5.7.0")
@@ -105,6 +107,7 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("org.bstats.bukkit", "com.dfsek.terra.lib.bstats")
relocate("parsii", "com.dfsek.terra.lib.parsii")
relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib")
relocate("it.unimi.dsi", "com.dfsek.terra.lib.unimi")
}
tasks.build {

View File

@@ -23,6 +23,7 @@ import org.bukkit.event.world.StructureGrowEvent;
import org.polydev.gaea.GaeaPlugin;
import org.polydev.gaea.tree.Tree;
import org.polydev.gaea.tree.TreeType;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import java.util.Random;
@@ -82,9 +83,9 @@ public class EventListener implements Listener {
Tree tree = registry.get(TreeType.fromBukkit(e.getSpecies()).toString());
Debug.info("Overriding tree type: " + e.getSpecies());
if(tree instanceof TreeConfig) {
if(!((TreeConfig) tree).plantBlockCheck(e.getLocation(), new Random())) {
if(!((TreeConfig) tree).plantBlockCheck(e.getLocation(), new XoRoShiRo128PlusRandom())) {
block.setBlockData(data);
}
} else if(!tree.plant(e.getLocation(), new Random(), Terra.getInstance())) block.setBlockData(data);
} else if(!tree.plant(e.getLocation(), new XoRoShiRo128PlusRandom(), Terra.getInstance())) block.setBlockData(data);
}
}

View File

@@ -6,6 +6,7 @@ import com.dfsek.terra.biome.grid.TerraBiomeGrid;
import com.dfsek.terra.config.genconfig.structure.StructureConfig;
import com.dfsek.terra.structure.Rotation;
import com.dfsek.terra.structure.Structure;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.Vector;
@@ -35,7 +36,7 @@ public class AsyncStructureFinder extends AsyncFeatureFinder<StructureConfig> {
Location spawn = target.getSpawn().getChunkSpawn(x, z, world.getSeed()).toLocation(world);
if(!TerraWorld.getWorld(world).getConfig().getBiome((UserDefinedBiome) getGrid().getBiome(spawn)).getStructures().contains(target))
return false;
Random r2 = new Random(spawn.hashCode());
Random r2 = new XoRoShiRo128PlusRandom(spawn.hashCode());
Structure struc = target.getStructure(r2);
Rotation rotation = Rotation.fromDegrees(r2.nextInt(4) * 90);
for(int y = target.getSearchStart().get(r2); y > 0; y--) {

View File

@@ -4,6 +4,7 @@ import com.dfsek.terra.procgen.GridSpawn;
import com.dfsek.terra.procgen.voxel.DeformedSphere;
import com.dfsek.terra.procgen.voxel.Tube;
import com.dfsek.terra.procgen.voxel.VoxelGeometry;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.util.Vector;
@@ -23,7 +24,7 @@ public class Cavern {
public VoxelGeometry carveChunk(int chunkX, int chunkZ) {
long seedC = MathUtil.getCarverChunkSeed(chunkX, chunkZ, seed);
Random chunk = new Random(seedC);
Random chunk = new XoRoShiRo128PlusRandom(seedC);
Vector org = node.getNodeLocation((chunkX << 4) + 8, (chunkZ << 4) + 8).clone().setY(chunk.nextInt(128));
VoxelGeometry carve = VoxelGeometry.getBlank();
@@ -33,7 +34,7 @@ public class Cavern {
Bukkit.getLogger().info("Cavern: " + org.toString());
carve.merge(new DeformedSphere(org.clone(), chunk.nextInt(4) + 3, 0.75, smpl));
Vector _00 = new Vector(org.getX() + 16, new Random(MathUtil.getCarverChunkSeed(chunkX + 1, chunkZ, seed)).nextInt(128), org.getZ());
Vector _00 = new Vector(org.getX() + 16, new XoRoShiRo128PlusRandom(MathUtil.getCarverChunkSeed(chunkX + 1, chunkZ, seed)).nextInt(128), org.getZ());
carve.merge(new Tube(org, _00, 4));
return carve;

View File

@@ -3,6 +3,7 @@ package com.dfsek.terra.carving;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.config.base.ConfigPack;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.World;
import org.bukkit.util.Vector;
@@ -40,7 +41,7 @@ public class UserDefinedCarver extends Carver {
@Override
public Worm getWorm(long l, Vector vector) {
Random r = new Random(l + hash);
Random r = new XoRoShiRo128PlusRandom(l + hash);
return new UserDefinedWorm(length.get(r) / 2, r, vector, radius.getMax(), topCut, bottomCut);
}
@@ -59,7 +60,7 @@ public class UserDefinedCarver extends Carver {
@Override
public boolean isChunkCarved(World w, int chunkX, int chunkZ, Random random) {
ConfigPack c = TerraWorld.getWorld(w).getConfig();
return new Random(random.nextLong() + hash).nextInt(100) < c.getBiome((UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(chunkX << 4, chunkZ << 4, GenerationPhase.POPULATE)).getCarverChance(this);
return new XoRoShiRo128PlusRandom(random.nextLong() + hash).nextInt(100) < c.getBiome((UserDefinedBiome) TerraWorld.getWorld(w).getGrid().getBiome(chunkX << 4, chunkZ << 4, GenerationPhase.POPULATE)).getCarverChance(this);
}
private class UserDefinedWorm extends Worm {

View File

@@ -3,6 +3,7 @@ package com.dfsek.terra.command;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.config.genconfig.OreConfig;
import com.dfsek.terra.config.lang.LangUtil;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.World;
import org.bukkit.block.Block;
@@ -36,7 +37,7 @@ public class OreCommand extends WorldCommand {
return true;
}
Vector source = new Vector(FastMath.floorMod(bl.getX(), 16), bl.getY(), FastMath.floorMod(bl.getZ(), 16));
ore.doVein(source, bl.getChunk(), new Random());
ore.doVein(source, bl.getChunk(), new XoRoShiRo128PlusRandom());
} else {
LangUtil.send("command.ore.main-menu", sender);
}

View File

@@ -4,6 +4,7 @@ import com.dfsek.terra.config.TerraConfig;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.base.ConfigUtil;
import com.dfsek.terra.config.exception.ConfigException;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.Chunk;
import org.bukkit.Location;
@@ -53,7 +54,7 @@ public class FloraConfig extends TerraConfig implements Flora {
physics = getBoolean("physics", false);
ceiling = getBoolean("ceiling", false);
Palette<BlockData> p = new RandomPalette<>(new Random(getInt("seed", 4)));
Palette<BlockData> p = new RandomPalette<>(new XoRoShiRo128PlusRandom(getInt("seed", 4)));
floraPalette = PaletteConfig.getPalette(getMapList("layers"), p);
}

View File

@@ -4,6 +4,7 @@ import com.dfsek.terra.Debug;
import com.dfsek.terra.config.TerraConfig;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.exception.ConfigException;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Bukkit;
import org.bukkit.block.data.BlockData;
import org.bukkit.configuration.InvalidConfigurationException;
@@ -36,7 +37,7 @@ public class PaletteConfig extends TerraConfig {
pNoise.setFractalOctaves(4);
pNoise.setFrequency(getDouble("frequency", 0.02));
pal = new SimplexPalette<>(pNoise);
} else pal = new RandomPalette<>(new Random(getInt("seed", 2403)));
} else pal = new RandomPalette<>(new XoRoShiRo128PlusRandom(getInt("seed", 2403)));
palette = getPalette(getMapList("layers"), pal);
}

View File

@@ -4,6 +4,7 @@ import com.dfsek.terra.config.TerraConfig;
import com.dfsek.terra.config.TerraConfigSection;
import com.dfsek.terra.config.exception.ConfigException;
import com.dfsek.terra.config.exception.NotFoundException;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
@@ -16,7 +17,7 @@ import org.polydev.gaea.world.palette.RandomPalette;
import java.util.Random;
public class BiomeOceanConfig extends TerraConfigSection {
private static final Palette<BlockData> oceanDefault = new RandomPalette<BlockData>(new Random(0)).add(Material.WATER.createBlockData(), 1);
private static final Palette<BlockData> oceanDefault = new RandomPalette<BlockData>(new XoRoShiRo128PlusRandom(0)).add(Material.WATER.createBlockData(), 1);
private final Palette<BlockData> ocean;
private final int seaLevel;
@@ -27,7 +28,7 @@ public class BiomeOceanConfig extends TerraConfigSection {
if(oceanN != null) {
if(oceanN.startsWith("BLOCK:")) {
try {
ocean = new RandomPalette<BlockData>(new Random(0)).add(new ProbabilityCollection<BlockData>().add(Bukkit.createBlockData(oceanN.substring(6)), 1), 1);
ocean = new RandomPalette<BlockData>(new XoRoShiRo128PlusRandom(0)).add(new ProbabilityCollection<BlockData>().add(Bukkit.createBlockData(oceanN.substring(6)), 1), 1);
} catch(IllegalArgumentException ex) {
throw new ConfigException("BlockData \"" + oceanN + "\" is invalid! (Ocean Palette)", parent.getID());
}

View File

@@ -4,6 +4,7 @@ import com.dfsek.terra.config.TerraConfig;
import com.dfsek.terra.config.TerraConfigSection;
import com.dfsek.terra.config.exception.ConfigException;
import com.dfsek.terra.config.exception.NotFoundException;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Bukkit;
import org.bukkit.block.data.BlockData;
import org.bukkit.configuration.InvalidConfigurationException;
@@ -29,7 +30,7 @@ public class BiomePaletteConfig extends TerraConfigSection {
try {
if(((String) entry.getKey()).startsWith("BLOCK:")) {
try {
paletteMap.put((Integer) entry.getValue(), new RandomPalette<BlockData>(new Random(0)).add(new ProbabilityCollection<BlockData>().add(Bukkit.createBlockData(((String) entry.getKey()).substring(6)), 1), 1));
paletteMap.put((Integer) entry.getValue(), new RandomPalette<BlockData>(new XoRoShiRo128PlusRandom(0)).add(new ProbabilityCollection<BlockData>().add(Bukkit.createBlockData(((String) entry.getKey()).substring(6)), 1), 1));
} catch(IllegalArgumentException ex) {
throw new ConfigException("BlockData " + entry.getKey() + " is invalid! (Palettes)", parent.getID());
}

View File

@@ -5,6 +5,7 @@ import com.dfsek.terra.config.TerraConfig;
import com.dfsek.terra.config.TerraConfigSection;
import com.dfsek.terra.config.exception.ConfigException;
import com.dfsek.terra.config.exception.NotFoundException;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
@@ -42,7 +43,7 @@ public class BiomeSlabConfig extends TerraConfigSection {
if(((String) entry.getValue()).startsWith("BLOCK:")) {
try {
Debug.info("Adding slab palette with single material " + entry.getKey());
paletteMap.put(Bukkit.createBlockData((String) entry.getKey()).getMaterial(), new RandomPalette<BlockData>(new Random(0)).add(new ProbabilityCollection<BlockData>().add(Bukkit.createBlockData(((String) entry.getValue()).substring(6)), 1), 1));
paletteMap.put(Bukkit.createBlockData((String) entry.getKey()).getMaterial(), new RandomPalette<BlockData>(new XoRoShiRo128PlusRandom(0)).add(new ProbabilityCollection<BlockData>().add(Bukkit.createBlockData(((String) entry.getValue()).substring(6)), 1), 1));
} catch(IllegalArgumentException ex) {
throw new ConfigException("Invalid BlockData in slab configuration: " + ex.getMessage(), getParent().getConfig().getID());
}

View File

@@ -13,6 +13,7 @@ import com.dfsek.terra.structure.Structure;
import com.dfsek.terra.structure.StructureContainedInventory;
import com.dfsek.terra.structure.features.Feature;
import com.dfsek.terra.util.structure.RotationUtil;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.Chunk;
import org.bukkit.Location;
@@ -41,7 +42,7 @@ public class StructurePopulator extends BlockPopulator {
for(StructureConfig conf : config.getAllStructures()) {
Location spawn = conf.getSpawn().getNearestSpawn(cx + 8, cz + 8, world.getSeed()).toLocation(world);
if(!config.getBiome((UserDefinedBiome) grid.getBiome(spawn)).getStructures().contains(conf)) continue;
Random r2 = new Random(spawn.hashCode());
Random r2 = new XoRoShiRo128PlusRandom(spawn.hashCode());
Structure struc = conf.getStructure(r2);
Rotation rotation = Rotation.fromDegrees(r2.nextInt(4) * 90);
for(int y = conf.getSearchStart().get(r2); y > 0; y--) {

View File

@@ -1,5 +1,6 @@
package com.dfsek.terra.procgen;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.util.Vector;
import org.polydev.gaea.math.MathUtil;
@@ -53,7 +54,7 @@ public class GridSpawn {
* @return Vector representing spawnpoint
*/
public Vector getChunkSpawn(int structureChunkX, int structureChunkZ, long seed) {
Random r = new Random(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed));
Random r = new XoRoShiRo128PlusRandom(MathUtil.getCarverChunkSeed(structureChunkX, structureChunkZ, seed));
int offsetX = r.nextInt(width);
int offsetZ = r.nextInt(width);
int sx = structureChunkX * (width + 2 * separation) + offsetX;

View File

@@ -4,6 +4,7 @@ import com.dfsek.terra.Debug;
import com.dfsek.terra.structure.Rotation;
import com.dfsek.terra.structure.Structure;
import com.dfsek.terra.structure.StructureInfo;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.apache.commons.math3.util.FastMath;
import org.bukkit.Chunk;
import org.bukkit.Location;
@@ -55,7 +56,7 @@ public class EntityFeature implements Feature {
@Override
public void apply(Structure structure, Rotation r, Location l, Chunk chunk) {
Random random = new Random(MathUtil.getCarverChunkSeed(chunk.getX(), chunk.getZ(), chunk.getWorld().getSeed()));
Random random = new XoRoShiRo128PlusRandom(MathUtil.getCarverChunkSeed(chunk.getX(), chunk.getZ(), chunk.getWorld().getSeed()));
for(Location attempt : getLocations(structure, r, l, random, amount.get(random))) {
if(!isInChunk(chunk, attempt)) continue; // Don't attempt spawn if not in current chunk.
attemptSpawn(attempt, l);

View File

@@ -1,5 +1,6 @@
package com.dfsek.terra.util;
import it.unimi.dsi.util.XoRoShiRo128PlusRandom;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.polydev.gaea.world.palette.Palette;
@@ -12,5 +13,5 @@ public final class DataUtil {
public static final BlockData SNOW = Material.SNOW.createBlockData();
public static final BlockData WATER = Material.WATER.createBlockData();
public static final BlockData AIR = Material.AIR.createBlockData();
public static final Palette<BlockData> BLANK_PALETTE = new RandomPalette<BlockData>(new Random(2403)).add(AIR, 1);
public static final Palette<BlockData> BLANK_PALETTE = new RandomPalette<BlockData>(new XoRoShiRo128PlusRandom(2403)).add(AIR, 1);
}