Total rotation support

This commit is contained in:
Daniel Mills
2020-08-01 08:46:52 -04:00
parent 71c90c6d31
commit f3d87f09d7
77 changed files with 264 additions and 178 deletions

View File

@@ -0,0 +1,162 @@
package com.volmit.iris.object.atomics;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import org.bukkit.World;
import com.volmit.iris.IrisSettings;
import com.volmit.iris.util.ByteArrayTag;
import com.volmit.iris.util.CompoundTag;
import com.volmit.iris.util.CustomOutputStream;
import com.volmit.iris.util.KMap;
import com.volmit.iris.util.NBTInputStream;
import com.volmit.iris.util.NBTOutputStream;
import com.volmit.iris.util.Tag;
public class AtomicRegionData
{
private final World world;
private Tag[] tag;
public AtomicRegionData(World world)
{
this.world = world;
tag = new Tag[1024];
}
public int size()
{
return tag.length;
}
public void read(InputStream in) throws IOException
{
NBTInputStream nin = new NBTInputStream(in);
KMap<String, Tag> tags = new KMap<>();
tags.putAll(((CompoundTag) nin.readTag()).getValue());
for(String i : tags.keySet())
{
int x = Integer.valueOf(i.split("\\Q.\\E")[0]);
int z = Integer.valueOf(i.split("\\Q.\\E")[1]);
tag[(z << 5) | x] = tags.get(i);
}
nin.close();
}
public void write(OutputStream out) throws IOException
{
NBTOutputStream nos = new NBTOutputStream(out);
KMap<String, Tag> tags = new KMap<>();
for(int i = 0; i < 32; i++)
{
for(int j = 0; j < 32; j++)
{
if(tag[(j << 5) | i] != null)
{
tags.put(i + "." + j, tag[(j << 5) | i]);
}
}
}
nos.writeTag(new CompoundTag("imca", tags));
nos.close();
}
public boolean contains(int rx, int rz)
{
return tag[(rz << 5) | rx] != null;
}
public void delete(int rx, int rz)
{
tag[(rz << 5) | rx] = null;
}
public void set(int rx, int rz, AtomicSliverMap data) throws IOException
{
if(data == null)
{
return;
}
OutputStream out;
ByteArrayOutputStream boas = new ByteArrayOutputStream();
out = boas;
if(IrisSettings.get().parallaxCompression)
{
out = new CustomOutputStream(boas, IrisSettings.get().parallaxCompressionLevel);
}
data.write(out);
out.flush();
out.close();
tag[(rz << 5) | rx] = new ByteArrayTag(rx + "." + rz, boas.toByteArray());
}
public AtomicSliverMap get(int rx, int rz) throws IOException
{
AtomicSliverMap data = new AtomicSliverMap();
if(!contains(rx, rz))
{
return data;
}
try
{
ByteArrayTag btag = (ByteArrayTag) tag[(rz << 5) | rx];
InputStream in;
if(IrisSettings.get().parallaxCompression)
{
in = new GZIPInputStream(new ByteArrayInputStream(btag.getValue()));
}
else
{
in = new ByteArrayInputStream(btag.getValue());
}
data.read(in);
}
catch(Throwable e)
{
}
return data;
}
public World getWorld()
{
return world;
}
public long guessMemoryUsage()
{
long bytes = 0;
for(int i = 0; i < 1024; i++)
{
if(tag[i] != null && tag[i] instanceof ByteArrayTag)
{
bytes += 122;
bytes += ((ByteArrayTag) tag[i]).getValue().length;
}
}
return bytes;
}
}

View File

