mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-05 15:26:28 +00:00
f
This commit is contained in:
@@ -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;
|
||||
|
||||
277
src/main/java/ninja/bytecode/iris/generator/WorldReactor.java
Normal file
277
src/main/java/ninja/bytecode/iris/generator/WorldReactor.java
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user