diff --git a/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java b/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java index 5d8a0268b..048f551db 100644 --- a/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java +++ b/src/main/java/com/dfsek/terra/config/base/ConfigUtil.java @@ -29,6 +29,7 @@ public final class ConfigUtil { public static long dataSave; // Period of population data saving, in ticks. public static boolean masterDisableCaves; public static int biomeSearchRes; + public static int cacheSize; public static void loadConfig(JavaPlugin main) { main.saveDefaultConfig(); @@ -40,6 +41,7 @@ public final class ConfigUtil { dataSave = Duration.parse(Objects.requireNonNull(config.getString("data-save", "PT6M"))).toMillis() / 20L; masterDisableCaves = config.getBoolean("master-disable.caves", false); biomeSearchRes = config.getInt("biome-search-resolution", 4); + cacheSize = config.getInt("cache-size", 512); if(config.getBoolean("dump-default", true)) { try(JarFile jar = new JarFile(new File(Terra.class.getProtectionDomain().getCodeSource().getLocation().toURI()))) { diff --git a/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java b/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java index ed85dfb39..2fde8d449 100644 --- a/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java +++ b/src/main/java/com/dfsek/terra/config/genconfig/CarverConfig.java @@ -15,7 +15,13 @@ import org.polydev.gaea.math.Range; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; public class CarverConfig extends TerraConfig { private final UserDefinedCarver carver; diff --git a/src/main/java/com/dfsek/terra/math/NoiseFunction2.java b/src/main/java/com/dfsek/terra/math/NoiseFunction2.java index 7ceeee80a..b8f37240a 100644 --- a/src/main/java/com/dfsek/terra/math/NoiseFunction2.java +++ b/src/main/java/com/dfsek/terra/math/NoiseFunction2.java @@ -1,12 +1,12 @@ package com.dfsek.terra.math; +import com.dfsek.terra.config.base.ConfigUtil; import com.dfsek.terra.generation.config.NoiseBuilder; +import com.dfsek.terra.util.hash.HashMapDoubleDouble; import org.polydev.gaea.math.FastNoiseLite; import parsii.eval.Expression; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; public class NoiseFunction2 implements NoiseFunction { private final FastNoiseLite gen; @@ -23,7 +23,7 @@ public class NoiseFunction2 implements NoiseFunction { @Override public double eval(List list) { - return cache.get(gen, (int) list.get(0).evaluate(), (int) list.get(1).evaluate()); + return cache.get(gen, list.get(0).evaluate(), list.get(1).evaluate()); } /** @@ -41,20 +41,22 @@ public class NoiseFunction2 implements NoiseFunction { return true; } - private static class Cache extends LinkedHashMap { + private static class Cache extends HashMapDoubleDouble { private static final long serialVersionUID = 8915092734723467010L; + private final int cacheSize = ConfigUtil.cacheSize; - public double get(FastNoiseLite noise, int x, int z) { - long key = (long) x << 32 | z & 0xFFFFFFFFL; - - return computeIfAbsent(key, k -> noise.getNoise(x, z)); + public double get(FastNoiseLite noise, double x, double z) { + double xx = x >= 0 ? x * 2 : x * -2 - 1; + double zz = z >= 0 ? z * 2 : z * -2 - 1; + double key = (xx >= zz) ? (xx * xx + xx + zz) : (zz * zz + xx); + double value = this.get(key); + if (this.size() > cacheSize) { this.clear(); } + return (value == 4.9E-324D ? addAndReturn(noise.getNoise(x, z), key) : value); } - - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - int maxSize = 512; - return size() > maxSize; + private double addAndReturn(double value, double key) { + this.put(key, value); + return value; } } } diff --git a/src/main/java/com/dfsek/terra/util/hash/HashIntrinsic.java b/src/main/java/com/dfsek/terra/util/hash/HashIntrinsic.java new file mode 100644 index 000000000..a5f13986b --- /dev/null +++ b/src/main/java/com/dfsek/terra/util/hash/HashIntrinsic.java @@ -0,0 +1,118 @@ +/* +Copyright 2009 Sandia Corporation. Under the terms of Contract +DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government +retains certain rights in this software. + +BSD Open Source License. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Sandia National Laboratories nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +package com.dfsek.terra.util.hash; + +import java.io.Serializable; + +public abstract class HashIntrinsic implements Serializable { + private static final long serialVersionUID = 8058099372006904458L; + protected static final int DEFAULT_INITIAL_CAPACITY = 16; + protected static final int MAXIMUM_CAPACITY = 1073741824; + protected static final float DEFAULT_LOAD_FACTOR = 0.75F; + protected int size; + protected int threshold; + protected float loadFactor; + protected int capMinus1; + public static final int FLOAT_EXP_BIT_MASK = 2139095040; + public static final int FLOAT_SIGNIF_BIT_MASK = 8388607; + public static final long DOUBLE_EXP_BIT_MASK = 9218868437227405312L; + public static final long DOUBLE_SIGNIF_BIT_MASK = 4503599627370495L; + + protected HashIntrinsic(int initialCapacity, float loadFactor) { + if (initialCapacity <= 0) { + throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); + } else if (!(loadFactor <= 0.0F) && !Float.isNaN(loadFactor)) { + if (initialCapacity > 1073741824) { + initialCapacity = 1073741824; + } + + int capacity; + for(capacity = 1; capacity < initialCapacity; capacity <<= 1) { + } + + this.capMinus1 = capacity - 1; + this.loadFactor = loadFactor; + this.threshold = (int)((float)capacity * loadFactor); + } else { + throw new IllegalArgumentException("Illegal load factor: " + loadFactor); + } + } + + protected static int hashCodeLong(long value) { + return (int)(value ^ value >>> 32); + } + + protected static int hashCodeFloat(float value) { + return floatToIntBits(value); + } + + protected static int hashCodeDouble(double value) { + long bits = doubleToLongBits(value); + return (int)(bits ^ bits >>> 32); + } + + public static int floatToIntBits(float value) { + int result = Float.floatToRawIntBits(value); + if ((result & 2139095040) == 2139095040 && (result & 8388607) != 0) { + result = 2143289344; + } + + return result; + } + + public static long doubleToLongBits(double value) { + long result = Double.doubleToRawLongBits(value); + if ((result & 9218868437227405312L) == 9218868437227405312L && (result & 4503599627370495L) != 0L) { + result = 9221120237041090560L; + } + + return result; + } + + protected static int tableIndex(int hc, int lm1) { + hc ^= hc >>> 20 ^ hc >>> 12; + hc ^= hc >>> 7 ^ hc >>> 4; + return hc & lm1; + } + + public int size() { + return this.size; + } + + public boolean isEmpty() { + return this.size == 0; + } + + public abstract void clear(); +} diff --git a/src/main/java/com/dfsek/terra/util/hash/HashMapDoubleDouble.java b/src/main/java/com/dfsek/terra/util/hash/HashMapDoubleDouble.java new file mode 100644 index 000000000..ca5721612 --- /dev/null +++ b/src/main/java/com/dfsek/terra/util/hash/HashMapDoubleDouble.java @@ -0,0 +1,291 @@ +/* +Copyright 2009 Sandia Corporation. Under the terms of Contract +DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government +retains certain rights in this software. + +BSD Open Source License. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Sandia National Laboratories nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +package com.dfsek.terra.util.hash; + +import java.io.Serializable; +import java.util.NoSuchElementException; + +public class HashMapDoubleDouble extends HashIntrinsic { + private static final long serialVersionUID = 2109458761298324234L; + private HashMapDoubleDouble.Entry[] table; + + public HashMapDoubleDouble(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + this.table = this.createTable(this.capMinus1 + 1); + } + + public HashMapDoubleDouble(int initialCapacity) { + this(initialCapacity, 0.75F); + } + + public HashMapDoubleDouble() { + this(16, 0.75F); + } + + public final boolean contains(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if (e.key == key) { + return true; + } + } + + return false; + } + + public boolean containsValue(double value) { + for(int i = 0; i < this.table.length; ++i) { + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if (value == e.value) { + return true; + } + } + } + + return false; + } + + public double get(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if (key == e.key) { + return e.value; + } + } + + return 4.9E-324D; + } + + public HashMapDoubleDouble.Entry getEntry(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if (key == e.key) { + return e; + } + } + + return null; + } + + public double put(double key, double value) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + + for(HashMapDoubleDouble.Entry e = this.table[i]; e != null; e = e.next) { + if (key == e.key) { + double oldValue = e.value; + e.value = value; + return oldValue; + } + } + + this.addEntry(key, value, i); + return 4.9E-324D; + } + + private void addEntry(double key, double value, int index) { + HashMapDoubleDouble.Entry e = this.table[index]; + this.table[index] = new HashMapDoubleDouble.Entry(key, value, e); + if (this.size++ >= this.threshold) { + this.resize(2 * this.table.length); + } + + } + + public void resize(int newCapacity) { + int oldCapacity = this.table.length; + if (oldCapacity == 1073741824) { + this.threshold = 2147483647; + } else { + HashMapDoubleDouble.Entry[] newTable = this.createTable(newCapacity); + this.capMinus1 = newCapacity - 1; + this.transfer(newTable); + this.table = newTable; + this.threshold = (int)((float)newCapacity * this.loadFactor); + } + } + + private void transfer(HashMapDoubleDouble.Entry[] newTable) { + for(int j = 0; j < this.table.length; ++j) { + HashMapDoubleDouble.Entry e = this.table[j]; + if (e != null) { + this.table[j] = null; + + HashMapDoubleDouble.Entry next; + do { + next = e.next; + int i = tableIndex(hashCodeDouble(e.key), this.capMinus1); + e.next = newTable[i]; + newTable[i] = e; + e = next; + } while(next != null); + } + } + + } + + public final HashMapDoubleDouble.Entry remove(double key) { + int i = tableIndex(hashCodeDouble(key), this.capMinus1); + HashMapDoubleDouble.Entry prev = this.table[i]; + + HashMapDoubleDouble.Entry e; + HashMapDoubleDouble.Entry next; + for(e = prev; e != null; e = next) { + next = e.next; + if (key == e.key) { + --this.size; + if (prev == e) { + this.table[i] = next; + } else { + prev.next = next; + } + + return e; + } + + prev = e; + } + + return e; + } + + public void clear() { + for(int i = 0; i < this.table.length; ++i) { + this.table[i] = null; + } + + this.size = 0; + } + + private HashMapDoubleDouble.Entry[] createTable(int capacity) { + return new HashMapDoubleDouble.Entry[capacity]; + } + + public long memoryEstimate(int ptrsize) { + return (long)ptrsize * (long)(this.capMinus1 + this.size + 1) + (long)(this.size * 64 / 4); + } + + public HashMapDoubleDouble.Iterator iterator() { + return new HashMapDoubleDouble.Iterator(); + } + + public class Iterator { + HashMapDoubleDouble.Entry next; + int index; + HashMapDoubleDouble.Entry current; + + Iterator() { + if (HashMapDoubleDouble.this.size > 0) { + while(this.index < HashMapDoubleDouble.this.table.length && (this.next = HashMapDoubleDouble.this.table[this.index++]) == null) { + } + } + + } + + public final boolean hasNext() { + return this.next != null; + } + + public HashMapDoubleDouble.Entry nextEntry() { + HashMapDoubleDouble.Entry e = this.next; + if (e == null) { + throw new NoSuchElementException(); + } else { + if ((this.next = e.next) == null) { + while(this.index < HashMapDoubleDouble.this.table.length && (this.next = HashMapDoubleDouble.this.table[this.index++]) == null) { + } + } + + this.current = e; + return e; + } + } + + public double next() { + return this.nextEntry().value; + } + + public void remove() { + if (this.current == null) { + throw new IllegalStateException(); + } else { + double k = this.current.key; + this.current = null; + HashMapDoubleDouble.this.remove(k); + } + } + } + + public static class Entry implements Serializable { + private final double key; + private double value; + private HashMapDoubleDouble.Entry next; + private static final long serialVersionUID = 7972173983741231238L; + + public Entry(double key, double val, HashMapDoubleDouble.Entry n) { + this.key = key; + this.value = val; + this.next = n; + } + + public final double getKey() { + return this.key; + } + + public final double getValue() { + return this.value; + } + + public final double setValue(double newValue) { + double oldValue = this.value; + this.value = newValue; + return oldValue; + } + + public final boolean equals(Object o) { + HashMapDoubleDouble.Entry e = (HashMapDoubleDouble.Entry)o; + return this.key == e.key && this.value == e.value; + } + + public final String toString() { + return this.key + " = " + this.value; + } + + public final int hashCode() { + return hashCodeDouble(key) + hashCodeDouble(value); + } + } +} diff --git a/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java b/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java index 7dd5f8052..8e75a0fed 100644 --- a/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java +++ b/src/main/java/com/dfsek/terra/util/structure/RotationUtil.java @@ -13,7 +13,6 @@ import org.bukkit.block.data.Rotatable; import org.bukkit.block.data.type.RedstoneWire; import java.util.EnumMap; -import java.util.HashMap; import java.util.Map; public final class RotationUtil { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 902ae6a91..9d303c647 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -4,5 +4,6 @@ language: "en_us" fail-type: SHUTDOWN dump-default: true biome-search-resolution: 4 +cache-size: 512 master-disable: caves: false \ No newline at end of file