This commit is contained in:
cyberpwn 2021-09-20 11:37:18 -04:00
parent 4489197d01
commit 5c19ae0e94
14 changed files with 317 additions and 408 deletions

View File

@ -26,6 +26,7 @@ import com.volmit.iris.core.link.MythicMobsLink;
import com.volmit.iris.core.link.OraxenLink;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.object.IrisBiome;
import com.volmit.iris.engine.object.IrisBiomeCustom;
@ -450,6 +451,8 @@ public class Iris extends VolmitPlugin implements Listener {
J.a(ServerConfigurator::configure, 20);
splash();
J.sr(() -> Iris.service(PreservationSVC.class).printCaches(), 20);
if (IrisSettings.get().getStudio().isAutoStartDefaultStudio()) {
Iris.info("Starting up auto Studio!");
try {

View File

@ -64,7 +64,11 @@ public class IrisSettings {
@Data
public static class IrisSettingsPerformance {
public int mantleKeepAliveSeconds = 60;
public int cacheSize = 131072;
public int maxStreamCacheSize = 1_000_000;
public int maxResourceLoaderCacheSize = 1_000;
public int maxObjectLoaderCacheSize = 3_000;
public int maxScriptLoaderCacheSize = 500;
public double cacheMemoryPercentage = 0.65;
}
@Data

View File

@ -71,6 +71,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
private static final KMap<File, IrisData> dataLoaders = new KMap<>();
private final File dataFolder;
private final int id;
private boolean closed = false;
private ResourceLoader<IrisBiome> biomeLoader;
private ResourceLoader<IrisLootTable> lootLoader;
private ResourceLoader<IrisRegion> regionLoader;
@ -115,7 +116,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
int m = 0;
for (IrisData i : dataLoaders.values()) {
for (ResourceLoader<?> j : i.getLoaders().values()) {
m += j.getLoadCache().size();
m += j.getLoadCache().getSize();
}
}
@ -123,7 +124,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
private static void printData(ResourceLoader<?> rl) {
Iris.warn(" " + rl.getResourceTypeName() + " @ /" + rl.getFolderName() + ": Cache=" + rl.getLoadCache().size() + " Folders=" + rl.getFolders().size());
Iris.warn(" " + rl.getResourceTypeName() + " @ /" + rl.getFolderName() + ": Cache=" + rl.getLoadCache().getSize() + " Folders=" + rl.getFolders().size());
}
public static IrisObject loadAnyObject(String key) {
@ -274,6 +275,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
}
public void close() {
closed = true;
dump();
}
@ -286,11 +288,11 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
IrisRegistrant rr = registrant.getConstructor().newInstance();
ResourceLoader<T> r = null;
if (registrant.equals(IrisObject.class)) {
r = (ResourceLoader<T>) new ObjectResourceLoader(dataFolder, this, rr.getFolderName(), rr.getTypeName());
} else if (registrant.equals(IrisMatter.class)) {
r = (ResourceLoader<T>) new MatterResourceLoader(dataFolder, this, rr.getFolderName(), rr.getTypeName());
r = (ResourceLoader<T>) new ObjectResourceLoader(dataFolder, this, rr.getFolderName(),
rr.getTypeName());
} else if (registrant.equals(IrisScript.class)) {
r = (ResourceLoader<T>) new ScriptResourceLoader(dataFolder, this, rr.getFolderName(), rr.getTypeName());
r = (ResourceLoader<T>) new ScriptResourceLoader(dataFolder, this, rr.getFolderName(),
rr.getTypeName());
} else {
J.attempt(() -> registrant.getConstructor().newInstance().registerTypeAdapters(builder));
r = new ResourceLoader<>(dataFolder, this, rr.getFolderName(), rr.getTypeName(), registrant);
@ -460,4 +462,8 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
return l;
});
}
public boolean isClosed() {
return closed;
}
}

View File

@ -1,228 +0,0 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.loader;
import com.volmit.iris.Iris;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.matter.IrisMatter;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
public class MatterResourceLoader extends ResourceLoader<IrisMatter> {
private final ChronoLatch useFlip = new ChronoLatch(2222);
private final KMap<String, Long> useCache = new KMap<>();
private final ChronoLatch cl;
private final AtomicInteger unload;
public MatterResourceLoader(File root, IrisData idm, String folderName, String resourceTypeName) {
super(root, idm, folderName, resourceTypeName, IrisMatter.class);
cl = new ChronoLatch(30000);
unload = new AtomicInteger(0);
}
public boolean supportsSchemas() {
return false;
}
public int getSize() {
return loadCache.size();
}
public int getTotalStorage() {
return getSize();
}
public void clean() {
if (useFlip.flip()) {
unloadLast(30000);
}
}
public void unloadLast(long age) {
String v = getOldest();
if (v == null) {
return;
}
if (M.ms() - useCache.get(v) > age) {
unload(v);
}
}
private String getOldest() {
long min = M.ms();
String v = null;
for (String i : useCache.k()) {
long t = useCache.get(i);
if (t < min) {
min = t;
v = i;
}
}
return v;
}
private void unload(String v) {
lock.lock();
useCache.remove(v);
loadCache.remove(v);
lock.unlock();
unload.getAndIncrement();
if (unload.get() == 1) {
cl.flip();
}
if (cl.flip()) {
J.a(() -> {
Iris.verbose("Unloaded " + C.WHITE + unload.get() + " " + resourceTypeName + (unload.get() == 1 ? "" : "s") + C.GRAY + " to optimize memory usage." + " (" + Form.f(getLoadCache().size()) + " " + resourceTypeName + (loadCache.size() == 1 ? "" : "s") + " Loaded)");
unload.set(0);
});
}
}
public IrisMatter loadFile(File j, String key, String name) {
lock.lock();
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
IrisMatter t = (IrisMatter) Matter.read(j);
loadCache.put(key, t);
t.setLoadKey(name);
t.setLoader(manager);
t.setLoadFile(j);
logLoad(j, t);
lock.unlock();
tlt.addAndGet(p.getMilliseconds());
return t;
} catch (Throwable e) {
Iris.reportError(e);
lock.unlock();
Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + ": " + e.getMessage());
return null;
}
}
public String[] getPossibleKeys() {
if (possibleKeys != null) {
return possibleKeys;
}
Iris.debug("Building " + resourceTypeName + " Possibility Lists");
KSet<String> m = new KSet<>();
for (File i : getFolders()) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".matter")) {
m.add(j.getName().replaceAll("\\Q.matter\\E", ""));
} else if (j.isDirectory()) {
for (File k : j.listFiles()) {
if (k.isFile() && k.getName().endsWith(".matter")) {
m.add(j.getName() + "/" + k.getName().replaceAll("\\Q.matter\\E", ""));
} else if (k.isDirectory()) {
for (File l : k.listFiles()) {
if (l.isFile() && l.getName().endsWith(".matter")) {
m.add(j.getName() + "/" + k.getName() + "/" + l.getName().replaceAll("\\Q.matter\\E", ""));
}
}
}
}
}
}
}
KList<String> v = new KList<>(m);
possibleKeys = v.toArray(new String[0]);
return possibleKeys;
}
public File findFile(String name) {
lock.lock();
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".matter") && j.getName().split("\\Q.\\E")[0].equals(name)) {
lock.unlock();
return j;
}
}
File file = new File(i, name + ".matter");
if (file.exists()) {
lock.unlock();
return file;
}
}
Iris.warn("Couldn't find " + resourceTypeName + ": " + name);
lock.unlock();
return null;
}
public IrisMatter load(String name) {
return load(name, true);
}
public IrisMatter load(String name, boolean warn) {
String key = name + "-" + objectClass.getCanonicalName();
if (loadCache.containsKey(key)) {
IrisMatter t = loadCache.get(key);
useCache.put(key, M.ms());
return t;
}
lock.lock();
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".matter") && j.getName().split("\\Q.\\E")[0].equals(name)) {
useCache.put(key, M.ms());
lock.unlock();
return loadFile(j, key, name);
}
}
File file = new File(i, name + ".matter");
if (file.exists()) {
useCache.put(key, M.ms());
lock.unlock();
return loadFile(file, key, name);
}
}
Iris.warn("Couldn't find " + resourceTypeName + ": " + name);
lock.unlock();
return null;
}
}

