This commit is contained in:
Daniel Mills
2020-01-20 19:46:20 -05:00
parent 2aef5f94c0
commit 2702c0f2e4
21 changed files with 525 additions and 40 deletions

View File

@@ -11,6 +11,7 @@ import org.bukkit.util.NumberConversions;
import mortar.util.text.C;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.controller.PackController;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.generator.genobject.GenObjectDecorator;
import ninja.bytecode.iris.generator.genobject.PlacedObject;
import ninja.bytecode.iris.generator.layer.GenLayerBiome;
@@ -20,15 +21,14 @@ import ninja.bytecode.iris.generator.layer.GenLayerCaves;
import ninja.bytecode.iris.generator.layer.GenLayerCliffs;
import ninja.bytecode.iris.generator.layer.GenLayerLayeredNoise;
import ninja.bytecode.iris.generator.layer.GenLayerSnow;
import ninja.bytecode.iris.generator.parallax.ParallaxWorldGenerator;
import ninja.bytecode.iris.pack.BiomeType;
import ninja.bytecode.iris.pack.CompiledDimension;
import ninja.bytecode.iris.pack.IrisBiome;
import ninja.bytecode.iris.pack.IrisRegion;
import ninja.bytecode.iris.util.AtomicChunkData;
import ninja.bytecode.iris.util.ChunkPlan;
import ninja.bytecode.iris.util.IrisInterpolation;
import ninja.bytecode.iris.util.MB;
import ninja.bytecode.iris.util.ParallaxWorldGenerator;
import ninja.bytecode.iris.util.SChunkVector;
import ninja.bytecode.shuriken.collections.GList;
import ninja.bytecode.shuriken.logging.L;

View File