@@ -0,0 +1,243 @@
package com.volmit.iris.object.atomics;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.locks.ReentrantLock;
import org.bukkit.Material;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
import org.bukkit.generator.ChunkGenerator.ChunkData;
import com.volmit.iris.object.IrisBiome;
import com.volmit.iris.util.BlockDataTools;
import com.volmit.iris.util.HeightMap;
import com.volmit.iris.util.KMap;
import com.volmit.iris.util.M;
import lombok.Data;
@Data
public class AtomicSliver
{
public static final BlockData AIR = BlockDataTools.getBlockData("AIR");
private KMap<Integer, BlockData> block;
private KMap<Integer, IrisBiome> truebiome;
private KMap<Integer, Biome> biome;
private ReentrantLock lock = new ReentrantLock();
private int highestBlock = 0;
private int highestBiome = 0;
private long last = M.ms();
private int x;
private int z;
boolean modified = false;
public AtomicSliver(int x, int z)
{
this.x = x;
this.z = z;
this.block = new KMap<>();
this.biome = new KMap<>();
this.truebiome = new KMap<>();
}
public Material getType(int h)
{
return get(h).getMaterial();
}
public BlockData get(int h)
{
BlockData b = block.get(h);
last = M.ms();
if(b == null)
{
return AIR;
}
return b;
}
public void set(int h, BlockData d)
{
if(d == null)
{
return;
}
lock.lock();
block.put(h, d);
modified = true;
if(d.getMaterial().equals(Material.AIR) || d.getMaterial().equals(Material.CAVE_AIR))
{
lock.unlock();
return;
}
highestBlock = h > highestBlock ? h : highestBlock;
lock.unlock();
}
public void setSilently(int h, BlockData d)
{
if(d == null)
{
return;
}
lock.lock();
modified = true;
block.put(h, d);
lock.unlock();
}
public boolean isSolid(int h)
{
return getType(h).isSolid();
}
public Biome getBiome(int h)
{
last = M.ms();
return biome.containsKey(h) ? biome.get(h) : Biome.THE_VOID;
}
public IrisBiome getTrueBiome(int h)
{
last = M.ms();
return truebiome.get(h);
}
public void set(int h, Biome d)
{
lock.lock();
biome.put(h, d);
modified = true;
highestBiome = h > highestBiome ? h : highestBiome;
lock.unlock();
}
public void set(int h, IrisBiome d)
{
lock.lock();
modified = true;
truebiome.put(h, d);
lock.unlock();
}
public void write(ChunkData d)
{
lock.lock();
for(int i = 0; i <= highestBlock; i++)
{
if(block.get(i) == null)
{
d.setBlock(x, i, z, AIR);
}
else
{
d.setBlock(x, i, z, block.get(i));
}
}
lock.unlock();
}
public void write(BiomeGrid d)
{
lock.lock();
for(int i = 0; i <= highestBiome; i++)
{
if(biome.get(i) != null)
{
d.setBiome(x, i, z, biome.get(i));
}
}
lock.unlock();
}
public void write(HeightMap height)
{
lock.lock();
height.setHeight(x, z, highestBlock);
lock.unlock();
}
public void read(DataInputStream din) throws IOException
{
lock.lock();
this.block = new KMap<Integer, BlockData>();
int h = din.readByte() - Byte.MIN_VALUE;
highestBlock = h;
for(int i = 0; i <= h; i++)
{
BlockData v = BlockDataTools.getBlockData(din.readUTF());
if(v == null)
{
block.put(i, AIR);
continue;
}
block.put(i, v);
}
modified = false;
lock.unlock();
}
public void write(DataOutputStream dos) throws IOException
{
lock.lock();
dos.writeByte(highestBlock + Byte.MIN_VALUE);
for(int i = 0; i <= highestBlock; i++)
{
BlockData dat = block.get(i);
dos.writeUTF((dat == null ? AIR : dat).getAsString(true));
}
lock.unlock();
}
public void insert(AtomicSliver atomicSliver)
{
lock.lock();
for(int i = 0; i < 256; i++)
{
if(block.get(i) == null || block.get(i).equals(AIR))
{
BlockData b = atomicSliver.block.get(i);
if(b == null || b.equals(AIR))
{
continue;
}
block.put(i, b);
}
}
lock.unlock();
}
public void inject(ChunkData currentData)
{
lock.lock();
for(int i = 0; i < 256; i++)
{
if(block.get(i) != null && !block.get(i).equals(AIR))
{
BlockData b = block.get(i);
currentData.setBlock(x, i, z, b);
}
}
lock.unlock();
}
public boolean isOlderThan(long m)
{
return M.ms() - last > m;
}
}

View File

