diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BasicHunkIOAdapter.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BasicHunkIOAdapter.java new file mode 100644 index 000000000..e38e8d89b --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BasicHunkIOAdapter.java @@ -0,0 +1,69 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.gen.v2.scaffold.hunk.Hunk; +import com.volmit.iris.util.Function3; +import com.volmit.iris.util.Function4; + +import java.io.*; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class BasicHunkIOAdapter implements HunkIOAdapter { + @Override + public void write(Hunk t, OutputStream out) throws IOException { + DataOutputStream dos = new DataOutputStream(out); + dos.writeShort(t.getWidth() + Short.MIN_VALUE); + dos.writeShort(t.getHeight() + Short.MIN_VALUE); + dos.writeShort(t.getDepth() + Short.MIN_VALUE); + dos.writeInt(t.getNonNullEntries() + Integer.MIN_VALUE); + + AtomicBoolean failure = new AtomicBoolean(false); + t.iterate(0, (x,y,z,w) -> { + if(w != null) + { + try + { + dos.writeShort(x + Short.MIN_VALUE); + dos.writeShort(y + Short.MIN_VALUE); + dos.writeShort(z + Short.MIN_VALUE); + write(w, dos); + } + + catch(Throwable e) + { + e.printStackTrace(); + failure.set(true); + } + } + }); + + dos.close(); + } + + @Override + public Hunk read(Function3> factory, InputStream in) throws IOException { + DataInputStream din = new DataInputStream(in); + int w = din.readShort() - Short.MIN_VALUE; + int h = din.readShort() - Short.MIN_VALUE; + int d = din.readShort() - Short.MIN_VALUE; + int e = din.readInt() - Integer.MIN_VALUE; + Hunk t = factory.apply(w, h, d); + + for(int i = 0; i < e; i++) + { + int x = din.readShort() - Short.MIN_VALUE; + int y = din.readShort() - Short.MIN_VALUE; + int z = din.readShort() - Short.MIN_VALUE; + T v = read(din); + + if(v == null) + { + throw new IOException("NULL VALUE AT " + x + " " + y + " " + z); + } + + t.setRaw(x,y,z, v); + } + + in.close(); + return t; + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BlockDataHunkIOAdapter.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BlockDataHunkIOAdapter.java new file mode 100644 index 000000000..9eb27695c --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BlockDataHunkIOAdapter.java @@ -0,0 +1,22 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.util.B; +import org.bukkit.Bukkit; +import org.bukkit.block.data.BlockData; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class BlockDataHunkIOAdapter extends PaletteHunkIOAdapter { + + @Override + public void write(BlockData blockData, DataOutputStream dos) throws IOException { + dos.writeUTF(blockData.getAsString(true)); + } + + @Override + public BlockData read(DataInputStream din) throws IOException { + return B.get(din.readUTF()).getBlockData(); + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BooleanHunkIOAdapter.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BooleanHunkIOAdapter.java new file mode 100644 index 000000000..efe5447b0 --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/BooleanHunkIOAdapter.java @@ -0,0 +1,18 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class BooleanHunkIOAdapter extends PaletteHunkIOAdapter { + + @Override + public void write(Boolean data, DataOutputStream dos) throws IOException { + dos.writeBoolean(data); + } + + @Override + public Boolean read(DataInputStream din) throws IOException { + return din.readBoolean(); + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkCompoundRegion.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkCompoundRegion.java new file mode 100644 index 000000000..0075a40c6 --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkCompoundRegion.java @@ -0,0 +1,37 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.util.CompoundTag; +import lombok.Getter; +import org.bukkit.block.data.BlockData; + +import java.io.File; + +public class HunkCompoundRegion extends HunkRegion { + + @Getter + private HunkRegionSlice parallaxSlice; + @Getter + private HunkRegionSlice objectSlice; + @Getter + private HunkRegionSlice updateSlice; + + private final int height; + + public HunkCompoundRegion(int height, File folder, int x, int z, CompoundTag compound) { + super(folder, x, z, compound); + this.height = height; + setupSlices(); + } + + public HunkCompoundRegion(int height, File folder, int x, int z) { + super(folder, x, z); + this.height = height; + setupSlices(); + } + + private void setupSlices() { + parallaxSlice = HunkRegionSlice.BLOCKDATA.apply(height, getCompound()); + objectSlice = HunkRegionSlice.STRING.apply(height, getCompound(), "objects"); + updateSlice = HunkRegionSlice.BOOLEAN.apply(height, getCompound(), "updates"); + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkIOAdapter.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkIOAdapter.java new file mode 100644 index 000000000..7597fc1f9 --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkIOAdapter.java @@ -0,0 +1,44 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.IrisSettings; +import com.volmit.iris.gen.v2.scaffold.data.IOAdapter; +import com.volmit.iris.gen.v2.scaffold.hunk.Hunk; +import com.volmit.iris.util.ByteArrayTag; +import com.volmit.iris.util.CustomOutputStream; +import com.volmit.iris.util.Function3; + +import java.io.*; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +public interface HunkIOAdapter extends IOAdapter +{ + public void write(Hunk t, OutputStream out) throws IOException; + + public Hunk read(Function3> factory, InputStream in) throws IOException; + + default void write(Hunk t, File f) throws IOException + { + f.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(f); + GZIPOutputStream gzo = new CustomOutputStream(fos, IrisSettings.get().parallaxCompressionLevel); + write(t, gzo); + } + + default Hunk read(Function3> factory, File f) throws IOException + { + return read(factory, new GZIPInputStream(new FileInputStream(f))); + } + + default Hunk read(Function3> factory, ByteArrayTag f) throws IOException + { + return read(factory, new ByteArrayInputStream(f.getValue())); + } + + default ByteArrayTag writeByteArrayTag(Hunk tHunk, String name) throws IOException + { + ByteArrayOutputStream boas = new ByteArrayOutputStream(); + write(tHunk, boas); + return new ByteArrayTag(name, boas.toByteArray()); + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkRegion.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkRegion.java new file mode 100644 index 000000000..414d3ec12 --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkRegion.java @@ -0,0 +1,67 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.gen.v2.scaffold.hunk.Hunk; +import com.volmit.iris.util.CompoundTag; +import com.volmit.iris.util.KMap; +import com.volmit.iris.util.NBTInputStream; +import com.volmit.iris.util.NBTOutputStream; +import lombok.Data; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +@Data +public class HunkRegion +{ + private final File folder; + private CompoundTag compound; + private final int x; + private final int z; + + public HunkRegion(File folder, int x, int z, CompoundTag compound) { + this.compound = compound; + this.folder = folder; + this.x = x; + this.z = z; + folder.mkdirs(); + } + + public HunkRegion(File folder, int x, int z) { + this(folder, x, z, new CompoundTag(x + "." + z, new KMap<>())); + File f = getFile(); + + if(f.exists()) + { + try + { + NBTInputStream in = new NBTInputStream(new FileInputStream(f)); + compound = (CompoundTag) in.readTag(); + in.close(); + } + + catch(Throwable e) + { + + } + } + } + + public File getFile() + { + return new File(folder, x + "." + z + ".dat"); + } + + public void save() throws IOException + { + synchronized (compound) + { + File f = getFile(); + FileOutputStream fos = new FileOutputStream(f); + NBTOutputStream out = new NBTOutputStream(fos); + out.writeTag(compound); + out.close(); + } + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkRegionSlice.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkRegionSlice.java new file mode 100644 index 000000000..7ceea369a --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/HunkRegionSlice.java @@ -0,0 +1,190 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.gen.v2.scaffold.hunk.Hunk; +import com.volmit.iris.manager.IrisDataManager; +import com.volmit.iris.object.IrisBiome; +import com.volmit.iris.object.IrisObject; +import com.volmit.iris.util.*; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; + +import java.io.IOException; +import java.util.function.Function; + +public class HunkRegionSlice { + public static final Function2> BLOCKDATA = (h,c) -> new HunkRegionSlice<>(h,Hunk::newMappedHunk, new BlockDataHunkIOAdapter(), c, "blockdata"); + public static final Function3> STRING = (h,c,t) -> new HunkRegionSlice<>(h,Hunk::newMappedHunk, new StringHunkIOAdapter(), c, t); + public static final Function3> BOOLEAN = (h,c,t) -> new HunkRegionSlice<>(h,Hunk::newMappedHunk, new BooleanHunkIOAdapter(), c, t); + private final Function3> factory; + private final HunkIOAdapter adapter; + private final CompoundTag compound; + private final String key; + private final KMap> loadedChunks; + private final KList save; + private final int height; + + public HunkRegionSlice(int height, Function3> factory, HunkIOAdapter adapter, CompoundTag compound, String key) + { + this.height = height; + this.loadedChunks = new KMap<>(); + this.factory = factory; + this.adapter = adapter; + this.compound = compound; + this.save = new KList<>(); + this.key = key; + } + + public void clear() + { + for(String i : new KList<>(compound.getValue().keySet())) + { + if(i.startsWith(key + ".")) + { + compound.getValue().remove(i); + } + } + } + + public boolean contains(int x, int z) + { + return compound.getValue().containsKey(key(x, z)); + } + + public void delete(int x, int z) + { + compound.getValue().remove(key(x,z)); + } + + public Hunk read(int x, int z) throws IOException + { + Tag t = compound.getValue().get(key(x,z)); + + if(!(t instanceof ByteArrayTag)) + { + return null; + } + + return adapter.read(factory, (ByteArrayTag) t); + } + + public void write(Hunk hunk, int x, int z) throws IOException + { + compound.getValue().put(key(x,z), hunk.writeByteArrayTag(adapter, key)); + } + + public synchronized void close() + { + for(Short i : loadedChunks.k()) + { + unload((byte) (i & 0xFF), (byte) ((i >> 8) & 0xFF)); + } + + save.clear(); + loadedChunks.clear(); + } + + public synchronized void save(Hunk region, int x, int z) + { + try { + write(region, x, z); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public boolean isLoaded(int x, int z) + { + return loadedChunks.containsKey(ikey(x,z)); + } + + public synchronized void save(int x, int z) + { + if(isLoaded(x,z)) + { + save(get(x,z), x, z); + } + } + + public synchronized void unload(int x, int z) + { + short key = ikey(x,z); + + if(isLoaded(x, z)) + { + if(save.contains(key)) + { + save(x,z); + save.remove(key); + } + + loadedChunks.remove(key); + } + } + + public synchronized Hunk load(int x, int z) + { + if(isLoaded(x,z)) + { + return loadedChunks.get(ikey(x,z)); + } + + Hunk v = null; + + if(contains(x ,z)) + { + try { + v = read(x,z); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if(v == null) + { + v = factory.apply(16, height, 16); + } + loadedChunks.put(ikey(x,z), v); + + return v; + } + + public Hunk get(int x, int z) + { + short key = ikey(x,z); + + Hunk c = loadedChunks.get(key); + + if(c == null) + { + c = load(x, z); + } + + return c; + } + + public Hunk getR(int x, int z) + { + return get(x,z).readOnly(); + } + + public Hunk getRW(int x, int z) + { + save.addIfMissing(ikey(x,z)); + return get(x,z); + } + + private short ikey(int x, int z) + { + return ((short)(((x & 0xFF) << 8) | (z & 0xFF))); + } + + private String key(int x, int z) + { + if(x < 0 || x >=32 || z < 0 || z >= 32) + { + throw new IndexOutOfBoundsException("The chunk " + x + " " + z + " is out of bounds max is 31x31"); + } + + return key + "." + Integer.toString(((short)(((x & 0xFF) << 8) | (z & 0xFF))), 36); + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/PaletteHunkIOAdapter.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/PaletteHunkIOAdapter.java new file mode 100644 index 000000000..e212fabd9 --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/PaletteHunkIOAdapter.java @@ -0,0 +1,82 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.gen.v2.scaffold.data.DataPalette; +import com.volmit.iris.gen.v2.scaffold.hunk.Hunk; +import com.volmit.iris.util.Function3; + +import java.io.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class PaletteHunkIOAdapter implements HunkIOAdapter { + @Override + public void write(Hunk t, OutputStream out) throws IOException { + DataOutputStream dos = new DataOutputStream(out); + dos.writeShort(t.getWidth() + Short.MIN_VALUE); + dos.writeShort(t.getHeight() + Short.MIN_VALUE); + dos.writeShort(t.getDepth() + Short.MIN_VALUE); + AtomicInteger nonNull = new AtomicInteger(0); + DataPalette palette = new DataPalette(); + + t.iterate(0, (x,y,z,w) -> { + if(w != null) + { + palette.getIndex(w); + nonNull.getAndAdd(1); + } + }); + + palette.write(this, dos); + dos.writeInt(nonNull.get() + Integer.MIN_VALUE); + AtomicBoolean failure = new AtomicBoolean(false); + t.iterate(0, (x,y,z,w) -> { + if(w != null) + { + try + { + dos.writeShort(x + Short.MIN_VALUE); + dos.writeShort(y + Short.MIN_VALUE); + dos.writeShort(z + Short.MIN_VALUE); + dos.writeShort(palette.getIndex(w) + Short.MIN_VALUE); + } + + catch(Throwable e) + { + e.printStackTrace(); + failure.set(true); + } + } + }); + + dos.close(); + } + + @Override + public Hunk read(Function3> factory, InputStream in) throws IOException { + DataInputStream din = new DataInputStream(in); + int w = din.readShort() - Short.MIN_VALUE; + int h = din.readShort() - Short.MIN_VALUE; + int d = din.readShort() - Short.MIN_VALUE; + DataPalette palette = DataPalette.getPalette(this, din); + int e = din.readInt() - Integer.MIN_VALUE; + Hunk t = factory.apply(w, h, d); + + for(int i = 0; i < e; i++) + { + int x = din.readShort() - Short.MIN_VALUE; + int y = din.readShort() - Short.MIN_VALUE; + int z = din.readShort() - Short.MIN_VALUE; + T v = palette.getPalette().get(din.readShort() - Short.MIN_VALUE); + + if(v == null) + { + throw new IOException("NULL VALUE AT " + x + " " + y + " " + z); + } + + t.setRaw(x,y,z, v); + } + + in.close(); + return t; + } +} diff --git a/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/StringHunkIOAdapter.java b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/StringHunkIOAdapter.java new file mode 100644 index 000000000..2f1054856 --- /dev/null +++ b/src/main/java/com/volmit/iris/gen/v2/scaffold/hunk/io/StringHunkIOAdapter.java @@ -0,0 +1,21 @@ +package com.volmit.iris.gen.v2.scaffold.hunk.io; + +import com.volmit.iris.util.B; +import org.bukkit.block.data.BlockData; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public class StringHunkIOAdapter extends PaletteHunkIOAdapter { + + @Override + public void write(String data, DataOutputStream dos) throws IOException { + dos.writeUTF(data); + } + + @Override + public String read(DataInputStream din) throws IOException { + return din.readUTF(); + } +}