@@ -0,0 +1,277 @@
package ninja.bytecode.iris.generator;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.entity.Player;
import mortar.api.nms.NMP;
import mortar.api.sched.J;
import mortar.compute.math.M;
import mortar.lang.collection.FinalDouble;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.util.ChronoQueue;
import ninja.bytecode.iris.util.ChunkPlan;
import ninja.bytecode.iris.util.SMCAVector;
import ninja.bytecode.shuriken.bench.PrecisionStopwatch;
import ninja.bytecode.shuriken.collections.GList;
import ninja.bytecode.shuriken.collections.GMap;
import ninja.bytecode.shuriken.execution.ChronoLatch;
import ninja.bytecode.shuriken.execution.NastyRunnable;
import ninja.bytecode.shuriken.execution.TaskExecutor;
import ninja.bytecode.shuriken.math.RNG;
public class WorldReactor
{
private final World world;
private IrisGenerator gen;
public WorldReactor(World world)
{
this.world = world;
if(!(world.getGenerator() instanceof IrisGenerator))
{
throw new IllegalArgumentException(world.getName() + " is not an iris world.");
}
this.gen = ((IrisGenerator) world.getGenerator());
}
public void generateMultipleRegions(GMap<SMCAVector, TaskExecutor> g, boolean force, double mst, Consumer<Double> progress, Runnable done)
{
ChronoLatch cl = new ChronoLatch(50);
GMap<SMCAVector, Double> p = new GMap<>();
ReentrantLock r = new ReentrantLock();
for(SMCAVector i : g.k())
{
generateRegion(i.getX(), i.getZ(), g.get(i), force, mst / (double) g.size(), (xp) ->
{
r.lock();
p.put(i, xp);
double m = 0;
for(double j : p.v())
{
m += j;
}
r.unlock();
double h = m / (double) p.size();
if(h == 1D || cl.flip())
{
progress.accept(h);
}
}, () ->
{
p.remove(i);
if(p.isEmpty())
{
done.run();
}
});
}
}
public void generateRegion(int mx, int mz, TaskExecutor executor, boolean force, double mst, Consumer<Double> progress, Runnable done)
{
J.a(() ->
{
FinalDouble of = new FinalDouble(0D);
FinalDouble max = new FinalDouble(0D);
PrecisionStopwatch t = PrecisionStopwatch.start();
GMap<SMCAVector, AtomicChunkData> data = new GMap<>();
GMap<SMCAVector, ChunkPlan> plan = new GMap<>();
GList<NastyRunnable> parallax = new GList<>();
GList<NastyRunnable> noise = new GList<>();
GList<Runnable> chunk = new GList<>();
ReentrantLock lock = new ReentrantLock();
for(int xx = mx << 5; xx < (mx << 5) + 32; xx++)
{
int x = xx;
for(int zz = mz << 5; zz < (mz << 5) + 32; zz++)
{
int z = zz;
SMCAVector w = new SMCAVector(x, z);
if(!force && world.loadChunk(x, z, false))
{
// continue;
}
max.add(1);
parallax.add(() ->
{
gen.doGenParallax(x, z);
of.add(1);
progress.accept(of.get() / max.get());
});
max.add(0.1);
noise.add(() ->
{
AtomicChunkData tydata = new AtomicChunkData(world);
ChunkPlan typlan = new ChunkPlan();
for(int i = 0; i < 16; i++)
{
for(int j = 0; j < 16; j++)
{
gen.onGenColumn((x << 4) + i, (z << 4) + j, i, j, typlan, tydata);
}
}
gen.getWorldData().getChunk(x, z).inject(tydata);
gen.onPostChunk(world, x, z, new RNG(world.getSeed()), tydata, typlan);
lock.lock();
data.put(w, tydata);
plan.put(w, typlan);
lock.unlock();
of.add(1);
progress.accept(of.get() / max.get());
});
max.add(4);
chunk.add(() ->
{
if(world.loadChunk(x, z, false))
{
world.regenerateChunk(x, z);
}
else
{
world.loadChunk(x, z, true);
}
Chunk cc = world.getChunkAt(x, z);
NMP.host.relight(cc);
cc.unload(true);
});
}
}
executor.startWork().queue(parallax).execute();
executor.startWork().queue(noise).execute();
gen.setSplicer((world, random, x, z, biome) ->
{
SMCAVector w = new SMCAVector(x, z);
for(int i = 0; i < 16; i++)
{
for(int j = 0; j < 16; j++)
{
try
{
biome.setBiome((x << 4) + i, (z << 4) + j, plan.get(w).getBiome(i, j).getRealBiome());
}
catch(Throwable e)
{
}
}
}
AtomicChunkData f = data.get(w);
if(f != null)
{
}
return f;
});
t.end();
J.s(() ->
{
PrecisionStopwatch s = PrecisionStopwatch.start();
ChronoQueue q = new ChronoQueue(mst, 1096);
int m = 0;
for(Runnable i : chunk)
{
int gg = m;
q.queue(() ->
{
i.run();
of.add(4);
if(gg == chunk.size() - 1)
{
progress.accept(1D);
s.end();
q.dieSlowly();
done.run();
}
else
{
progress.accept(M.clip(of.get() / max.get(), 0D, 1D));
}
});
m++;
}
});
});
}
public void generateRegionNormal(Player p, boolean force, double mst, Consumer<Double> progress, Runnable done)
{
ChronoQueue q = new ChronoQueue(mst, 1024);
FinalDouble of = new FinalDouble(0D);
FinalDouble max = new FinalDouble(0D);
for(int xx = p.getLocation().getChunk().getX() - 32; xx < p.getLocation().getChunk().getX() + 32; xx++)
{
int x = xx;
for(int zz = p.getLocation().getChunk().getX() - 32; zz < p.getLocation().getChunk().getX() + 32; zz++)
{
int z = zz;
max.add(1);
q.queue(() ->
{
if(world.loadChunk(x, z, false))
{
world.regenerateChunk(x, z);
}
else
{
world.loadChunk(x, z, true);
}
Chunk cc = world.getChunkAt(x, z);
NMP.host.relight(cc);
of.add(1);
if(of.get() == max.get())
{
progress.accept(1D);
q.dieSlowly();
done.run();
}
else
{
progress.accept(M.clip(of.get() / max.get(), 0D, 1D));
}
});
}
}
q.dieSlowly();
}
}

View File

@@ -0,0 +1,83 @@
package ninja.bytecode.iris.generator.atomics;
import java.io.Serializable;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
@SuppressWarnings("restriction")
public class AtomicCharArray implements Serializable
{
private static final long serialVersionUID = 2862133569453604235L;
private static final Unsafe unsafe;
private static final int base;
private static final int shift;
volatile char[] array;
public AtomicCharArray(int var1)
{
this.array = new char[var1];
}
private long checkedByteOffset(int var1)
{
if(var1 >= 0 && var1 < this.array.length)
{
return byteOffset(var1);
}
else
{
throw new IndexOutOfBoundsException("index " + var1);
}
}
public final char get(int var1)
{
return this.getRaw(this.checkedByteOffset(var1));
}
private char getRaw(long var1)
{
return unsafe.getCharVolatile(this.array, var1);
}
public final void set(int var1, char var2)
{
unsafe.putCharVolatile(this.array, this.checkedByteOffset(var1), var2);
}
private static long byteOffset(int var0)
{
return ((long) var0 << shift) + (long) base;
}
static
{
Field f;
Unsafe o = null;
try
{
f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
o = (Unsafe) f.get(null);
}
catch(Throwable e)
{
e.printStackTrace();
}
unsafe = o;
base = unsafe.arrayBaseOffset(int[].class);
int var0 = unsafe.arrayIndexScale(int[].class);
if((var0 & var0 - 1) != 0)
{
throw new Error("data type scale not a power of two");
}
else
{
shift = 31 - Integer.numberOfLeadingZeros(var0);
}
}
}