View File

@ -19,10 +19,12 @@
package com.volmit.iris.core.loader;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.engine.object.IrisObject;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.math.M;
@ -34,94 +36,29 @@ import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
public class ObjectResourceLoader extends ResourceLoader<IrisObject> {
private final ChronoLatch useFlip = new ChronoLatch(2222);
private final KMap<String, Long> useCache = new KMap<>();
private final ChronoLatch cl;
private final AtomicInteger unload;
public ObjectResourceLoader(File root, IrisData idm, String folderName, String resourceTypeName) {
super(root, idm, folderName, resourceTypeName, IrisObject.class);
cl = new ChronoLatch(30000);
unload = new AtomicInteger(0);
loadCache = new KCache<>(this::loadRaw, IrisSettings.get().getPerformance().getMaxObjectLoaderCacheSize());
}
public boolean supportsSchemas() {
return false;
}
public int getSize() {
return loadCache.size();
public long getSize() {
return loadCache.getSize();
}
public int getTotalStorage() {
int m = 0;
for (IrisObject i : loadCache.values()) {
m += i.getBlocks().size();
public long getTotalStorage() {
return getSize();
}
return m;
}
public void clean() {
if (useFlip.flip()) {
unloadLast(30000);
}
}
public void unloadLast(long age) {
String v = getOldest();
if (v == null) {
return;
}
if (M.ms() - useCache.get(v) > age) {
unload(v);
}
}
private String getOldest() {
long min = M.ms();
String v = null;
for (String i : useCache.k()) {
long t = useCache.get(i);
if (t < min) {
min = t;
v = i;
}
}
return v;
}
private void unload(String v) {
lock.lock();
useCache.remove(v);
loadCache.remove(v);
lock.unlock();
unload.getAndIncrement();
if (unload.get() == 1) {
cl.flip();
}
if (cl.flip()) {
J.a(() -> {
Iris.verbose("Unloaded " + C.WHITE + unload.get() + " " + resourceTypeName + (unload.get() == 1 ? "" : "s") + C.GRAY + " to optimize memory usage." + " (" + Form.f(getLoadCache().size()) + " " + resourceTypeName + (loadCache.size() == 1 ? "" : "s") + " Loaded)");
unload.set(0);
});
}
}
public IrisObject loadFile(File j, String key, String name) {
protected IrisObject loadFile(File j, String name) {
lock.lock();
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
IrisObject t = new IrisObject(0, 0, 0);
t.read(j);
loadCache.put(key, t);
t.setLoadKey(name);
t.setLoader(manager);
t.setLoadFile(j);
@ -198,31 +135,21 @@ public class ObjectResourceLoader extends ResourceLoader<IrisObject> {
return load(name, true);
}
public IrisObject load(String name, boolean warn) {
String key = name + "-" + objectClass.getCanonicalName();
if (loadCache.containsKey(key)) {
IrisObject t = loadCache.get(key);
useCache.put(key, M.ms());
return t;
}
private IrisObject loadRaw(String name){
lock.lock();
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".iob") && j.getName().split("\\Q.\\E")[0].equals(name)) {
useCache.put(key, M.ms());
lock.unlock();
return loadFile(j, key, name);
return loadFile(j, name);
}
}
File file = new File(i, name + ".iob");
if (file.exists()) {
useCache.put(key, M.ms());
lock.unlock();
return loadFile(file, key, name);
return loadFile(file, name);
}
}
@ -231,4 +158,8 @@ public class ObjectResourceLoader extends ResourceLoader<IrisObject> {
lock.unlock();
return null;
}
public IrisObject load(String name, boolean warn) {
return loadCache.get(name);
}
}

View File

@ -20,10 +20,14 @@ package com.volmit.iris.core.loader;
import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.project.SchemaBuilder;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.engine.framework.MeteredCache;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
@ -42,13 +46,13 @@ import java.util.function.Predicate;
import java.util.stream.Stream;
@Data
public class ResourceLoader<T extends IrisRegistrant> {
public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
public static final AtomicDouble tlt = new AtomicDouble(0);
private static final int CACHE_SIZE = 100000;
protected File root;
protected String folderName;
protected String resourceTypeName;
protected KMap<String, T> loadCache;
protected KCache<String, T> loadCache;
protected KList<File> folderCache;
protected Class<? extends T> objectClass;
protected String cname;
@ -68,8 +72,9 @@ public class ResourceLoader<T extends IrisRegistrant> {
this.resourceTypeName = resourceTypeName;
this.root = root;
this.folderName = folderName;
loadCache = new KMap<>();
loadCache = new KCache<>(this::loadRaw, IrisSettings.get().getPerformance().getMaxResourceLoaderCacheSize());
Iris.debug("Loader<" + C.GREEN + resourceTypeName + C.LIGHT_PURPLE + "> created in " + C.RED + "IDM/" + manager.getId() + C.LIGHT_PURPLE + " on " + C.GRAY + manager.getDataFolder().getPath());
Iris.service(PreservationSVC.class).registerCache(this);
}
public JSONObject buildSchema() {
@ -122,7 +127,7 @@ public class ResourceLoader<T extends IrisRegistrant> {
if (sec.flip()) {
J.a(() -> {
Iris.verbose("Loaded " + C.WHITE + loads.get() + " " + resourceTypeName + (loads.get() == 1 ? "" : "s") + C.GRAY + " (" + Form.f(getLoadCache().size()) + " " + resourceTypeName + (loadCache.size() == 1 ? "" : "s") + " Loaded)");
Iris.verbose("Loaded " + C.WHITE + loads.get() + " " + resourceTypeName + (loads.get() == 1 ? "" : "s") + C.GRAY + " (" + Form.f(getLoadCache().getSize()) + " " + resourceTypeName + (loadCache.getSize() == 1 ? "" : "s") + " Loaded)");
loads.set(0);
});
}
@ -177,10 +182,10 @@ public class ResourceLoader<T extends IrisRegistrant> {
}
public long count() {
return loadCache.size();
return loadCache.getSize();
}
protected T loadFile(File j, String key, String name) {
protected T loadFile(File j, String name) {
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
T t = getManager().getGson()
@ -189,7 +194,6 @@ public class ResourceLoader<T extends IrisRegistrant> {
t.setLoadFile(j);
t.setLoader(manager);
getManager().preprocessObject(t);
loadCache.put(key, t);
logLoad(j, t);
lock.unlock();
tlt.addAndGet(p.getMilliseconds());
@ -242,6 +246,28 @@ public class ResourceLoader<T extends IrisRegistrant> {
return load(name, true);
}
private T loadRaw(String name)
{
lock.lock();
for (File i : getFolders(name)) {
//noinspection ConstantConditions
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".json") && j.getName().split("\\Q.\\E")[0].equals(name)) {
return loadFile(j, name);
}
}
File file = new File(i, name + ".json");
if (file.exists()) {
return loadFile(file, name);
}
}
lock.unlock();
return null;
}
public T load(String name, boolean warn) {
if (name == null) {
return null;
@ -251,33 +277,7 @@ public class ResourceLoader<T extends IrisRegistrant> {
return null;
}
String key = name + "-" + cname;
if (loadCache.containsKey(key)) {
return loadCache.get(key);
}
lock.lock();
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".json") && j.getName().split("\\Q.\\E")[0].equals(name)) {
return loadFile(j, key, name);
}
}
File file = new File(i, name + ".json");
if (file.exists()) {
return loadFile(file, key, name);
}
}
if (warn && !resourceTypeName.equals("Dimension")) {
J.a(() -> Iris.warn("Couldn't find " + resourceTypeName + ": " + name));
}
lock.unlock();
return null;
return loadCache.get(name);
}
public KList<File> getFolders() {
@ -323,7 +323,7 @@ public class ResourceLoader<T extends IrisRegistrant> {
public void clearCache() {
lock.lock();
possibleKeys = null;
loadCache.clear();
loadCache.invalidate();
folderCache = null;
lock.unlock();
}
@ -347,7 +347,7 @@ public class ResourceLoader<T extends IrisRegistrant> {
}
public boolean isLoaded(String next) {
return loadCache.containsKey(next);
return loadCache.contains(next);
}
public void clearList() {
@ -377,11 +377,21 @@ public class ResourceLoader<T extends IrisRegistrant> {
}
public int getSize() {
return loadCache.size();
public long getSize() {
return loadCache.getSize();
}
public int getTotalStorage() {
@Override
public long getMaxSize() {
return loadCache.getMaxSize();
}
@Override
public boolean isClosed() {
return getManager().isClosed();
}
public long getTotalStorage() {
return getSize();
}
}

View File

@ -19,34 +19,35 @@
package com.volmit.iris.core.loader;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.engine.object.IrisScript;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import java.io.File;
public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
public ScriptResourceLoader(File root, IrisData idm, String folderName, String resourceTypeName) {
super(root, idm, folderName, resourceTypeName, IrisScript.class);
loadCache = new KCache<>(this::loadRaw, IrisSettings.get().getPerformance().getMaxScriptLoaderCacheSize());
}
public boolean supportsSchemas() {
return false;
}
public int getSize() {
return loadCache.size();
public long getSize() {
return loadCache.getSize();
}
public IrisScript loadFile(File j, String key, String name) {
protected IrisScript loadFile(File j, String name) {
lock.lock();
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
IrisScript t = new IrisScript(IO.readAll(j));
loadCache.put(key, t);
t.setLoadKey(name);
t.setLoader(manager);
t.setLoadFile(j);
@ -119,20 +120,14 @@ public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
return null;
}
public IrisScript load(String name, boolean warn) {
String key = name + "-" + objectClass.getCanonicalName();
if (loadCache.containsKey(key)) {
IrisScript t = loadCache.get(key);
return t;
}
private IrisScript loadRaw(String name)
{
lock.lock();
for (File i : getFolders(name)) {
for (File j : i.listFiles()) {
if (j.isFile() && j.getName().endsWith(".js") && j.getName().split("\\Q.\\E")[0].equals(name)) {
lock.unlock();
return loadFile(j, key, name);
return loadFile(j, name);
}
}
@ -140,7 +135,7 @@ public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
if (file.exists()) {
lock.unlock();
return loadFile(file, key, name);
return loadFile(file, name);
}
}
@ -149,4 +144,8 @@ public class ScriptResourceLoader extends ResourceLoader<IrisScript> {
lock.unlock();
return null;
}
public IrisScript load(String name, boolean warn) {
return loadCache.get(name);
}
}

View File

@ -19,19 +19,27 @@
package com.volmit.iris.core.service;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.MeteredCache;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.context.IrisContext;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.scheduling.Looper;
import com.volmit.iris.util.stream.utility.CachedStream2D;
import java.util.Comparator;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
public class PreservationSVC implements IrisService {
private final KList<Thread> threads = new KList<>();
private final KList<ExecutorService> services = new KList<>();
private Looper dereferencer;
private final KList<MeteredCache> caches = new KList<>();
public void register(Thread t) {
threads.add(t);
@ -45,11 +53,35 @@ public class PreservationSVC implements IrisService {
services.add(service);
}
public void printCaches()
{
long s = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getSize).sum();
long m = caches.stream().filter(i -> !i.isClosed()).mapToLong(MeteredCache::getMaxSize).sum();
double p = 0;
double mf = 0;
for(MeteredCache i : caches)
{
if(i.isClosed())
{
continue;
}
mf++;
p+= i.getUsage();
}
mf = mf == 0 ? 1 : mf;
Iris.info("Cached " + Form.f(s) + " / " + Form.f(m) + " (" + Form.pc(p/mf) + ") from " + caches.size() + " Caches");
}
public void dereference() {
IrisContext.dereference();
IrisData.dereference();
threads.removeWhere((i) -> !i.isAlive());
services.removeWhere(ExecutorService::isShutdown);
updateCaches();
}
@Override
@ -94,4 +126,13 @@ public class PreservationSVC implements IrisService {
}
});
}
public void updateCaches()
{
caches.removeWhere(MeteredCache::isClosed);
}
public void registerCache(MeteredCache cache) {
caches.add(cache);
}
}

View File

@ -89,7 +89,7 @@ public class IrisComplex implements DataProvider {
}
public IrisComplex(Engine engine, boolean simple) {
int cacheSize = IrisSettings.get().getPerformance().getCacheSize();
int cacheSize = IrisSettings.get().getPerformance().getMaxStreamCacheSize();
IrisBiome emptyBiome = new IrisBiome();
UUID focusUUID = UUID.nameUUIDFromBytes("focus".getBytes());
this.rng = new RNG(engine.getSeedManager().getComplex());
@ -124,7 +124,7 @@ public class IrisComplex implements DataProvider {
Interpolated.of(a -> 0D, a -> focusRegion))
: regionStyleStream
.selectRarity(engine.getDimension().getRegions(), (i) -> data.getRegionLoader().load(i))
.convertCached((s) -> data.getRegionLoader().load(s)).cache2D(cacheSize);
.convertCached((s) -> data.getRegionLoader().load(s)).cache2D(engine, cacheSize);
regionIDStream = regionIdentityStream.convertCached((i) -> new UUID(Double.doubleToLongBits(i), String.valueOf(i * 38445).hashCode() * 3245556666L));
caveBiomeStream = regionStream.convert((r)
-> engine.getDimension().getCaveBiomeStyle().create(rng.nextParallelRNG(InferredType.CAVE.ordinal()), getData()).stream()
@ -139,7 +139,7 @@ public class IrisComplex implements DataProvider {
return data.getBiomeLoader().load(s)
.setInferredType(InferredType.CAVE);
})
).convertAware2D(ProceduralStream::get).cache2D(cacheSize);
).convertAware2D(ProceduralStream::get).cache2D(engine, cacheSize);
inferredStreams.put(InferredType.CAVE, caveBiomeStream);
landBiomeStream = regionStream.convert((r)
-> engine.getDimension().getLandBiomeStyle().create(rng.nextParallelRNG(InferredType.LAND.ordinal()), getData()).stream()
@ -148,7 +148,7 @@ public class IrisComplex implements DataProvider {
.convertCached((s) -> data.getBiomeLoader().load(s)
.setInferredType(InferredType.LAND))
).convertAware2D(ProceduralStream::get)
.cache2D(cacheSize);
.cache2D(engine, cacheSize);
inferredStreams.put(InferredType.LAND, landBiomeStream);
seaBiomeStream = regionStream.convert((r)
-> engine.getDimension().getSeaBiomeStyle().create(rng.nextParallelRNG(InferredType.SEA.ordinal()), getData()).stream()
@ -157,7 +157,7 @@ public class IrisComplex implements DataProvider {
.convertCached((s) -> data.getBiomeLoader().load(s)
.setInferredType(InferredType.SEA))
).convertAware2D(ProceduralStream::get)
.cache2D(cacheSize);
.cache2D(engine, cacheSize);
inferredStreams.put(InferredType.SEA, seaBiomeStream);
shoreBiomeStream = regionStream.convert((r)
-> engine.getDimension().getShoreBiomeStyle().create(rng.nextParallelRNG(InferredType.SHORE.ordinal()), getData()).stream()
@ -165,60 +165,60 @@ public class IrisComplex implements DataProvider {
.selectRarity(r.getShoreBiomes(), (i) -> data.getBiomeLoader().load(i))
.convertCached((s) -> data.getBiomeLoader().load(s)
.setInferredType(InferredType.SHORE))
).convertAware2D(ProceduralStream::get).cache2D(cacheSize);
).convertAware2D(ProceduralStream::get).cache2D(engine, cacheSize);
inferredStreams.put(InferredType.SHORE, shoreBiomeStream);
bridgeStream = focus != null ? ProceduralStream.of((x, z) -> focus.getInferredType(),
Interpolated.of(a -> 0D, a -> focus.getInferredType())) :
engine.getDimension().getContinentalStyle().create(rng.nextParallelRNG(234234565), getData())
.bake().scale(1D / engine.getDimension().getContinentZoom()).bake().stream()
.convert((v) -> v >= engine.getDimension().getLandChance() ? InferredType.SEA : InferredType.LAND).cache2D(cacheSize);
.convert((v) -> v >= engine.getDimension().getLandChance() ? InferredType.SEA : InferredType.LAND).cache2D(engine, cacheSize);
baseBiomeStream = focus != null ? ProceduralStream.of((x, z) -> focus,
Interpolated.of(a -> 0D, a -> focus)) :
bridgeStream.convertAware2D((t, x, z) -> inferredStreams.get(t).get(x, z))
.convertAware2D(this::implode).cache2D(cacheSize);
.convertAware2D(this::implode).cache2D(engine, cacheSize);
heightStream = ProceduralStream.of((x, z) -> {
IrisBiome b = focus != null ? focus : baseBiomeStream.get(x, z);
return getHeight(engine, b, x, z, engine.getSeedManager().getHeight());
}, Interpolated.DOUBLE).clamp(0, engine.getHeight()).cache2D(cacheSize);
}, Interpolated.DOUBLE).clamp(0, engine.getHeight()).cache2D(engine, cacheSize, true);
roundedHeighteightStream = heightStream.round();
slopeStream = heightStream.slope(3).cache2D(cacheSize);
slopeStream = heightStream.slope(3).cache2D(engine, cacheSize);
trueBiomeStream = focus != null ? ProceduralStream.of((x, y) -> focus, Interpolated.of(a -> 0D,
b -> focus))
.cache2D(cacheSize) : heightStream
.cache2D(engine, cacheSize) : heightStream
.convertAware2D((h, x, z) ->
fixBiomeType(h, baseBiomeStream.get(x, z),
regionStream.get(x, z), x, z, fluidHeight))
.cache2D(cacheSize);
.cache2D(engine, cacheSize);
trueBiomeStream = focus != null ? ProceduralStream.of((x, y) -> focus, Interpolated.of(a -> 0D,
b -> focus))
.cache2D(cacheSize) : heightStream
.cache2D(engine, cacheSize) : heightStream
.convertAware2D((h, x, z) ->
fixBiomeType(h, baseBiomeStream.get(x, z),
regionStream.get(x, z), x, z, fluidHeight))
.cache2D(cacheSize);
trueBiomeDerivativeStream = trueBiomeStream.convert(IrisBiome::getDerivative).cache2D(cacheSize);
heightFluidStream = heightStream.max(fluidHeight).cache2D(cacheSize);
.cache2D(engine, cacheSize);
trueBiomeDerivativeStream = trueBiomeStream.convert(IrisBiome::getDerivative).cache2D(engine, cacheSize);
heightFluidStream = heightStream.max(fluidHeight).cache2D(engine, cacheSize, true);
maxHeightStream = ProceduralStream.ofDouble((x, z) -> height);
terrainSurfaceDecoration = trueBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D(engine, cacheSize);
terrainCeilingDecoration = trueBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.CEILING)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.CEILING)).cache2D(engine, cacheSize);
terrainCaveSurfaceDecoration = caveBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.NONE)).cache2D(engine, cacheSize);
terrainCaveCeilingDecoration = caveBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.CEILING)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.CEILING)).cache2D(engine, cacheSize);
shoreSurfaceDecoration = trueBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SHORE_LINE)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SHORE_LINE)).cache2D(engine, cacheSize);
seaSurfaceDecoration = trueBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_SURFACE)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_SURFACE)).cache2D(engine, cacheSize);
seaFloorDecoration = trueBiomeStream
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_FLOOR)).cache2D(cacheSize);
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, IrisDecorationPart.SEA_FLOOR)).cache2D(engine, cacheSize);
baseBiomeIDStream = trueBiomeStream.convertAware2D((b, x, z) -> {
UUID d = regionIDStream.get(x, z);
return new UUID(b.getLoadKey().hashCode() * 818223L,
d.hashCode());
})
.cache2D(cacheSize);
.cache2D(engine, cacheSize);
//@done
}

