This commit is contained in:
Daniel Mills
2021-08-07 08:16:24 -04:00
parent 6683eee49a
commit 6a68da0559
14 changed files with 138 additions and 237 deletions

View File

@@ -31,7 +31,10 @@ import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.HyperLock;
import com.volmit.iris.util.parallel.MultiBurst;
import java.io.*;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
@@ -43,8 +46,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
* This class is fully thread safe read & write
*/
public class Mantle
{
public class Mantle {
private final File dataFolder;
private final int worldHeight;
private final Map<Long, Long> lastUse;
@@ -56,12 +58,12 @@ public class Mantle
/**
* Create a new mantle
* @param dataFolder the data folder
*
* @param dataFolder the data folder
* @param worldHeight the world's height (in blocks)
*/
@BlockCoordinates
public Mantle(File dataFolder, int worldHeight)
{
public Mantle(File dataFolder, int worldHeight) {
this.hyperLock = new HyperLock();
this.closed = new AtomicBoolean(false);
this.dataFolder = dataFolder;
@@ -82,25 +84,23 @@ public class Mantle
* reading & writing other regions. Hyperlocks are slow sync, but in multicore
* environments, they drastically speed up loading & saving large counts of plates
*
* @param x the block's x coordinate
* @param y the block's y coordinate
* @param z the block's z coordinate
* @param t the data to set at the block
* @param x the block's x coordinate
* @param y the block's y coordinate
* @param z the block's z coordinate
* @param t the data to set at the block
* @param <T> the type of data (generic method)
*/
@BlockCoordinates
public <T> void set(int x, int y, int z, T t)
{
if(closed.get())
{
public <T> void set(int x, int y, int z, T t) {
if (closed.get()) {
throw new RuntimeException("The Mantle is closed");
}
MantleMatter matter = null;
try {
matter = get((x >> 4) >> 5, (z >> 4) >> 5).get()
.getOrCreate((x >> 4) & 31, (z >> 4) & 31)
.getOrCreate(y >> 4);
.getOrCreate((x >> 4) & 31, (z >> 4) & 31)
.getOrCreate(y >> 4);
} catch (InterruptedException e) {
Iris.error("Failed to get Tectonic Plate " + ((x >> 4) >> 5) + " " + ((z >> 4) >> 5) + " Due to a thread intterruption");
Iris.reportError(e);
@@ -111,13 +111,12 @@ public class Mantle
e.printStackTrace();
}
if(matter == null)
{
if (matter == null) {
return;
}
matter.slice(matter.getClass(t))
.set(x & 15, y & 15, z & 15, t);
.set(x & 15, y & 15, z & 15, t);
}
/**
@@ -128,19 +127,17 @@ public class Mantle
* reading & writing other regions. Hyperlocks are slow sync, but in multicore
* environments, they drastically speed up loading & saving large counts of plates
*
* @param x the block's x coordinate
* @param y the block's y coordinate
* @param z the block's z coordinate
* @param t the class representing the type of data being requested
* @param x the block's x coordinate
* @param y the block's y coordinate
* @param z the block's z coordinate
* @param t the class representing the type of data being requested
* @param <T> the type assumed from the provided class
* @return the returned result (or null) if it doesnt exist
*/
@SuppressWarnings("unchecked")
@BlockCoordinates
public <T> T get(int x, int y, int z, Class<T> t)
{
if(closed.get())
{
public <T> T get(int x, int y, int z, Class<T> t) {
if (closed.get()) {
throw new RuntimeException("The Mantle is closed");
}
@@ -167,18 +164,15 @@ public class Mantle
* any data to the mantle or it's Tectonic Plates. Closing will also flush any
* loaded regions to the disk in parallel.
*/
public synchronized void close()
{
public synchronized void close() {
Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
if(closed.get())
{
if (closed.get()) {
throw new RuntimeException("The Mantle is closed");
}
closed.set(true);
BurstExecutor b = ioBurst.burst(loadedRegions.size());
for(Long i : loadedRegions.keySet())
{
for (Long i : loadedRegions.keySet()) {
b.queue(() -> {
try {
loadedRegions.get(i).write(fileForRegion(dataFolder, i));
@@ -196,34 +190,29 @@ public class Mantle
/**
* Save & unload regions that have not been used for more than the
* specified amount of milliseconds
*
* @param idleDuration the duration
*/
public synchronized void trim(long idleDuration)
{
if(closed.get())
{
public synchronized void trim(long idleDuration) {
if (closed.get()) {
throw new RuntimeException("The Mantle is closed");
}
Iris.debug("Trimming Tectonic Plates older than " + Form.duration((double)idleDuration, 0));
Iris.debug("Trimming Tectonic Plates older than " + Form.duration((double) idleDuration, 0));
unload.clear();
for(Long i : lastUse.keySet())
{
if(M.ms() - lastUse.get(i) >= idleDuration)
{
for (Long i : lastUse.keySet()) {
if (M.ms() - lastUse.get(i) >= idleDuration) {
unload.add(i);
}
}
for(Long i : unload)
{
for (Long i : unload) {
TectonicPlate m = loadedRegions.remove(i);
lastUse.remove(i);
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + i);
if(m != null)
{
if (m != null) {
ioBurst.lazy(() -> {
try {
m.write(fileForRegion(dataFolder, i));
@@ -238,39 +227,33 @@ public class Mantle
/**
* This retreives a future of the Tectonic Plate at the given coordinates.
* All methods accessing tectonic plates should go through this method
*
* @param x the region x
* @param z the region z
* @return the future of a tectonic plate.
*/
@RegionCoordinates
private CompletableFuture<TectonicPlate> get(int x, int z)
{
private CompletableFuture<TectonicPlate> get(int x, int z) {
return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> {
Long k = key(x, z);
lastUse.put(k, M.ms());
TectonicPlate region = loadedRegions.get(k);
if(region != null)
{
if (region != null) {
return region;
}
File file = fileForRegion(dataFolder, x, z);
if(file.exists())
{
try
{
if (file.exists()) {
try {
FileInputStream fin = new FileInputStream(file);
DataInputStream din = new DataInputStream(fin);
region = new TectonicPlate(worldHeight, din);
din.close();
loadedRegions.put(k, region);
Iris.debug("Loaded Tectonic Plate " + C.DARK_GREEN + x + " " + z + C.DARK_AQUA + " " + file.getName());
}
catch(Throwable e)
{
} catch (Throwable e) {
Iris.error("Failed to read Tectonic Plate " + file.getAbsolutePath() + " creating a new chunk instead.");
Iris.reportError(e);
e.printStackTrace();
@@ -300,8 +283,7 @@ public class Mantle
return f;
}
public static Long key(int x, int z)
{
public static Long key(int x, int z) {
return Cache.key(x, z);
}
}

View File

@@ -18,13 +18,10 @@
package com.volmit.iris.util.mantle;
import com.volmit.iris.engine.data.chunk.MCATerrainChunk;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.nbt.mca.Section;
import lombok.Data;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -40,29 +37,28 @@ public class MantleChunk {
/**
* Create a mantle chunk
*
* @param sectionHeight the height of the world in sections (blocks >> 4)
*/
@ChunkCoordinates
public MantleChunk(int sectionHeight)
{
public MantleChunk(int sectionHeight) {
sections = new AtomicReferenceArray<>(sectionHeight);
}
/**
* Load a mantle chunk from a data stream
*
* @param sectionHeight the height of the world in sections (blocks >> 4)
* @param din the data input
* @throws IOException shit happens
* @param din the data input
* @throws IOException shit happens
* @throws ClassNotFoundException shit happens
*/
public MantleChunk(int sectionHeight, DataInputStream din) throws IOException, ClassNotFoundException {
this(sectionHeight);
int s = Varint.readUnsignedVarInt(din);
for(int i = 0; i < s; i++)
{
if(din.readBoolean())
{
for (int i = 0; i < s; i++) {
if (din.readBoolean()) {
sections.set(i, Matter.read(din));
}
}
@@ -70,59 +66,56 @@ public class MantleChunk {
/**
* Check if a section exists (same as get(section) != null)
*
* @param section the section (0 - (worldHeight >> 4))
* @return true if it exists
*/
@ChunkCoordinates
public boolean exists(int section)
{
public boolean exists(int section) {
return get(section) != null;
}
/**
* Get thje matter at the given section or null if it doesnt exist
*
* @param section the section (0 - (worldHeight >> 4))
* @return the matter or null if it doesnt exist
*/
@ChunkCoordinates
public Matter get(int section)
{
public Matter get(int section) {
return sections.get(section);
}
/**
* Clear all matter from this chunk
*/
public void clear()
{
for(int i = 0; i < sections.length(); i++)
{
public void clear() {
for (int i = 0; i < sections.length(); i++) {
delete(i);
}
}
/**
* Delete the matter from the given section
*
* @param section the section (0 - (worldHeight >> 4))
*/
@ChunkCoordinates
public void delete(int section)
{
public void delete(int section) {
sections.set(section, null);
}
/**
* Get or create a new matter section at the given section
*
* @param section the section (0 - (worldHeight >> 4))
* @return the matter
*/
@ChunkCoordinates
public Matter getOrCreate(int section)
{
public Matter getOrCreate(int section) {
Matter matter = get(section);
if(matter == null)
{
if (matter == null) {
matter = new IrisMatter(16, 16, 16);
sections.set(section, matter);
}
@@ -132,23 +125,19 @@ public class MantleChunk {
/**
* Write this chunk to a data stream
*
* @param dos the stream
* @throws IOException shit happens
*/
public void write(DataOutputStream dos) throws IOException {
Varint.writeUnsignedVarInt(sections.length(), dos);
for(int i = 0; i < sections.length(); i++)
{
if(exists(i))
{
for (int i = 0; i < sections.length(); i++) {
if (exists(i)) {
dos.writeBoolean(true);
Matter matter = get(i);
matter.writeDos(dos);
}
else
{
} else {
dos.writeBoolean(false);
}
}

View File

@@ -35,28 +35,27 @@ public class TectonicPlate {
/**
* Create a new tectonic plate
*
* @param worldHeight the height of the world
*/
public TectonicPlate(int worldHeight)
{
public TectonicPlate(int worldHeight) {
this.sectionHeight = worldHeight >> 4;
this.chunks = new AtomicReferenceArray<>(1024);
}
/**
* Load a tectonic plate from a data stream
*
* @param worldHeight the height of the world
* @param din the data input
* @throws IOException shit happens yo
* @param din the data input
* @throws IOException shit happens yo
* @throws ClassNotFoundException real shit bro
*/
public TectonicPlate(int worldHeight, DataInputStream din) throws IOException, ClassNotFoundException {
this(worldHeight);
for(int i = 0; i < chunks.length(); i++)
{
if(din.readBoolean())
{
for (int i = 0; i < chunks.length(); i++) {
if (din.readBoolean()) {
chunks.set(i, new MantleChunk(sectionHeight, din));
}
}
@@ -64,63 +63,60 @@ public class TectonicPlate {
/**
* Check if a chunk exists in this plate or not (same as get(x, z) != null)
*
* @param x the chunk relative x (0-31)
* @param z the chunk relative z (0-31)
* @return true if the chunk exists
*/
@ChunkCoordinates
public boolean exists(int x, int z)
{
public boolean exists(int x, int z) {
return get(x, z) != null;
}
/**
* Get a chunk at the given coordinates or null if it doesnt exist
*
* @param x the chunk relative x (0-31)
* @param z the chunk relative z (0-31)
* @return the chunk or null if it doesnt exist
*/
@ChunkCoordinates
public MantleChunk get(int x, int z)
{
public MantleChunk get(int x, int z) {
return chunks.get(index(x, z));
}
/**
* Clear all chunks from this tectonic plate
*/
public void clear()
{
for(int i = 0; i < chunks.length(); i++)
{
public void clear() {
for (int i = 0; i < chunks.length(); i++) {
chunks.set(i, null);
}
}
/**
* Delete a chunk from this tectonic plate
*
* @param x the chunk relative x (0-31)
* @param z the chunk relative z (0-31)
*/
@ChunkCoordinates
public void delete(int x, int z)
{
public void delete(int x, int z) {
chunks.set(index(x, z), null);
}
/**
* Get a tectonic plate, or create one and insert it & return it if it diddnt exist
*
* @param x the chunk relative x (0-31)
* @param z the chunk relative z (0-31)
* @return the chunk (read or created & inserted)
*/
@ChunkCoordinates
public MantleChunk getOrCreate(int x, int z)
{
public MantleChunk getOrCreate(int x, int z) {
MantleChunk chunk = get(x, z);
if(chunk == null)
{
if (chunk == null) {
chunk = new MantleChunk(sectionHeight);
chunks.set(index(x, z), chunk);
}
@@ -135,6 +131,7 @@ public class TectonicPlate {
/**
* Write this tectonic plate to file
*
* @param file the file to write it to
* @throws IOException shit happens
*/
@@ -148,17 +145,16 @@ public class TectonicPlate {
/**
* Write this tectonic plate to a data stream
*
* @param dos the data output
* @throws IOException shit happens
*/
public void write(DataOutputStream dos) throws IOException {
for(int i = 0; i < chunks.length(); i++)
{
for (int i = 0; i < chunks.length(); i++) {
MantleChunk chunk = chunks.get(i);
dos.writeBoolean(chunk != null);
if(chunk != null)
{
if (chunk != null) {
chunk.write(dos);
}
}