View File

@@ -0,0 +1,451 @@
package ninja.bytecode.iris.generator.atomics;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.concurrent.locks.ReentrantLock;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_12_R1.generator.CraftChunkData;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.ChunkGenerator.ChunkData;
import org.bukkit.material.MaterialData;
import mortar.compute.math.M;
import ninja.bytecode.iris.util.MB;
public final class AtomicChunkData implements ChunkGenerator.ChunkData
{
private static final Field t;
private static final Field[] sections;
private static final int h = 0x1000;
private final int maxHeight;
private static ReentrantLock[] locks;
private char[] s0;
private char[] s1;
private char[] s2;
private char[] s3;
private char[] s4;
private char[] s5;
private char[] s6;
private char[] s7;
private char[] s8;
private char[] s9;
private char[] s10;
private char[] s11;
private char[] s12;
private char[] s13;
private char[] s14;
private char[] s15;
private char[][] m;
private World w;
private long lastUse;
private int bits;
public AtomicChunkData(World world)
{
this.maxHeight = world.getMaxHeight();
this.w = world;
bits = 0;
lastUse = M.ms();
}
public long getTimeSinceLastUse()
{
return M.ms() - lastUse;
}
public void read(InputStream in) throws IOException
{
read(in, true);
}
public void read(InputStream in, boolean ignoreAir) throws IOException
{
DataInputStream din = new DataInputStream(in);
int bits = din.readInt();
for(int i = 0; i < 16; i++)
{
int bit = getBit(i);
if((bits & bit) == bit)
{
char[] section = getChunkSection(i << 4, true);
for(int j = 0; j < section.length; j++)
{
char c = din.readChar();
if(c == 0 && ignoreAir)
{
continue;
}
section[j] = c;
}
}
}
din.close();
}
public void write(OutputStream out) throws IOException
{
DataOutputStream dos = new DataOutputStream(out);
dos.writeInt(getDataBits());
for(int i = 0; i < 16; i++)
{
if(hasDataBit(i))
{
char[] section = getChunkSection(i << 4, false);
for(int j = 0; j < section.length; j++)
{
dos.writeChar(section[j]);
}
}
}
dos.close();
}
public boolean hasDataBit(int section)
{
int b = getBit(section);
return (bits & b) == b;
}
public void clearDataBits()
{
bits = 0;
}
public void addDataBit(int section)
{
bits |= getBit(section);
}
public void removeDataBit(int section)
{
bits ^= getBit(section);
}
public int getDataBits()
{
return bits;
}
public int getBit(int index)
{
return (int) (index < 0 ? -1 : Math.pow(2, index));
}
public int computeDataBits()
{
int bits = 0;
for(int i = 0; i < 16; i++)
{
try
{
bits |= sections[i].get(this) != null ? getBit(i) : 0;
}
catch(Throwable e)
{
}
}
return bits;
}
@Override
public int getMaxHeight()
{
return maxHeight;
}
@SuppressWarnings("deprecation")
@Override
public void setBlock(int x, int y, int z, Material material)
{
setBlock(x, y, z, material.getId());
}
@SuppressWarnings("deprecation")
@Override
public void setBlock(int x, int y, int z, MaterialData material)
{
setBlock(x, y, z, material.getItemTypeId(), material.getData());
}
@SuppressWarnings("deprecation")
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material)
{
setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.getId());
}
@SuppressWarnings("deprecation")
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material)
{
setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material.getItemTypeId(), material.getData());
}
@SuppressWarnings("deprecation")
@Override
public Material getType(int x, int y, int z)
{
lastUse = M.ms();
return Material.getMaterial(getTypeId(x, y, z));
}
@SuppressWarnings("deprecation")
@Override
public MaterialData getTypeAndData(int x, int y, int z)
{
lastUse = M.ms();
return getType(x, y, z).getNewData(getData(x, y, z));
}
@SuppressWarnings("deprecation")
public void setBlock(int x, int y, int z, Material blockId, byte data)
{
setBlock(x, y, z, blockId.getId(), data);
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int blockId)
{
lastUse = M.ms();
setRegion(xMin, yMin, zMin, xMax, yMax, zMax, blockId, (byte) 0);
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, int blockId, int data)
{
lastUse = M.ms();
throw new UnsupportedOperationException("AtomicChunkData does not support setting regions");
}
@Override
public void setBlock(int x, int y, int z, int blockId)
{
setBlock(x, y, z, blockId, (byte) 0);
}
@Override
public void setBlock(int x, int y, int z, int blockId, byte data)
{
setBlock(x, y, z, (char) (blockId << 4 | data));
}
@SuppressWarnings("deprecation")
public MB getMB(int x, int y, int z)
{
if(x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf))
{
lastUse = M.ms();
return MB.of(Material.AIR);
}
char[] section = getChunkSection(y, false);
if(section == null)
{
lastUse = M.ms();
return MB.of(Material.AIR);
}
else
{
lastUse = M.ms();
char xf = section[(y & 0xf) << 8 | z << 4 | x];
return MB.of(Material.getMaterial(xf >> 4), xf & 0xf);
}
}
@Override
public int getTypeId(int x, int y, int z)
{
if(x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf))
{
return 0;
}
char[] section = getChunkSection(y, false);
if(section == null)
{
return 0;
}
else
{
lastUse = M.ms();
return section[(y & 0xf) << 8 | z << 4 | x] >> 4;
}
}
@Override
public byte getData(int x, int y, int z)
{
if(x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf))
{
lastUse = M.ms();
return (byte) 0;
}
char[] section = getChunkSection(y, false);
if(section == null)
{
lastUse = M.ms();
return (byte) 0;
}
else
{
lastUse = M.ms();
return (byte) (section[(y & 0xf) << 8 | z << 4 | x] & 0xf);
}
}
private void setBlock(int x, int y, int z, char type)
{
if(x != (x & 0xf) || y < 0 || y >= maxHeight || z != (z & 0xf))
{
return;
}
lastUse = M.ms();
ReentrantLock l = locks[y >> 4];
l.lock();
getChunkSection(y, true)[(y & 0xf) << 8 | z << 4 | x] = type;
l.unlock();
}
private char[] getChunkSection(int y, boolean c)
{
try
{
int s = y >> 4;
Field sf = sections[s];
char[] section = (char[]) sf.get(this);
if(section == null && c)
{
sf.set(this, new char[h]);
section = (char[]) sf.get(this);
addDataBit(s);
}
return section;
}
catch(Throwable e)
{
e.printStackTrace();
}
return null;
}
public ChunkData toChunkData()
{
ChunkData c = new CraftChunkData(w);
try
{
m = (char[][]) t.get(c);
m[0] = s0;
m[1] = s1;
m[2] = s2;
m[3] = s3;
m[4] = s4;
m[5] = s5;
m[6] = s6;
m[7] = s7;
m[8] = s8;
m[9] = s9;
m[10] = s10;
m[11] = s11;
m[12] = s12;
m[13] = s13;
m[14] = s14;
m[15] = s15;
}
catch(IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
return c;
}
static
{
locks = new ReentrantLock[16];
Field[] s = new Field[16];
for(int i = 0; i < 16; i++)
{
try
{
s[i] = AtomicChunkData.class.getDeclaredField("s" + i);
locks[i] = new ReentrantLock();
}
catch(Throwable e)
{
e.printStackTrace();
}
}
sections = s;
Field x = null;
try
{
x = CraftChunkData.class.getDeclaredField("sections");
x.setAccessible(true);
}
catch(Throwable e)
{
e.printStackTrace();
}
t = x;
}
public void inject(AtomicChunkData data)
{
for(int i = 0; i < 16; i++)
{
if(hasDataBit(i))
{
char[] fromSection = getChunkSection(i << 4, false);
char[] toSection = data.getChunkSection(i << 4, true);
for(int j = 0; j < fromSection.length; j++)
{
char x = fromSection[j];
if(x != 0)
{
toSection[j] = x;
}
}
}
}
}
}

