From 412c67b47aa7de86866a389b761bd4d51da01a5c Mon Sep 17 00:00:00 2001 From: Daniel Mills Date: Tue, 3 Aug 2021 21:45:09 -0400 Subject: [PATCH] Objects "Matter" --- .../com/volmit/iris/engine/cache/Cache.java | 12 + .../com/volmit/iris/engine/hunk/Hunk.java | 33 +++ .../iris/engine/hunk/storage/ArrayHunk.java | 3 +- .../volmit/iris/engine/object/IrisMatter.java | 24 ++ .../iris/engine/object/matter/Matter.java | 245 ++++++++++++++++++ .../engine/object/matter/MatterHeader.java | 48 ++++ .../iris/engine/object/matter/MatterHunk.java | 83 ++++++ .../engine/object/matter/MatterSlice.java | 65 +++++ .../com/volmit/iris/util/data/Varint.java | 224 ++++++++++++++++ .../iris/util/function/Consumer4IO.java | 26 ++ 10 files changed, 762 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/volmit/iris/engine/object/IrisMatter.java create mode 100644 src/main/java/com/volmit/iris/engine/object/matter/Matter.java create mode 100644 src/main/java/com/volmit/iris/engine/object/matter/MatterHeader.java create mode 100644 src/main/java/com/volmit/iris/engine/object/matter/MatterHunk.java create mode 100644 src/main/java/com/volmit/iris/engine/object/matter/MatterSlice.java create mode 100644 src/main/java/com/volmit/iris/util/data/Varint.java create mode 100644 src/main/java/com/volmit/iris/util/function/Consumer4IO.java diff --git a/src/main/java/com/volmit/iris/engine/cache/Cache.java b/src/main/java/com/volmit/iris/engine/cache/Cache.java index b8218da7f..da7b7c3cb 100644 --- a/src/main/java/com/volmit/iris/engine/cache/Cache.java +++ b/src/main/java/com/volmit/iris/engine/cache/Cache.java @@ -40,4 +40,16 @@ public interface Cache { static int keyZ(long key) { return (int) key; } + + static int to1D(int x, int y, int z, int w, int h) { + return (z * w * h) + (y * w) + x; + } + + static int[] to3D(int idx, int w, int h) { + final int z = idx / (w * h); + idx -= (z * w * h); + final int y = idx / w; + final int x = idx % w; + return new int[]{x, y, z}; + } } diff --git a/src/main/java/com/volmit/iris/engine/hunk/Hunk.java b/src/main/java/com/volmit/iris/engine/hunk/Hunk.java index 57f63d4eb..abd29d514 100644 --- a/src/main/java/com/volmit/iris/engine/hunk/Hunk.java +++ b/src/main/java/com/volmit/iris/engine/hunk/Hunk.java @@ -29,6 +29,7 @@ import com.volmit.iris.engine.parallel.MultiBurst; import com.volmit.iris.engine.stream.interpolation.Interpolated; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.function.*; +import com.volmit.iris.util.math.BlockPosition; import com.volmit.iris.util.oldnbt.ByteArrayTag; import org.bukkit.Chunk; import org.bukkit.block.Biome; @@ -557,6 +558,18 @@ public interface Hunk { return this; } + default Hunk iterateSyncIO(Consumer4IO c) throws IOException { + for (int i = 0; i < getWidth(); i++) { + for (int j = 0; j < getHeight(); j++) { + for (int k = 0; k < getDepth(); k++) { + c.accept(i, j, k, get(i, j, k)); + } + } + } + + return this; + } + default Hunk iterate(int parallelism, Consumer3 c) { compute3D(parallelism, (x, y, z, h) -> { @@ -971,6 +984,26 @@ public interface Hunk { return getRaw(x >= getWidth() ? getWidth() - 1 : x < 0 ? 0 : x, y >= getHeight() ? getHeight() - 1 : y < 0 ? 0 : y, z >= getDepth() ? getDepth() - 1 : z < 0 ? 0 : z); } + default BlockPosition getCenter() + { + return new BlockPosition(getCenterX(), getCenterY(), getCenterZ()); + } + + default int getCenterX() + { + return Math.round(getWidth() / 2); + } + + default int getCenterY() + { + return Math.round(getHeight() / 2); + } + + default int getCenterZ() + { + return Math.round(getDepth() / 2); + } + default void fill(T t) { set(0, 0, 0, getWidth() - 1, getHeight() - 1, getDepth() - 1, t); } diff --git a/src/main/java/com/volmit/iris/engine/hunk/storage/ArrayHunk.java b/src/main/java/com/volmit/iris/engine/hunk/storage/ArrayHunk.java index 8e93c481a..127539811 100644 --- a/src/main/java/com/volmit/iris/engine/hunk/storage/ArrayHunk.java +++ b/src/main/java/com/volmit/iris/engine/hunk/storage/ArrayHunk.java @@ -18,6 +18,7 @@ package com.volmit.iris.engine.hunk.storage; +import com.volmit.iris.engine.cache.Cache; import com.volmit.iris.engine.hunk.Hunk; import lombok.Data; import lombok.EqualsAndHashCode; @@ -47,7 +48,7 @@ public class ArrayHunk extends StorageHunk implements Hunk { } private int index(int x, int y, int z) { - return (z * getWidth() * getHeight()) + (y * getWidth()) + x; + return Cache.to1D(x, y, z, getWidth(), getHeight()); } @Override diff --git a/src/main/java/com/volmit/iris/engine/object/IrisMatter.java b/src/main/java/com/volmit/iris/engine/object/IrisMatter.java new file mode 100644 index 000000000..172432e2d --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/object/IrisMatter.java @@ -0,0 +1,24 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine.object; + +// Because Red Matter is too weak +public class IrisMatter { + +} diff --git a/src/main/java/com/volmit/iris/engine/object/matter/Matter.java b/src/main/java/com/volmit/iris/engine/object/matter/Matter.java new file mode 100644 index 000000000..3842aff72 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/object/matter/Matter.java @@ -0,0 +1,245 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine.object.matter; + +import com.volmit.iris.util.data.Varint; +import com.volmit.iris.util.math.BlockPosition; + +import java.io.*; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +/** + * When Red Matter isn't enough + * + * UVI width + * UVI height + * UVI depth + * UVI sliceCount + * UTF author + * UVL createdAt + * UVI version + * UTF sliceType (canonical class name) + * UVI nodeCount (for each slice) + * UVI position [(z * w * h) + (y * w) + x] + * ??? nodeData + * + */ +public interface Matter { + int VERSION = 1; + + /** + * Get the header information + * @return the header info + */ + MatterHeader getHeader(); + + /** + * Get the width of this matter + * @return the width + */ + int getWidth(); + + /** + * Get the height of this matter + * @return the height + */ + int getHeight(); + + /** + * Get the depth of this matter + * @return the depth + */ + int getDepth(); + + /** + * Get the center of this matter + * @return the center + */ + default BlockPosition getCenter() + { + return new BlockPosition(getCenterX(), getCenterY(), getCenterZ()); + } + + /** + * Create a slice from the given type + * @param type the type class + * @param matter the matter this slice will go into (size provider) + * @param the type + * @return the slice (or null if not supported) + */ + MatterSlice createSlice(Class type, Matter matter); + + /** + * Get the size of this matter + * @return the size + */ + default BlockPosition getSize() + { + return new BlockPosition(getWidth(), getHeight(), getDepth()); + } + + /** + * Get the center X of this matter + * @return the center X + */ + default int getCenterX() + { + return Math.round(getWidth() / 2); + } + + /** + * Get the center Y of this matter + * @return the center Y + */ + default int getCenterY() + { + return Math.round(getHeight() / 2); + } + + /** + * Get the center Z of this matter + * @return the center Z + */ + default int getCenterZ() + { + return Math.round(getDepth() / 2); + } + + /** + * Return the slice for the given type + * @param t the type class + * @param the type + * @return the slice or null + */ + default MatterSlice getSlice(Class t) + { + return (MatterSlice) getSliceMap().get(t); + } + + /** + * Delete the slice for the given type + * @param c the type class + * @param the type + * @return the deleted slice, or null if it diddn't exist + */ + default MatterSlice deleteSlice(Class c) + { + return (MatterSlice) getSliceMap().remove(c); + } + + /** + * Put a given slice type + * @param c the slice type class + * @param slice the slice to assign to the type + * @param the slice type + * @return the overwritten slice if there was an existing slice of that type + */ + default MatterSlice putSlice(Class c, MatterSlice slice) + { + return (MatterSlice) getSliceMap().put(c, slice); + } + + /** + * Check if a slice exists for a given type + * @param c the slice class type + * @return true if it exists + */ + default boolean hasSlice(Class c) + { + return getSlice(c) != null; + } + + /** + * Remove all slices + */ + default void clearSlices() + { + getSliceMap().clear(); + } + + /** + * Get the set backing the slice map keys (slice types) + * @return the slice types + */ + default Set> getSliceTypes() + { + return getSliceMap().keySet(); + } + + /** + * Get all slices + * @return the real slice map + */ + Map, MatterSlice> getSliceMap(); + + /** + * Writes the data to the output stream. The data will be flushed to the provided output + * stream however the provided stream will NOT BE CLOSED, so be sure to actually close it + * @param out the output stream + * @throws IOException shit happens yo + */ + default void write(OutputStream out) throws IOException + { + DataOutputStream dos = new DataOutputStream(out); + // Write size + Varint.writeUnsignedVarInt(getWidth(), dos); + Varint.writeUnsignedVarInt(getHeight(), dos); + Varint.writeUnsignedVarInt(getDepth(), dos); + dos.writeByte(getSliceTypes().size() + Byte.MIN_VALUE); + getHeader().write(dos); + + for(Class i : getSliceTypes()) + { + getSlice(i).write(dos); + } + + dos.flush(); + } + + /** + * Reads the input stream into a matter object using a matter factory. + * Does not close the input stream. Be a man, close it yourself. + * @param in the input stream + * @param matterFactory the matter factory (size) -> new MatterImpl(size); + * @return the matter object + * @throws IOException shit happens yo + */ + static Matter read(InputStream in, Function matterFactory) throws IOException, ClassNotFoundException { + DataInputStream din = new DataInputStream(in); + // Read size into new matter object + Matter matter = matterFactory.apply(new BlockPosition( + Varint.readUnsignedVarInt(din), + Varint.readUnsignedVarInt(din), + Varint.readUnsignedVarInt(din))); + int sliceCount = din.readByte() - Byte.MIN_VALUE; + matter.getHeader().read(din); + + while(sliceCount-- > 0) + { + Class type = Class.forName(din.readUTF()); + MatterSlice slice = matter.createSlice(type, matter); + slice.read(din); + matter.putSlice(type, slice); + } + + return matter; + } +} diff --git a/src/main/java/com/volmit/iris/engine/object/matter/MatterHeader.java b/src/main/java/com/volmit/iris/engine/object/matter/MatterHeader.java new file mode 100644 index 000000000..9b049e950 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/object/matter/MatterHeader.java @@ -0,0 +1,48 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine.object.matter; + +import com.volmit.iris.util.data.Varint; +import com.volmit.iris.util.math.M; +import lombok.Data; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +@Data +public class MatterHeader { + private String author = "anonymous"; + private long createdAt = M.ms(); + private int version = Matter.VERSION; + + public void write(DataOutputStream out) throws IOException + { + out.writeUTF(author); + Varint.writeUnsignedVarLong(createdAt, out); + Varint.writeUnsignedVarInt(version, out); + } + + public void read(DataInputStream din) throws IOException + { + setAuthor(din.readUTF()); + setCreatedAt(Varint.readUnsignedVarLong(din)); + setVersion(Varint.readUnsignedVarInt(din)); + } +} diff --git a/src/main/java/com/volmit/iris/engine/object/matter/MatterHunk.java b/src/main/java/com/volmit/iris/engine/object/matter/MatterHunk.java new file mode 100644 index 000000000..8d74b5420 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/object/matter/MatterHunk.java @@ -0,0 +1,83 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine.object.matter; + +import com.volmit.iris.engine.hunk.Hunk; +import com.volmit.iris.engine.hunk.storage.StorageHunk; +import com.volmit.iris.util.collection.KMap; +import com.volmit.iris.util.function.Consumer4; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Map; + +@SuppressWarnings({"DefaultAnnotationParam", "Lombok"}) +@Data +@EqualsAndHashCode(callSuper = false) +public class MatterHunk extends StorageHunk implements Hunk { + private final Map data; + + public MatterHunk(int w, int h, int d) { + super(w, h, d); + data = new KMap<>(); + } + + public int getCount() + { + return data.size(); + } + + @Override + public void setRaw(int x, int y, int z, T t) { + if (t == null) { + data.remove(index(x, y, z)); + return; + } + + data.put(index(x, y, z), t); + } + + private Integer index(int x, int y, int z) { + return (z * getWidth() * getHeight()) + (y * getWidth()) + x; + } + + @Override + public synchronized Hunk iterateSync(Consumer4 c) { + int idx, z; + + for (Map.Entry g : data.entrySet()) { + idx = g.getKey(); + z = idx / (getWidth() * getHeight()); + idx -= (z * getWidth() * getHeight()); + c.accept(idx % getWidth(), idx / getWidth(), z, g.getValue()); + } + + return this; + } + + @Override + public void empty(T b) { + data.clear(); + } + + @Override + public T getRaw(int x, int y, int z) { + return data.get(index(x, y, z)); + } +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/engine/object/matter/MatterSlice.java b/src/main/java/com/volmit/iris/engine/object/matter/MatterSlice.java new file mode 100644 index 000000000..e8841eae8 --- /dev/null +++ b/src/main/java/com/volmit/iris/engine/object/matter/MatterSlice.java @@ -0,0 +1,65 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.engine.object.matter; + +import com.volmit.iris.engine.cache.Cache; +import com.volmit.iris.engine.hunk.Hunk; +import com.volmit.iris.util.data.Varint; + +import java.io.DataInput; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public interface MatterSlice extends Hunk +{ + Class getType(); + + void writeNode(T b, DataOutputStream dos) throws IOException; + + T readNode(DataInputStream din) throws IOException; + + default void write(DataOutputStream dos) throws IOException + { + int w = getWidth(); + int h = getHeight(); + MatterHunk mapped = (MatterHunk) this; + dos.writeUTF(getType().getCanonicalName()); + Varint.writeUnsignedVarInt(mapped.getCount(), dos); + iterateSyncIO((x,y,z,b) -> { + Varint.writeUnsignedVarInt((z * w * h) + (y * w) + x, dos); + writeNode(b, dos); + }); + } + + default void read(DataInputStream din) throws IOException + { + int nodes = Varint.readUnsignedVarInt(din); + int w = getWidth(); + int h = getHeight(); + int d = getDepth(); + int[] pos; + + while(nodes-- > 0) + { + pos = Cache.to3D(Varint.readUnsignedVarInt(din), w, h); + setRaw(pos[0], pos[1], pos[2], readNode(din)); + } + } +} diff --git a/src/main/java/com/volmit/iris/util/data/Varint.java b/src/main/java/com/volmit/iris/util/data/Varint.java new file mode 100644 index 000000000..8b9aaa6ae --- /dev/null +++ b/src/main/java/com/volmit/iris/util/data/Varint.java @@ -0,0 +1,224 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.data; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +/** + *

Encodes signed and unsigned values using a common variable-length + * scheme, found for example in + * + * Google's Protocol Buffers. It uses fewer bytes to encode smaller values, + * but will use slightly more bytes to encode large values.

+ *

+ *

Signed values are further encoded using so-called zig-zag encoding + * in order to make them "compatible" with variable-length encoding.

+ */ +public final class Varint { + + private Varint() { + } + + /** + * Encodes a value using the variable-length encoding from + * + * Google Protocol Buffers. It uses zig-zag encoding to efficiently + * encode signed values. If values are known to be nonnegative, + * {@link #writeUnsignedVarLong(long, DataOutput)} should be used. + * + * @param value value to encode + * @param out to write bytes to + * @throws IOException if {@link DataOutput} throws {@link IOException} + */ + public static void writeSignedVarLong(long value, DataOutput out) throws IOException { + // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types + writeUnsignedVarLong((value << 1) ^ (value >> 63), out); + } + + /** + * Encodes a value using the variable-length encoding from + * + * Google Protocol Buffers. Zig-zag is not used, so input must not be negative. + * If values can be negative, use {@link #writeSignedVarLong(long, DataOutput)} + * instead. This method treats negative input as like a large unsigned value. + * + * @param value value to encode + * @param out to write bytes to + * @throws IOException if {@link DataOutput} throws {@link IOException} + */ + public static void writeUnsignedVarLong(long value, DataOutput out) throws IOException { + while ((value & 0xFFFFFFFFFFFFFF80L) != 0L) { + out.writeByte(((int) value & 0x7F) | 0x80); + value >>>= 7; + } + out.writeByte((int) value & 0x7F); + } + + /** + * @see #writeSignedVarLong(long, DataOutput) + */ + public static void writeSignedVarInt(int value, DataOutput out) throws IOException { + // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types + writeUnsignedVarInt((value << 1) ^ (value >> 31), out); + } + + /** + * @see #writeUnsignedVarLong(long, DataOutput) + */ + public static void writeUnsignedVarInt(int value, DataOutput out) throws IOException { + while ((value & 0xFFFFFF80) != 0L) { + out.writeByte((value & 0x7F) | 0x80); + value >>>= 7; + } + out.writeByte(value & 0x7F); + } + + public static byte[] writeSignedVarInt(int value) { + // Great trick from http://code.google.com/apis/protocolbuffers/docs/encoding.html#types + return writeUnsignedVarInt((value << 1) ^ (value >> 31)); + } + + /** + * @see #writeUnsignedVarLong(long, DataOutput) + *

+ * This one does not use streams and is much faster. + * Makes a single object each time, and that object is a primitive array. + */ + public static byte[] writeUnsignedVarInt(int value) { + byte[] byteArrayList = new byte[10]; + int i = 0; + while ((value & 0xFFFFFF80) != 0L) { + byteArrayList[i++] = ((byte) ((value & 0x7F) | 0x80)); + value >>>= 7; + } + byteArrayList[i] = ((byte) (value & 0x7F)); + byte[] out = new byte[i + 1]; + for (; i >= 0; i--) { + out[i] = byteArrayList[i]; + } + return out; + } + + /** + * @param in to read bytes from + * @return decode value + * @throws IOException if {@link DataInput} throws {@link IOException} + * @throws IllegalArgumentException if variable-length value does not terminate + * after 9 bytes have been read + * @see #writeSignedVarLong(long, DataOutput) + */ + public static long readSignedVarLong(DataInput in) throws IOException { + long raw = readUnsignedVarLong(in); + // This undoes the trick in writeSignedVarLong() + long temp = (((raw << 63) >> 63) ^ raw) >> 1; + // This extra step lets us deal with the largest signed values by treating + // negative results from read unsigned methods as like unsigned values + // Must re-flip the top bit if the original read value had it set. + return temp ^ (raw & (1L << 63)); + } + + /** + * @param in to read bytes from + * @return decode value + * @throws IOException if {@link DataInput} throws {@link IOException} + * @throws IllegalArgumentException if variable-length value does not terminate + * after 9 bytes have been read + * @see #writeUnsignedVarLong(long, DataOutput) + */ + public static long readUnsignedVarLong(DataInput in) throws IOException { + long value = 0L; + int i = 0; + long b; + while (((b = in.readByte()) & 0x80L) != 0) { + value |= (b & 0x7F) << i; + i += 7; + if (i > 63) { + throw new IllegalArgumentException("Variable length quantity is too long"); + } + } + return value | (b << i); + } + + /** + * @throws IllegalArgumentException if variable-length value does not terminate + * after 5 bytes have been read + * @throws IOException if {@link DataInput} throws {@link IOException} + * @see #readSignedVarLong(DataInput) + */ + public static int readSignedVarInt(DataInput in) throws IOException { + int raw = readUnsignedVarInt(in); + // This undoes the trick in writeSignedVarInt() + int temp = (((raw << 31) >> 31) ^ raw) >> 1; + // This extra step lets us deal with the largest signed values by treating + // negative results from read unsigned methods as like unsigned values. + // Must re-flip the top bit if the original read value had it set. + return temp ^ (raw & (1 << 31)); + } + + /** + * @throws IllegalArgumentException if variable-length value does not terminate + * after 5 bytes have been read + * @throws IOException if {@link DataInput} throws {@link IOException} + * @see #readUnsignedVarLong(DataInput) + */ + public static int readUnsignedVarInt(DataInput in) throws IOException { + int value = 0; + int i = 0; + int b; + while (((b = in.readByte()) & 0x80) != 0) { + value |= (b & 0x7F) << i; + i += 7; + if (i > 35) { + throw new IllegalArgumentException("Variable length quantity is too long"); + } + } + return value | (b << i); + } + + public static int readSignedVarInt(byte[] bytes) { + int raw = readUnsignedVarInt(bytes); + // This undoes the trick in writeSignedVarInt() + int temp = (((raw << 31) >> 31) ^ raw) >> 1; + // This extra step lets us deal with the largest signed values by treating + // negative results from read unsigned methods as like unsigned values. + // Must re-flip the top bit if the original read value had it set. + return temp ^ (raw & (1 << 31)); + } + + public static int readUnsignedVarInt(byte[] bytes) { + int value = 0; + int i = 0; + byte rb = Byte.MIN_VALUE; + for (byte b : bytes) { + rb = b; + if ((b & 0x80) == 0) { + break; + } + value |= (b & 0x7f) << i; + i += 7; + if (i > 35) { + throw new IllegalArgumentException("Variable length quantity is too long"); + } + } + return value | (rb << i); + } + +} \ No newline at end of file diff --git a/src/main/java/com/volmit/iris/util/function/Consumer4IO.java b/src/main/java/com/volmit/iris/util/function/Consumer4IO.java new file mode 100644 index 000000000..b44d01e04 --- /dev/null +++ b/src/main/java/com/volmit/iris/util/function/Consumer4IO.java @@ -0,0 +1,26 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2021 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.util.function; + +import java.io.IOException; + +@FunctionalInterface +public interface Consumer4IO { + void accept(A a, B b, C c, D d) throws IOException; +}