@@ -0,0 +1,117 @@
package com.volmit.iris.object.atomics;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
import org.bukkit.generator.ChunkGenerator.ChunkData;
import com.volmit.iris.util.HeightMap;
import lombok.Data;
@Data
public class AtomicSliverMap
{
private final AtomicSliver[] slivers;
private boolean parallaxGenerated;
private boolean worldGenerated;
public AtomicSliverMap()
{
parallaxGenerated = false;
worldGenerated = false;
slivers = new AtomicSliver[256];
for(int i = 0; i < 16; i++)
{
for(int j = 0; j < 16; j++)
{
slivers[i * 16 + j] = new AtomicSliver(i, j);
}
}
}
public void insert(AtomicSliverMap map)
{
for(int i = 0; i < 256; i++)
{
slivers[i].insert(map.slivers[i]);
}
}
public void write(OutputStream out) throws IOException
{
DataOutputStream dos = new DataOutputStream(out);
dos.writeBoolean(isParallaxGenerated());
dos.writeBoolean(isWorldGenerated());
for(int i = 0; i < 256; i++)
{
slivers[i].write(dos);
}
dos.flush();
}
public void read(InputStream in) throws IOException
{
DataInputStream din = new DataInputStream(in);
parallaxGenerated = din.readBoolean();
worldGenerated = din.readBoolean();
for(int i = 0; i < 256; i++)
{
try
{
slivers[i].read(din);
}
catch(Throwable e)
{
}
}
}
public AtomicSliver getSliver(int x, int z)
{
return slivers[x * 16 + z];
}
public void write(ChunkData data, BiomeGrid grid, HeightMap height)
{
for(AtomicSliver i : slivers)
{
if(i != null)
{
i.write(data);
i.write(grid);
i.write(height);
}
}
}
public void inject(ChunkData currentData)
{
for(AtomicSliver i : slivers)
{
i.inject(currentData);
}
}
public boolean isModified()
{
for(AtomicSliver i : slivers)
{
if(i.isModified())
{
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,348 @@
package com.volmit.iris.object.atomics;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.bukkit.World;
import com.volmit.iris.Iris;
import com.volmit.iris.util.ChunkPosition;
import com.volmit.iris.util.KList;
import com.volmit.iris.util.KMap;
import com.volmit.iris.util.M;
public class AtomicWorldData
{
private World world;
private KMap<ChunkPosition, AtomicSliverMap> loadedChunks;
private KMap<ChunkPosition, AtomicRegionData> loadedSections;
private KMap<ChunkPosition, Long> lastRegion;
private KMap<ChunkPosition, Long> lastChunk;
private KList<ChunkPosition> unloadRegions;
private KList<ChunkPosition> unloadChunks;
private long last = M.ms();
private String prefix;
public AtomicWorldData(World world, String prefix)
{
this.world = world;
loadedSections = new KMap<>();
loadedChunks = new KMap<>();
lastRegion = new KMap<>();
lastChunk = new KMap<>();
unloadRegions = new KList<>();
unloadChunks = new KList<>();
this.prefix = prefix;
getSubregionFolder().mkdirs();
}
public KMap<ChunkPosition, AtomicRegionData> getLoadedRegions()
{
return loadedSections;
}
public AtomicRegionData getSubregion(int x, int z) throws IOException
{
lastRegion.put(new ChunkPosition(x, z), M.ms());
if(!isSectionLoaded(x, z))
{
loadedSections.put(new ChunkPosition(x, z), loadSection(x, z));
}
AtomicRegionData f = loadedSections.get(new ChunkPosition(x, z));
return f;
}
public void saveAll() throws IOException
{
saveChunks();
for(ChunkPosition i : loadedSections.keySet())
{
saveSection(i);
}
}
public void unloadAll(boolean save) throws IOException
{
saveChunks();
for(ChunkPosition i : loadedSections.keySet())
{
unloadSection(i, save);
}
loadedSections.clear();
loadedChunks.clear();
lastRegion.clear();
}
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 ChunkPosition(x, z));
}
public boolean isSectionLoaded(ChunkPosition s)
{
return loadedSections.containsKey(s);
}
public boolean unloadSection(int x, int z, boolean save) throws IOException
{
return unloadSection(new ChunkPosition(x, z), save);
}
public boolean unloadSection(ChunkPosition s, boolean save) throws IOException
{
if(!isSectionLoaded(s))
{
Iris.warn("Cant unload because section isnt loaded?");
return false;
}
if(save)
{
saveSection(s);
}
loadedSections.remove(s);
lastRegion.remove(s);
return true;
}
public boolean saveSection(int x, int z) throws IOException
{
return saveSection(new ChunkPosition(x, z));
}
public boolean saveSection(ChunkPosition s) throws IOException
{
if(!isSectionLoaded(s.getX(), s.getZ()))
{
Iris.warn("Cant save section " + s.getX() + " " + s.getZ() + " because section isnt loaded?");
return false;
}
saveChunks(s);
AtomicRegionData data = loadedSections.get(s);
FileOutputStream fos = new FileOutputStream(getSubregionFile(s.getX(), s.getZ()));
data.write(fos);
fos.close();
return true;
}
public void saveChunks() throws IOException
{
for(ChunkPosition i : loadedChunks.k())
{
saveChunk(i);
}
}
public void saveChunks(ChunkPosition reg) throws IOException
{
for(ChunkPosition i : loadedChunks.k())
{
int x = i.getX();
int z = i.getZ();
if(x >> 5 == reg.getX() && z >> 5 == reg.getZ())
{
saveChunk(i);
}
}
}
public void saveChunk(ChunkPosition i) throws IOException
{
AtomicSliverMap m = loadedChunks.get(i);
if(m.isModified())
{
int x = i.getX();
int z = i.getZ();
AtomicRegionData dat = loadSection(x >> 5, z >> 5, true);
dat.set(x & 31, z & 31, m);
}
loadedChunks.remove(i);
lastChunk.remove(i);
}
public AtomicSliverMap loadChunk(int x, int z) throws IOException
{
ChunkPosition pos = new ChunkPosition(x, z);
lastChunk.put(pos, M.ms());
if(loadedChunks.containsKey(pos))
{
return loadedChunks.get(pos);
}
AtomicRegionData dat = loadSection(x >> 5, z >> 5);
AtomicSliverMap m = dat.get(x & 31, z & 31);
loadedChunks.put(pos, m);
return m;
}
public boolean hasChunk(int x, int z) throws IOException
{
return loadSection(x >> 5, z >> 5).contains(x & 31, z & 31);
}
public AtomicRegionData loadSection(int x, int z, boolean anonymous) throws IOException
{
ChunkPosition pos = new ChunkPosition(x, z);
if(!anonymous)
{
lastRegion.put(pos, M.ms());
}
if(isSectionLoaded(x, z))
{
return loadedSections.get(pos);
}
File file = getSubregionFile(x, z);
if(!file.exists())
{
AtomicRegionData dat = createSection(x, z);
loadedSections.put(pos, dat);
return dat;
}
FileInputStream fin = new FileInputStream(file);
AtomicRegionData data = new AtomicRegionData(world);
data.read(fin);
fin.close();
loadedSections.put(pos, data);
return data;
}
public AtomicRegionData loadSection(int x, int z) throws IOException
{
return loadSection(x, z, false);
}
public AtomicRegionData createSection(int x, int z)
{
if(isSectionLoaded(x, z))
{
return loadedSections.get(new ChunkPosition(x, z));
}
AtomicRegionData data = new AtomicRegionData(world);
loadedSections.put(new ChunkPosition(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-" + prefix);
}
public KMap<ChunkPosition, AtomicSliverMap> getLoadedChunks()
{
return loadedChunks;
}
public void clean(int j)
{
if(M.ms() - last < getUnloadBatchSpeed())
{
return;
}
int m = 0;
for(ChunkPosition i : lastRegion.keySet())
{
if(m > 1)
{
break;
}
if(M.ms() - lastRegion.get(i) > 60000)
{
unloadRegions.add(i);
m++;
}
}
m = 0;
for(ChunkPosition i : unloadRegions)
{
lastRegion.remove(i);
try
{
unloadSection(i, true);
}
catch(IOException e)
{
e.printStackTrace();
}
}
unloadRegions.clear();
for(ChunkPosition i : lastChunk.keySet())
{
if(m > getUnloadBatchSize())
{
break;
}
if(M.ms() - lastChunk.get(i) > 15000)
{
m++;
unloadChunks.add(i);
}
}
for(ChunkPosition i : unloadChunks)
{
try
{
saveChunk(i);
}
catch(IOException e)
{
Iris.warn("Failed to save chunk");
}
}
unloadChunks.clear();
}
private int getUnloadBatchSize()
{
return Math.max(3, getLoadedRegions().size() / 125);
}
private int getUnloadBatchSpeed()
{
return Math.max(250, 2000 - getLoadedRegions().size());
}
}

View File

@@ -0,0 +1,48 @@
package com.volmit.iris.object.atomics;
import java.util.concurrent.locks.ReentrantLock;
import com.volmit.iris.util.KMap;
public class MasterLock
{
private KMap<String, ReentrantLock> locks;
private ReentrantLock lock;
public MasterLock()
{
locks = new KMap<>();
lock = new ReentrantLock();
}
public void clear()
{
locks.clear();
}
public void lock(String key)
{
lock.lock();
if(!locks.containsKey(key))
{
locks.put(key, new ReentrantLock());
}
ReentrantLock l = locks.get(key);
lock.unlock();
l.lock();
}
public void unlock(String key)
{
lock.lock();
if(!locks.containsKey(key))
{
locks.put(key, new ReentrantLock());
}
ReentrantLock l = locks.get(key);
lock.unlock();
l.unlock();
}
}