View File

@@ -0,0 +1,80 @@
package ninja.bytecode.iris.generator.atomics;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.bukkit.World;
import org.jnbt.ByteArrayTag;
import org.jnbt.CompoundTag;
import org.jnbt.NBTInputStream;
import org.jnbt.NBTOutputStream;
import org.jnbt.Tag;
import ninja.bytecode.shuriken.collections.GMap;
public class AtomicRegionData
{
private final World world;
private GMap<String, Tag> tag;
public AtomicRegionData(World world)
{
this.world = world;
tag = new GMap<>();
}
public void read(InputStream in) throws IOException
{
NBTInputStream nin = new NBTInputStream(in);
tag = new GMap<>();
tag.putAll(((CompoundTag) nin.readTag()).getValue());
nin.close();
}
public void write(OutputStream out) throws IOException
{
NBTOutputStream nos = new NBTOutputStream(out);
nos.writeTag(new CompoundTag("imca", tag));
nos.close();
}
public boolean contains(int rx, int rz)
{
return tag.containsKey(rx + "." + rz);
}
public void delete(int rx, int rz)
{
tag.remove(rx + "." + rz);
}
public void set(int rx, int rz, AtomicChunkData data) throws IOException
{
ByteArrayOutputStream boas = new ByteArrayOutputStream();
data.write(boas);
tag.put(rx + "." + rz, new ByteArrayTag(rx + "." + rz, boas.toByteArray()));
}
public AtomicChunkData get(int rx, int rz) throws IOException
{
if(!contains(rx, rz))
{
return null;
}
AtomicChunkData data = new AtomicChunkData(world);
ByteArrayTag btag = (ByteArrayTag) tag.get(rx + "." + rz);
ByteArrayInputStream in = new ByteArrayInputStream(btag.getValue());
data.read(in);
return data;
}
public World getWorld()
{
return world;
}
}