View File

@ -0,0 +1,33 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.engine.framework;
public interface MeteredCache
{
long getSize();
long getMaxSize();
default double getUsage()
{
return (double)getSize() / (double)getMaxSize();
}
boolean isClosed();
}

View File

@ -39,6 +39,7 @@ import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterCavern;
import com.volmit.iris.util.matter.slices.MarkerMatter;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import com.volmit.iris.util.stream.utility.CachedStream2D;
import lombok.Data;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;

View File

@ -0,0 +1,82 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.util.data;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.volmit.iris.engine.framework.MeteredCache;
import java.util.function.Function;
public class KCache<K,V> implements MeteredCache {
private long max;
private CacheLoader<? super K, ? extends V> loader;
private LoadingCache<K, V> cache;
public KCache(CacheLoader<K, V> loader, long max)
{
this.max = max;
this.loader = loader;
this.cache = Caffeine
.newBuilder()
.maximumSize(max)
.build((k) -> loader == null ? null : loader.load(k));
}
public void setLoader(CacheLoader<? super K, ? extends V> loader)
{
this.loader = loader;
}
public void invalidate(K k)
{
cache.invalidate(k);
}
public void invalidate()
{
cache.invalidateAll();
cache.cleanUp();
}
public V get(K k)
{
return cache.get(k);
}
@Override
public long getSize() {
return cache.estimatedSize();
}
@Override
public long getMaxSize() {
return max;
}
@Override
public boolean isClosed() {
return false;
}
public boolean contains(K next) {
return cache.getIfPresent(next) != null;
}
}