View File

@@ -0,0 +1,158 @@
package ninja.bytecode.iris.generator.atomics;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.bukkit.World;
import ninja.bytecode.iris.util.SMCAVector;
import ninja.bytecode.shuriken.collections.GList;
import ninja.bytecode.shuriken.collections.GMap;
public class AtomicWorldData
{
private World world;
private GMap<SMCAVector, AtomicRegionData> loadedSections;
public AtomicWorldData(World world)
{
this.world = world;
loadedSections = new GMap<>();
getSubregionFolder().mkdirs();
}
public GList<SMCAVector> getLoadedRegions()
{
return loadedSections.k();
}
public AtomicRegionData getSubregion(int x, int z) throws IOException
{
if(!isSectionLoaded(x, z))
{
loadedSections.put(new SMCAVector(x, z), loadSection(x, z));
}
AtomicRegionData f = loadedSections.get(new SMCAVector(x, z));
return f;
}
public void saveAll() throws IOException
{
for(SMCAVector i : loadedSections.keySet())
{
saveSection(i);
}
}
public void unloadAll(boolean save) throws IOException
{
for(SMCAVector i : loadedSections.keySet())
{
unloadSection(i, save);
}
}
public void deleteSection(int x, int z) throws IOException
{
unloadSection(x, z, false);
getSubregionFile(x, z).delete();
}
public boolean isSectionLoaded(int x, int z)
{
return isSectionLoaded(new SMCAVector(x, z));
}
public boolean isSectionLoaded(SMCAVector s)
{
return loadedSections.containsKey(s);
}
public boolean unloadSection(int x, int z, boolean save) throws IOException
{
return unloadSection(new SMCAVector(x, z), save);
}
public boolean unloadSection(SMCAVector s, boolean save) throws IOException
{
if(!isSectionLoaded(s))
{
return false;
}
if(save)
{
saveSection(s);
}
loadedSections.remove(s);
return true;
}
public boolean saveSection(int x, int z) throws IOException
{
return saveSection(new SMCAVector(x, z));
}
public boolean saveSection(SMCAVector s) throws IOException
{
if(!isSectionLoaded(s.getX(), s.getZ()))
{
return false;
}
AtomicRegionData data = loadedSections.get(s);
FileOutputStream fos = new FileOutputStream(getSubregionFile(s.getX(), s.getZ()));
data.write(fos);
fos.close();
return true;
}
public AtomicRegionData loadSection(int x, int z) throws IOException
{
if(isSectionLoaded(x, z))
{
return loadedSections.get(new SMCAVector(x, z));
}
File file = getSubregionFile(x, z);
if(!file.exists())
{
return createSection(x, z);
}
FileInputStream fin = new FileInputStream(file);
AtomicRegionData data = new AtomicRegionData(world);
data.read(fin);
fin.close();
return data;
}
public AtomicRegionData createSection(int x, int z)
{
if(isSectionLoaded(x, z))
{
return loadedSections.get(new SMCAVector(x, z));
}
AtomicRegionData data = new AtomicRegionData(world);
loadedSections.put(new SMCAVector(x, z), data);
return data;
}
public File getSubregionFile(int x, int z)
{
return new File(getSubregionFolder(), "sr." + x + "." + z + ".smca");
}
public File getSubregionFolder()
{
return new File(world.getWorldFolder(), "subregion");
}
}

View File

@@ -10,11 +10,11 @@ import mortar.util.text.C;
import net.md_5.bungee.api.ChatColor;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.generator.parallax.ParallaxCache;
import ninja.bytecode.iris.generator.placer.AtomicParallaxPlacer;
import ninja.bytecode.iris.pack.IrisBiome;
import ninja.bytecode.iris.util.IPlacer;
import ninja.bytecode.iris.util.MB;
import ninja.bytecode.iris.util.ParallaxCache;
import ninja.bytecode.iris.util.SMCAVector;
import ninja.bytecode.shuriken.collections.GList;
import ninja.bytecode.shuriken.collections.GMap;

View File

@@ -7,8 +7,8 @@ import org.bukkit.World;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.pack.IrisBiome;
import ninja.bytecode.iris.util.AtomicChunkData;
import ninja.bytecode.iris.util.GenLayer;
import ninja.bytecode.iris.util.IrisInterpolation;
import ninja.bytecode.iris.util.MB;

View File

@@ -7,8 +7,8 @@ import org.bukkit.World;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.pack.IrisBiome;
import ninja.bytecode.iris.util.AtomicChunkData;
import ninja.bytecode.iris.util.GenLayer;
import ninja.bytecode.iris.util.IrisInterpolation;
import ninja.bytecode.iris.util.MB;