View File

@ -20,6 +20,7 @@ package com.volmit.iris.util.stream;
import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IRare;
import com.volmit.iris.engine.object.IrisStyledRange;
import com.volmit.iris.util.collection.KList;
@ -287,8 +288,12 @@ public interface ProceduralStream<T> extends ProceduralLayer, Interpolated<T> {
return new To3DStream<T>(this);
}
default ProceduralStream<T> cache2D(int maxSize) {
return new CachedStream2D<T>(this, maxSize);
default ProceduralStream<T> cache2D(Engine engine, int maxSize) {
return cache2D(engine, maxSize, false);
}
default ProceduralStream<T> cache2D(Engine engine, int maxSize, boolean weak) {
return new CachedStream2D<T>(engine, this, maxSize, weak);
}
default ProceduralStream<T> cache3D(int maxSize) {

View File

@ -18,23 +18,30 @@
package com.volmit.iris.util.stream.utility;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.service.PreservationSVC;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.MeteredCache;
import com.volmit.iris.util.data.KCache;
import com.volmit.iris.util.stream.BasicStream;
import com.volmit.iris.util.stream.ProceduralStream;
public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStream<T> {
public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStream<T>, MeteredCache {
private final ProceduralStream<T> stream;
private final ConcurrentLinkedHashMap<Long, T> cache;
private final KCache<Long, T> cache;
private final Engine engine;
public CachedStream2D(ProceduralStream<T> stream, int size) {
public CachedStream2D(Engine engine, ProceduralStream<T> stream, int size, boolean weak) {
super();
this.stream = stream;
cache = new ConcurrentLinkedHashMap.Builder<Long, T>()
.initialCapacity(size)
.maximumWeightedCapacity(size)
.concurrencyLevel(32)
.build();
this.engine = engine;
cache = new KCache<>(k -> stream.get(Cache.keyX(k), Cache.keyZ(k)), size);
Iris.service(PreservationSVC.class).registerCache(this);
}
@Override
@ -49,11 +56,26 @@ public class CachedStream2D<T> extends BasicStream<T> implements ProceduralStrea
@Override
public T get(double x, double z) {
return cache.compute(Cache.key((int) x, (int) z), (k, v) -> v != null ? v : stream.get((int) x, (int) z));
return cache.get(Cache.key((int)x, (int)z));
}
@Override
public T get(double x, double y, double z) {
return stream.get(x, y, z);
}
@Override
public long getSize() {
return cache.getSize();
}
@Override
public long getMaxSize() {
return cache.getMaxSize();
}
@Override
public boolean isClosed() {
return engine.isClosed();
}
}