View File

@@ -7,7 +7,7 @@ import org.bukkit.World;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.util.AtomicChunkData;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.util.GenLayer;
import ninja.bytecode.iris.util.PolygonGenerator;
import ninja.bytecode.shuriken.math.CNG;

View File

@@ -0,0 +1,45 @@
package ninja.bytecode.iris.generator.parallax;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.pack.IrisBiome;
public class ParallaxAnchor
{
private final int height;
private final int water;
private final IrisBiome biome;
private final AtomicChunkData data;
public ParallaxAnchor(int height, int water, IrisBiome biome, AtomicChunkData data)
{
this.height = height;
this.water = water;
this.biome = biome;
this.data = data;
}
public AtomicChunkData getData()
{
return data;
}
public int getWater()
{
return water;
}
public int getHeight()
{
return height;
}
public int getWaterHeight()
{
return water;
}
public IrisBiome getBiome()
{
return biome;
}
}

View File

@@ -0,0 +1,102 @@
package ninja.bytecode.iris.generator.parallax;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.pack.IrisBiome;
import ninja.bytecode.iris.util.ChunkPlan;
import ninja.bytecode.iris.util.MB;
import ninja.bytecode.iris.util.SMCAVector;
import ninja.bytecode.shuriken.collections.GMap;
import ninja.bytecode.shuriken.collections.GSet;
public class ParallaxCache
{
private GMap<SMCAVector, ChunkPlan> cachePlan;
private GMap<SMCAVector, AtomicChunkData> cacheData;
private GSet<SMCAVector> contains;
private IrisGenerator gen;
public ParallaxCache(IrisGenerator gen)
{
this.gen = gen;
cacheData = new GMap<>();
cachePlan = new GMap<>();
contains = new GSet<>();
}
public MB get(int x, int y, int z)
{
SMCAVector s = new SMCAVector(x, z);
SMCAVector c = new SMCAVector(x >> 4, z >> 4);
if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) )
{
return cacheData.get(c).getMB(x & 15, y, z & 15);
}
createData(x, z, s, c);
return cacheData.get(c).getMB(x & 15, y, z & 15);
}
public IrisBiome getBiome(int x, int z)
{
SMCAVector s = new SMCAVector(x, z);
SMCAVector c = new SMCAVector(x >> 4, z >> 4);
if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) )
{
return cachePlan.get(c).getBiome(x & 15, z & 15);
}
createData(x, z, s, c);
return cachePlan.get(c).getBiome(x & 15, z & 15);
}
public int getWaterHeight(int x, int z)
{
SMCAVector s = new SMCAVector(x, z);
SMCAVector c = new SMCAVector(x >> 4, z >> 4);
if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) )
{
return cachePlan.get(c).getRealWaterHeight(x & 15, z & 15);
}
createData(x, z, s, c);
return cachePlan.get(c).getRealWaterHeight(x & 15, z & 15);
}
public int getHeight(int x, int z)
{
SMCAVector s = new SMCAVector(x, z);
SMCAVector c = new SMCAVector(x >> 4, z >> 4);
if(contains.contains(s) && cacheData.containsKey(c) && cachePlan.containsKey(c) )
{
return cachePlan.get(c).getRealHeight(x & 15, z & 15);
}
createData(x, z, s, c);
return cachePlan.get(c).getRealHeight(x & 15, z & 15);
}
private void createData(int x, int z, SMCAVector s, SMCAVector c)
{
if(!cacheData.containsKey(c))
{
cacheData.put(c, new AtomicChunkData(gen.getWorld()));
}
if(!cachePlan.containsKey(c))
{
cachePlan.put(c, new ChunkPlan());
}
gen.computeAnchor(x, z, cachePlan.get(c), cacheData.get(c));
contains.add(s);
}
}

View File

@@ -0,0 +1,227 @@
package ninja.bytecode.iris.generator.parallax;
import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.ChunkLoadEvent;
import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import mortar.api.nms.NMP;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.controller.TimingsController;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.util.ChunkPlan;
import ninja.bytecode.iris.util.IrisWorldData;
import ninja.bytecode.iris.util.SChunkVector;
import ninja.bytecode.shuriken.bench.PrecisionStopwatch;
import ninja.bytecode.shuriken.collections.GSet;
import ninja.bytecode.shuriken.execution.ChronoLatch;
import ninja.bytecode.shuriken.execution.TaskExecutor.TaskGroup;
import ninja.bytecode.shuriken.format.F;
import ninja.bytecode.shuriken.math.RNG;
public abstract class ParallaxWorldGenerator extends ParallelChunkGenerator implements Listener
{
private World world;
private IrisWorldData data;
private RNG rMaster;
private AtomicChunkData buffer;
private GSet<Chunk> fix;
private ChronoLatch cl;
protected boolean saving;
@Override
public final void init(World world, Random random)
{
this.world = world;
saving = true;
cl = new ChronoLatch(3000);
fix = new GSet<>();
buffer = new AtomicChunkData(world);
this.data = new IrisWorldData(world);
this.rMaster = new RNG(world.getSeed() + 1);
onInit(world, rMaster.nextParallelRNG(1));
Bukkit.getPluginManager().registerEvents(this, Iris.instance);
}
public void disableSaving()
{
saving = false;
data.disableSaving();
}
public void enableSaving()
{
saving = true;
data.enableSaving();
}
@EventHandler
public void on(ChunkLoadEvent e)
{
if(!saving)
{
return;
}
if(!Iris.settings.performance.fastMode && e.getWorld().equals(world))
{
NMP.host.relight(e.getChunk());
Bukkit.getScheduler().scheduleSyncDelayedTask(Iris.instance, () -> fix.add(e.getChunk()), 20);
if(cl.flip())
{
for(Chunk i : fix)
{
for(Player f : e.getWorld().getPlayers())
{
NMP.CHUNK.refreshIgnorePosition(f, i);
}
}
fix.clear();
}
}
}
@EventHandler
public void on(WorldUnloadEvent e)
{
if(e.getWorld().equals(world))
{
getWorldData().dispose();
onUnload();
}
}
@EventHandler
public void on(WorldSaveEvent e)
{
if(!saving)
{
return;
}
if(e.getWorld().equals(world))
{
getWorldData().saveAll();
}
}
public ParallaxAnchor computeAnchor(int wx, int wz, ChunkPlan heightBuffer, AtomicChunkData data)
{
onGenColumn(wx, wz, wx & 15, wz & 15, heightBuffer, data);
return new ParallaxAnchor(heightBuffer.getRealHeight(wx & 15, wz & 15), heightBuffer.getRealWaterHeight(wx & 15, wz & 15), heightBuffer.getBiome(wx & 15, wz & 15), data);
}
public ParallaxAnchor computeAnchor(int wx, int wz)
{
ChunkPlan heightBuffer = new ChunkPlan();
onGenColumn(wx, wz, wx & 15, wz & 15, heightBuffer, buffer);
return new ParallaxAnchor(heightBuffer.getRealHeight(wx & 15, wz & 15), heightBuffer.getRealWaterHeight(wx & 15, wz & 15), heightBuffer.getBiome(wx & 15, wz & 15), buffer);
}
public void doGenParallax(int x, int z)
{
onGenParallax(x, z, getRMaster(x, z, -59328));
getWorldData().getChunk(x, z);
}
@Override
public final ChunkPlan initChunk(World world, int x, int z, Random random)
{
PrecisionStopwatch ps = PrecisionStopwatch.start();
TaskGroup g = startWork();
int gg = 0;
int gx = 0;
for(int ii = Iris.settings.performance.fastMode ? -1 : -(getParallaxSize().getX() / 2) - 1; ii < (Iris.settings.performance.fastMode ? 1 : ((getParallaxSize().getX() / 2) + 1)); ii++)
{
int i = ii;
for(int jj = Iris.settings.performance.fastMode ? -1 : -(getParallaxSize().getZ() / 2) - 1; jj < (Iris.settings.performance.fastMode ? 1 : ((getParallaxSize().getZ() / 2) + 1)); jj++)
{
gx++;
int j = jj;
int cx = x + i;
int cz = z + j;
if(!getWorldData().exists(cx, cz))
{
g.queue(() ->
{
onGenParallax(cx, cz, getRMaster(cx, cz, -59328));
getWorldData().getChunk(cx, cz);
});
gg++;
}
}
}
double a = ps.getMilliseconds();
double b = g.execute().timeElapsed;
if(Iris.settings.performance.verbose)
{
System.out.println("MS: " + F.duration(Iris.getController(TimingsController.class).getResult("terrain"), 2) + " \tQMS: " + F.duration(a, 2) + " " + " \tEMS: " + F.duration(b, 2) + "\tSCG: " + gg + " / " + gx + " (" + F.pc(((double) gg / (double) gx)) + ") " + " \tTC: " + F.f(getWorldData().getLoadedChunks().size()) + " \tTR: " + getWorldData().getLoadedRegions().size());
}
return onInitChunk(world, x, z, random);
}
@Override
public final void postChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan)
{
getWorldData().inject(x, z, data);
onPostChunk(world, x, z, random, data, plan);
}
@Override
public final Biome genColumn(int wx, int wz, int x, int z, ChunkPlan plan, AtomicChunkData data)
{
return onGenColumn(wx, wz, x, z, plan, data);
}
public World getWorld()
{
return world;
}
public IrisWorldData getWorldData()
{
return data;
}
public RNG getRMaster()
{
return rMaster;
}
public RNG getRMaster(int x, int z, int signature)
{
return rMaster.nextParallelRNG((int) (signature + x * z + z + x * 2.12));
}
protected abstract void onUnload();
protected abstract SChunkVector getParallaxSize();
public abstract void onGenParallax(int x, int z, Random random);
public abstract void onInit(World world, Random random);
public abstract ChunkPlan onInitChunk(World world, int x, int z, Random random);
public abstract Biome onGenColumn(int wx, int wz, int x, int z, ChunkPlan plan, AtomicChunkData data2);
public abstract void onPostChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan);
}

View File

@@ -0,0 +1,163 @@
package ninja.bytecode.iris.generator.parallax;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.generator.ChunkGenerator;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.controller.ExecutionController;
import ninja.bytecode.iris.controller.TimingsController;
import ninja.bytecode.iris.generator.atomics.AtomicChunkData;
import ninja.bytecode.iris.util.ChunkPlan;
import ninja.bytecode.iris.util.ChunkSpliceListener;
import ninja.bytecode.shuriken.execution.TaskExecutor;
import ninja.bytecode.shuriken.execution.TaskExecutor.TaskGroup;
import ninja.bytecode.shuriken.execution.TaskExecutor.TaskResult;
import ninja.bytecode.shuriken.logging.L;
import ninja.bytecode.shuriken.math.RollingSequence;
import ninja.bytecode.shuriken.reaction.O;
public abstract class ParallelChunkGenerator extends ChunkGenerator
{
private int i;
private int j;
private int wx;
private int wz;
private ReentrantLock biomeLock;
private TaskGroup tg;
private boolean ready = false;
int cg = 0;
private RollingSequence rs = new RollingSequence(512);
private World world;
private TaskExecutor genPool;
private TaskExecutor genPar;
private ChunkSpliceListener splicer;
public void setSplicer(ChunkSpliceListener splicer)
{
this.splicer = splicer;
}
public World getWorld()
{
return world;
}
public Biome generateFullColumn(int a, int b, int c, int d, ChunkPlan p, AtomicChunkData data)
{
return genColumn(a, b, c, d, p, data);
}
public TaskGroup startParallaxWork()
{
if(genPar == null)
{
genPar = Iris.getController(ExecutionController.class).getExecutor(world, "Parallax");
}
return genPar.startWork();
}
public TaskGroup startWork()
{
if(genPool == null)
{
genPool = Iris.getController(ExecutionController.class).getExecutor(world, "Generator");
}
return genPool.startWork();
}
public ChunkData generateChunkData(World world, Random random, int x, int z, BiomeGrid biome)
{
if(splicer != null)
{
AtomicChunkData d = splicer.onSpliceAvailable(world, random, x, z, biome);
if(d != null)
{
return d.toChunkData();
}
}
AtomicChunkData data = new AtomicChunkData(world);
try
{
Iris.getController(TimingsController.class).started("terrain");
this.world = world;
if(!ready)
{
biomeLock = new ReentrantLock();
init(world, random);
ready = true;
}
tg = startWork();
O<ChunkPlan> plan = new O<ChunkPlan>();
for(i = 0; i < 16; i++)
{
wx = (x << 4) + i;
for(j = 0; j < 16; j++)
{
wz = (z << 4) + j;
int a = wx;
int b = wz;
int c = i;
int d = j;
tg.queue(() ->
{
Biome f = generateFullColumn(a, b, c, d, plan.get(), data);
biomeLock.lock();
biome.setBiome(c, d, f);
biomeLock.unlock();
});
}
}
plan.set(initChunk(world, x, z, random));
TaskResult r = tg.execute();
postChunk(world, x, z, random, data, plan.get());
rs.put(r.timeElapsed);
cg++;
Iris.getController(TimingsController.class).stopped("terrain");
}
catch(Throwable e)
{
try
{
for(int i = 0; i < 16; i++)
{
for(int j = 0; j < 16; j++)
{
data.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA);
}
}
}
catch(Throwable ex)
{
}
e.printStackTrace();
}
return data.toChunkData();
}
public abstract void init(World world, Random random);
public abstract ChunkPlan initChunk(World world, int x, int z, Random random);
public abstract void postChunk(World world, int x, int z, Random random, AtomicChunkData data, ChunkPlan plan);
public abstract Biome genColumn(int wx, int wz, int x, int z, ChunkPlan plan, AtomicChunkData data);
}

View File

@@ -3,9 +3,9 @@ package ninja.bytecode.iris.generator.placer;
import org.bukkit.Location;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.generator.parallax.ParallaxCache;
import ninja.bytecode.iris.util.IrisWorldData;
import ninja.bytecode.iris.util.MB;
import ninja.bytecode.iris.util.ParallaxCache;
import ninja.bytecode.iris.util.Placer;
public class AtomicParallaxPlacer extends Placer