Merge pull request #1198 from VolmitSoftware/feat/versioned_mantle

This commit is contained in:
Julian Krings 2025-06-02 17:28:58 +02:00 committed by GitHub
commit 2436ebb857
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 367 additions and 428 deletions

View File

@ -113,6 +113,11 @@ shadowJar {
relocate 'net.kyori', 'com.volmit.iris.util.kyori' relocate 'net.kyori', 'com.volmit.iris.util.kyori'
relocate 'org.bstats', 'com.volmit.util.metrics' relocate 'org.bstats', 'com.volmit.util.metrics'
archiveFileName.set("Iris-${project.version}.jar") archiveFileName.set("Iris-${project.version}.jar")
dependencies {
exclude(dependency("org.ow2.asm:asm:"))
exclude(dependency("org.jetbrains:"))
}
} }
dependencies { dependencies {

View File

@ -23,6 +23,7 @@ import com.volmit.iris.Iris;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONException;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@ -160,6 +161,14 @@ public class IrisSettings {
public int resourceLoaderCacheSize = 1_024; public int resourceLoaderCacheSize = 1_024;
public int objectLoaderCacheSize = 4_096; public int objectLoaderCacheSize = 4_096;
public int scriptLoaderCacheSize = 512; public int scriptLoaderCacheSize = 512;
public int tectonicPlateSize = -1;
public int getTectonicPlateSize() {
if (tectonicPlateSize > 0)
return tectonicPlateSize;
return (int) (getHardware.getProcessMemory() / 200L);
}
} }
@Data @Data
@ -191,6 +200,7 @@ public class IrisSettings {
public boolean DoomsdayAnnihilationSelfDestructMode = false; public boolean DoomsdayAnnihilationSelfDestructMode = false;
public boolean commandSounds = true; public boolean commandSounds = true;
public boolean debug = false; public boolean debug = false;
public boolean dumpMantleOnError = false;
public boolean disableNMS = false; public boolean disableNMS = false;
public boolean pluginMetrics = true; public boolean pluginMetrics = true;
public boolean splashLogoStartup = true; public boolean splashLogoStartup = true;

View File

@ -20,7 +20,6 @@ package com.volmit.iris.core.commands;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.ServerConfigurator;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.datapack.DataVersion; import com.volmit.iris.core.nms.datapack.DataVersion;
import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisPackBenchmarking; import com.volmit.iris.core.tools.IrisPackBenchmarking;
@ -68,53 +67,8 @@ public class CommandDeveloper implements DecreeExecutor {
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
public void EngineStatus() { public void EngineStatus() {
List<World> IrisWorlds = new ArrayList<>(); Iris.service(IrisEngineSVC.class)
int TotalLoadedChunks = 0; .engineStatus(sender());
int TotalQueuedTectonicPlates = 0;
int TotalNotQueuedTectonicPlates = 0;
int TotalTectonicPlates = 0;
long lowestUnloadDuration = 0;
long highestUnloadDuration = 0;
for (World world : Bukkit.getWorlds()) {
try {
if (IrisToolbelt.access(world).getEngine() != null) {
IrisWorlds.add(world);
}
} catch (Exception e) {
// no
}
}
for (World world : IrisWorlds) {
Engine engine = IrisToolbelt.access(world).getEngine();
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
}
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
}
for (Chunk chunk : world.getLoadedChunks()) {
if (chunk.isLoaded()) {
TotalLoadedChunks++;
}
}
}
Iris.info("-------------------------");
Iris.info(C.DARK_PURPLE + "Engine Status");
Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks);
Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit());
Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates);
Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates);
Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates);
Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration));
Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration));
Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
Iris.info("-------------------------");
} }
@Decree(description = "Test") @Decree(description = "Test")
@ -166,7 +120,7 @@ public class CommandDeveloper implements DecreeExecutor {
File tectonicplates = new File(folder, "mantle"); File tectonicplates = new File(folder, "mantle");
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) { for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
TectonicPlate.read(maxHeight, i); TectonicPlate.read(maxHeight, i, true);
c++; c++;
Iris.info("Loaded count: " + c ); Iris.info("Loaded count: " + c );
@ -272,7 +226,8 @@ public class CommandDeveloper implements DecreeExecutor {
@Param(description = "base IrisWorld") World world, @Param(description = "base IrisWorld") World world,
@Param(description = "raw TectonicPlate File") String path, @Param(description = "raw TectonicPlate File") String path,
@Param(description = "Algorithm to Test") String algorithm, @Param(description = "Algorithm to Test") String algorithm,
@Param(description = "Amount of Tests") int amount) { @Param(description = "Amount of Tests") int amount,
@Param(description = "Is versioned", defaultValue = "false") boolean versioned) {
if (!IrisToolbelt.isIrisWorld(world)) { if (!IrisToolbelt.isIrisWorld(world)) {
sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList()));
return; return;
@ -289,7 +244,7 @@ public class CommandDeveloper implements DecreeExecutor {
service.submit(() -> { service.submit(() -> {
try { try {
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file)); CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
TectonicPlate plate = new TectonicPlate(height, raw); TectonicPlate plate = new TectonicPlate(height, raw, versioned);
raw.close(); raw.close();
double d1 = 0; double d1 = 0;
@ -308,7 +263,7 @@ public class CommandDeveloper implements DecreeExecutor {
size = tmp.length(); size = tmp.length();
start = System.currentTimeMillis(); start = System.currentTimeMillis();
CountingDataInputStream din = createInput(tmp, algorithm); CountingDataInputStream din = createInput(tmp, algorithm);
new TectonicPlate(height, din); new TectonicPlate(height, din, true);
din.close(); din.close();
d2 += System.currentTimeMillis() - start; d2 += System.currentTimeMillis() - start;
tmp.delete(); tmp.delete();

View File

@ -1,317 +1,245 @@
package com.volmit.iris.core.service; package com.volmit.iris.core.service;
import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.plugin.IrisService; import com.volmit.iris.util.plugin.IrisService;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.plugin.VolmitSender;
import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.Looper;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.ServerLoadEvent;
import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.event.world.WorldUnloadEvent;
import org.checkerframework.checker.units.qual.A;
import java.util.*; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Supplier; import java.util.function.Supplier;
public class IrisEngineSVC implements IrisService { public class IrisEngineSVC implements IrisService {
public static IrisEngineSVC instance; private final AtomicInteger tectonicLimit = new AtomicInteger(30);
public boolean isServerShuttingDown = false; private final AtomicInteger tectonicPlates = new AtomicInteger();
public boolean isServerLoaded = false; private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
private static final AtomicInteger tectonicLimit = new AtomicInteger(30); private final AtomicDouble maxIdleDuration = new AtomicDouble();
private ReentrantLock lastUseLock; private final AtomicDouble minIdleDuration = new AtomicDouble();
private KMap<World, Long> lastUse; private final AtomicLong loadedChunks = new AtomicLong();
private List<World> IrisWorlds; private final List<Pair<World, PlatformChunkGenerator>> worlds = new CopyOnWriteArrayList<>();
private Looper cacheTicker;
private Looper trimTicker; private Looper trimTicker;
private Looper unloadTicker; private Looper unloadTicker;
private Looper updateTicker; private Looper updateTicker;
private PrecisionStopwatch trimAlive;
private PrecisionStopwatch unloadAlive;
public PrecisionStopwatch trimActiveAlive;
public PrecisionStopwatch unloadActiveAlive;
private AtomicInteger TotalTectonicPlates;
private AtomicInteger TotalQueuedTectonicPlates;
private AtomicInteger TotalNotQueuedTectonicPlates;
private AtomicBoolean IsUnloadAlive;
private AtomicBoolean IsTrimAlive;
ChronoLatch cl;
public List<World> corruptedIrisWorlds = new ArrayList<>();
@Override @Override
public void onEnable() { public void onEnable() {
this.cl = new ChronoLatch(5000); tectonicLimit.set(IrisSettings.get().getPerformance().getTectonicPlateSize());
lastUse = new KMap<>(); for (World world : Bukkit.getWorlds()) {
lastUseLock = new ReentrantLock(); var access = IrisToolbelt.access(world);
IrisWorlds = new ArrayList<>(); if (access == null) return;
IsUnloadAlive = new AtomicBoolean(true); worlds.add(new Pair<>(world, access));
IsTrimAlive = new AtomicBoolean(true);
trimActiveAlive = new PrecisionStopwatch();
unloadActiveAlive = new PrecisionStopwatch();
trimAlive = new PrecisionStopwatch();
unloadAlive = new PrecisionStopwatch();
TotalTectonicPlates = new AtomicInteger();
TotalQueuedTectonicPlates = new AtomicInteger();
TotalNotQueuedTectonicPlates = new AtomicInteger();
tectonicLimit.set(2);
long t = getHardware.getProcessMemory();
while (t > 200) {
tectonicLimit.getAndAdd(1);
t = t - 200;
} }
this.setup();
this.TrimLogic();
this.UnloadLogic();
trimAlive.begin();
unloadAlive.begin();
trimActiveAlive.begin();
unloadActiveAlive.begin();
updateTicker.start();
cacheTicker.start();
//trimTicker.start();
//unloadTicker.start();
instance = this;
trimLogic();
unloadLogic();
setup();
} }
public void engineStatus() { @Override
boolean trimAlive = trimTicker.isAlive(); public void onDisable() {
boolean unloadAlive = unloadTicker.isAlive(); updateTicker.interrupt();
Iris.info("Status:"); trimTicker.interrupt();
Iris.info("- Trim: " + trimAlive); unloadTicker.interrupt();
Iris.info("- Unload: " + unloadAlive); worlds.clear();
} }
public static int getTectonicLimit() { public void engineStatus(VolmitSender sender) {
return tectonicLimit.get(); sender.sendMessage(C.DARK_PURPLE + "-------------------------");
sender.sendMessage(C.DARK_PURPLE + "Status:");
sender.sendMessage(C.DARK_PURPLE + "- Trim: " + C.LIGHT_PURPLE + trimTicker.isAlive());
sender.sendMessage(C.DARK_PURPLE + "- Unload: " + C.LIGHT_PURPLE + unloadTicker.isAlive());
sender.sendMessage(C.DARK_PURPLE + "- Update: " + C.LIGHT_PURPLE + updateTicker.isAlive());
sender.sendMessage(C.DARK_PURPLE + "Tectonic Plates:");
sender.sendMessage(C.DARK_PURPLE + "- Limit: " + C.LIGHT_PURPLE + tectonicLimit.get());
sender.sendMessage(C.DARK_PURPLE + "- Total: " + C.LIGHT_PURPLE + tectonicPlates.get());
sender.sendMessage(C.DARK_PURPLE + "- Queued: " + C.LIGHT_PURPLE + queuedTectonicPlates.get());
sender.sendMessage(C.DARK_PURPLE + "- Max Idle Duration: " + C.LIGHT_PURPLE + Form.duration(maxIdleDuration.get(), 2));
sender.sendMessage(C.DARK_PURPLE + "- Min Idle Duration: " + C.LIGHT_PURPLE + Form.duration(minIdleDuration.get(), 2));
sender.sendMessage(C.DARK_PURPLE + "Other:");
sender.sendMessage(C.DARK_PURPLE + "- Iris Worlds: " + C.LIGHT_PURPLE + worlds.size());
sender.sendMessage(C.DARK_PURPLE + "- Loaded Chunks: " + C.LIGHT_PURPLE + loadedChunks.get());
sender.sendMessage(C.DARK_PURPLE + "- Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
} }
@EventHandler @EventHandler
public void onWorldUnload(WorldUnloadEvent event) { public void onWorldUnload(WorldUnloadEvent event) {
updateWorlds(); worlds.removeIf(p -> p.getA() == event.getWorld());
} }
@EventHandler @EventHandler
public void onWorldLoad(WorldLoadEvent event) { public void onWorldLoad(WorldLoadEvent event) {
updateWorlds(); var world = event.getWorld();
var access = IrisToolbelt.access(world);
if (access == null) return;
worlds.add(new Pair<>(world, access));
} }
@EventHandler private synchronized void setup() {
public void onServerBoot(ServerLoadEvent event) { if (updateTicker != null && updateTicker.isAlive())
isServerLoaded = true; return;
}
@EventHandler
public void onPluginDisable(PluginDisableEvent event) {
if (event.getPlugin().equals(Iris.instance)) {
isServerShuttingDown = true;
}
}
public void updateWorlds() {
for (World world : Bukkit.getWorlds()) {
try {
if (IrisToolbelt.access(world).getEngine() != null) {
IrisWorlds.add(world);
}
} catch (Exception e) {
// no
}
}
}
private void setup() {
cacheTicker = new Looper() {
@Override
protected long loop() {
long now = System.currentTimeMillis();
lastUseLock.lock();
try {
for (World key : new ArrayList<>(lastUse.keySet())) {
Long last = lastUse.get(key);
if (last == null)
continue;
if (now - last > 60000) {
lastUse.remove(key);
}
}
} finally {
lastUseLock.unlock();
}
return 1000;
}
};
updateTicker = new Looper() { updateTicker = new Looper() {
@Override @Override
protected long loop() { protected long loop() {
try { try {
TotalQueuedTectonicPlates.set(0); queuedTectonicPlates.set(0);
TotalNotQueuedTectonicPlates.set(0); tectonicPlates.set(0);
TotalTectonicPlates.set(0); loadedChunks.set(0);
for (World world : IrisWorlds) {
Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine();
TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload());
TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions());
TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
}
if (!isServerShuttingDown && isServerLoaded) {
if (!trimTicker.isAlive()) {
Iris.info(C.RED + "TrimTicker found dead! Booting it up!");
try {
TrimLogic();
} catch (Exception e) {
Iris.error("What happened?");
e.printStackTrace();
}
}
if (!unloadTicker.isAlive()) { double maxDuration = Long.MIN_VALUE;
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!"); double minDuration = Long.MAX_VALUE;
try { for (var pair : worlds) {
UnloadLogic(); var engine = pair.getB().getEngine();
} catch (Exception e) { if (engine == null) continue;
Iris.error("What happened?");
e.printStackTrace(); queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount());
} tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
loadedChunks.addAndGet(pair.getA().getLoadedChunks().length);
double duration = engine.getMantle().getAdjustedIdleDuration();
if (duration > maxDuration) maxDuration = duration;
if (duration < minDuration) minDuration = duration;
}
maxIdleDuration.set(maxDuration);
minIdleDuration.set(minDuration);
if (!trimTicker.isAlive()) {
Iris.error("TrimTicker found dead! Booting it up!");
try {
trimLogic();
} catch (Exception e) {
Iris.error("What happened?");
e.printStackTrace();
} }
} }
} catch (Exception e) { if (!unloadTicker.isAlive()) {
return -1; Iris.error("UnloadTicker found dead! Booting it up!");
try {
unloadLogic();
} catch (Exception e) {
Iris.error("What happened?");
e.printStackTrace();
}
}
} catch (Throwable e) {
e.printStackTrace();
} }
return 1000; return 1000;
} }
}; };
updateTicker.start();
} }
public void TrimLogic() {
if (trimTicker == null || !trimTicker.isAlive()) {
trimTicker = new Looper() {
private final Supplier<Engine> supplier = createSupplier();
@Override private synchronized void trimLogic() {
protected long loop() { if (trimTicker != null && trimTicker.isAlive())
long start = System.currentTimeMillis(); return;
trimAlive.reset();
try { trimTicker = new Looper() {
Engine engine = supplier.get(); private final Supplier<Engine> supplier = createSupplier();
if (engine != null) {
engine.getMantle().trim(tectonicLimit.get() / lastUse.size()); @Override
} protected long loop() {
} catch (Throwable e) { long start = System.currentTimeMillis();
Iris.reportError(e); try {
Iris.info(C.RED + "EngineSVC: Failed to trim."); Engine engine = supplier.get();
e.printStackTrace(); if (engine != null) {
return -1; engine.getMantle().trim(tectonicLimit.get() / worlds.size());
} }
} catch (Throwable e) {
int size = lastUse.size(); Iris.reportError(e);
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); Iris.error("EngineSVC: Failed to trim.");
if (time <= 0) e.printStackTrace();
return 0; return -1;
return time;
} }
};
trimTicker.start(); int size = worlds.size();
} long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
if (time <= 0)
return 0;
return time;
}
};
trimTicker.start();
} }
public void UnloadLogic() {
if (unloadTicker == null || !unloadTicker.isAlive()) {
unloadTicker = new Looper() {
private final Supplier<Engine> supplier = createSupplier();
@Override private synchronized void unloadLogic() {
protected long loop() { if (unloadTicker != null && unloadTicker.isAlive())
long start = System.currentTimeMillis(); return;
unloadAlive.reset();
try { unloadTicker = new Looper() {
Engine engine = supplier.get(); private final Supplier<Engine> supplier = createSupplier();
if (engine != null) {
long unloadStart = System.currentTimeMillis(); @Override
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size()); protected long loop() {
if (count > 0) { long start = System.currentTimeMillis();
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); try {
} Engine engine = supplier.get();
if (engine != null) {
long unloadStart = System.currentTimeMillis();
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / worlds.size());
if (count > 0) {
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
} }
} catch (Throwable e) {
Iris.reportError(e);
Iris.info(C.RED + "EngineSVC: Failed to unload.");
e.printStackTrace();
return -1;
} }
} catch (Throwable e) {
int size = lastUse.size(); Iris.reportError(e);
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start); Iris.error("EngineSVC: Failed to unload.");
if (time <= 0) e.printStackTrace();
return 0; return -1;
return time;
} }
};
unloadTicker.start(); int size = worlds.size();
} long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
if (time <= 0)
return 0;
return time;
}
};
unloadTicker.start();
} }
private Supplier<Engine> createSupplier() { private Supplier<Engine> createSupplier() {
AtomicInteger i = new AtomicInteger(); AtomicInteger i = new AtomicInteger();
return () -> { return () -> {
List<World> worlds = Bukkit.getWorlds();
if (i.get() >= worlds.size()) { if (i.get() >= worlds.size()) {
i.set(0); i.set(0);
} }
try { try {
for (int j = 0; j < worlds.size(); j++) { for (int j = 0; j < worlds.size(); j++) {
World world = worlds.get(i.getAndIncrement()); var pair = worlds.get(i.getAndIncrement());
PlatformChunkGenerator generator = IrisToolbelt.access(world);
if (i.get() >= worlds.size()) { if (i.get() >= worlds.size()) {
i.set(0); i.set(0);
} }
if (generator != null) { var engine = pair.getB().getEngine();
Engine engine = generator.getEngine(); if (engine != null && !engine.isClosed() && engine.getMantle().getMantle().shouldReduce(engine)) {
boolean closed = engine.getMantle().getData().isClosed(); return engine;
if (engine != null && !engine.isStudio() && !closed) {
lastUseLock.lock();
lastUse.put(world, System.currentTimeMillis());
lastUseLock.unlock();
return engine;
}
} }
} }
} catch (Throwable e) { } catch (Throwable e) {
Iris.info(C.RED + "EngineSVC: Failed to create supplier."); Iris.error("EngineSVC: Failed to create supplier.");
e.printStackTrace(); e.printStackTrace();
Iris.reportError(e); Iris.reportError(e);
} }
return null; return null;
}; };
} }
@Override
public void onDisable() {
cacheTicker.interrupt();
trimTicker.interrupt();
unloadTicker.interrupt();
lastUse.clear();
}
} }

View File

@ -299,13 +299,11 @@ public interface EngineMantle extends IObjectPlacer {
} }
} }
default long getToUnload(){ default long getUnloadRegionCount() {
return getMantle().getToUnload().size(); return getMantle().getUnloadRegionCount();
} }
default long getNotQueuedLoadedRegions(){
return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size(); default double getAdjustedIdleDuration() {
} return getMantle().getAdjustedIdleDuration();
default double getTectonicDuration(){
return getMantle().getAdjustedIdleDuration().get();
} }
} }

View File

@ -86,12 +86,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final boolean studio; private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0); private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>(); private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
private Engine engine; private volatile Engine engine;
private Looper hotloader; private volatile Looper hotloader;
private StudioMode lastMode; private volatile StudioMode lastMode;
private DummyBiomeProvider dummyBiomeProvider; private volatile DummyBiomeProvider dummyBiomeProvider;
@Setter @Setter
private StudioGenerator studioGenerator; private volatile StudioGenerator studioGenerator;
private boolean initialized = false; private boolean initialized = false;
@ -110,20 +110,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
} }
private static Field getField(Class clazz, String fieldName)
throws NoSuchFieldException {
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
Class superClass = clazz.getSuperclass();
if (superClass == null) {
throw e;
} else {
return getField(superClass, fieldName);
}
}
}
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onWorldInit(WorldInitEvent event) { public void onWorldInit(WorldInitEvent event) {
try { try {

View File

@ -31,11 +31,11 @@ import com.volmit.iris.util.data.DoubleArrayUtils;
*/ */
public class AtomicAverage { public class AtomicAverage {
protected final AtomicDoubleArray values; protected final AtomicDoubleArray values;
protected int cursor; protected transient int cursor;
private double average; private transient double average;
private double lastSum; private transient double lastSum;
private boolean dirty; private transient boolean dirty;
private boolean brandNew; private transient boolean brandNew;
/** /**
* Create an average holder * Create an average holder
@ -57,7 +57,7 @@ public class AtomicAverage {
* *
* @param i the value * @param i the value
*/ */
public void put(double i) { public synchronized void put(double i) {
try { try {
dirty = true; dirty = true;

View File

@ -18,29 +18,67 @@
package com.volmit.iris.util.collection; package com.volmit.iris.util.collection;
import java.util.Collection; import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
public class KSet<T> extends HashSet<T> { import java.io.Serializable;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
public class KSet<T> extends AbstractSet<T> implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final ConcurrentHashMap<T, Boolean> map;
public KSet() { public KSet() {
super(); map = new ConcurrentHashMap<>();
} }
public KSet(Collection<? extends T> c) { public KSet(Collection<? extends T> c) {
super(c); this();
addAll(c);
} }
public KSet(int initialCapacity, float loadFactor) { public KSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor); map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
} }
public KSet(int initialCapacity) { public KSet(int initialCapacity) {
super(initialCapacity); map = new ConcurrentHashMap<>(initialCapacity);
}
@Override
public int size() {
return map.size();
}
@Override
public boolean contains(Object o) {
return map.containsKey(o);
}
@Override
public boolean add(T t) {
return map.putIfAbsent(t, Boolean.TRUE) == null;
}
@Override
public boolean remove(Object o) {
return map.remove(o) != null;
}
@Override
public void clear() {
map.clear();
}
@NotNull
@Override
public Iterator<T> iterator() {
return map.keySet().iterator();
} }
public KSet<T> copy() { public KSet<T> copy() {
return new KSet<T>(this); return new KSet<>(this);
} }
} }

View File

@ -21,14 +21,13 @@ package com.volmit.iris.util.mantle;
import com.google.common.util.concurrent.AtomicDouble; import com.google.common.util.concurrent.AtomicDouble;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.service.IrisEngineSVC;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.EngineMantle;
import com.volmit.iris.engine.mantle.MantleWriter; import com.volmit.iris.engine.mantle.MantleWriter;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.BlockCoordinates;
import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates; import com.volmit.iris.util.documentation.RegionCoordinates;
@ -51,8 +50,6 @@ import java.util.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* The mantle can store any type of data slice anywhere and manage regions & IO on it's own. * The mantle can store any type of data slice anywhere and manage regions & IO on it's own.
@ -60,18 +57,18 @@ import java.util.concurrent.locks.ReentrantLock;
*/ */
public class Mantle { public class Mantle {
private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true");
private final File dataFolder; private final File dataFolder;
@Getter @Getter
private final int worldHeight; private final int worldHeight;
private final Map<Long, Long> lastUse; private final Map<Long, Long> lastUse;
@Getter
private final Map<Long, TectonicPlate> loadedRegions; private final Map<Long, TectonicPlate> loadedRegions;
private final HyperLock hyperLock; private final HyperLock hyperLock;
private final AtomicBoolean closed; private final AtomicBoolean closed;
private final MultiBurst ioBurst; private final MultiBurst ioBurst;
private final AtomicBoolean ioTrim; private final AtomicBoolean ioTrim;
private final AtomicBoolean ioTectonicUnload; private final AtomicBoolean ioTectonicUnload;
private final AtomicDouble adjustedIdleDuration;
private final KSet<Long> toUnload;
/** /**
* Create a new mantle * Create a new mantle
@ -91,6 +88,8 @@ public class Mantle {
loadedRegions = new KMap<>(); loadedRegions = new KMap<>();
lastUse = new KMap<>(); lastUse = new KMap<>();
ioBurst = MultiBurst.burst; ioBurst = MultiBurst.burst;
adjustedIdleDuration = new AtomicDouble(0);
toUnload = new KSet<>();
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
} }
@ -103,7 +102,7 @@ public class Mantle {
* @return the file * @return the file
*/ */
public static File fileForRegion(File folder, int x, int z) { public static File fileForRegion(File folder, int x, int z) {
return fileForRegion(folder, key(x, z)); return fileForRegion(folder, key(x, z), true);
} }
/** /**
@ -113,12 +112,28 @@ public class Mantle {
* @param key the region key * @param key the region key
* @return the file * @return the file
*/ */
public static File fileForRegion(File folder, Long key) { public static File fileForRegion(File folder, Long key, boolean convert) {
File f = new File(folder, "p." + key + ".ttp.lz4b"); File f = oldFileForRegion(folder, key);
if (!f.getParentFile().exists()) { File fv = new File(folder, "pv." + key + ".ttp.lz4b");
f.getParentFile().mkdirs(); if (f.exists() && !fv.exists() && convert)
return f;
if (!fv.getParentFile().exists()) {
fv.getParentFile().mkdirs();
} }
return f; return fv;
}
/**
* Get the old file for the given region
*
* @param folder the data folder
* @param key the region key
* @return the file
*/
public static File oldFileForRegion(File folder, Long key) {
return new File(folder, "p." + key + ".ttp.lz4b");
} }
/** /**
@ -210,7 +225,7 @@ public class Mantle {
@RegionCoordinates @RegionCoordinates
public boolean hasTectonicPlate(int x, int z) { public boolean hasTectonicPlate(int x, int z) {
Long k = key(x, z); Long k = key(x, z);
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k).exists(); return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k, true).exists();
} }
/** /**
@ -364,7 +379,8 @@ public class Mantle {
loadedRegions.forEach((i, plate) -> b.queue(() -> { loadedRegions.forEach((i, plate) -> b.queue(() -> {
try { try {
plate.close(); plate.close();
plate.write(fileForRegion(dataFolder, i)); plate.write(fileForRegion(dataFolder, i, false));
oldFileForRegion(dataFolder, i).delete();
} catch (Throwable e) { } catch (Throwable e) {
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i)); Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
e.printStackTrace(); e.printStackTrace();
@ -393,16 +409,6 @@ public class Mantle {
return numberOfEntries * bytesPerEntry; return numberOfEntries * bytesPerEntry;
} }
@Getter
private final AtomicDouble adjustedIdleDuration = new AtomicDouble(0);
@Getter
private final AtomicInteger forceAggressiveThreshold = new AtomicInteger(30);
@Getter
private final AtomicLong oldestTectonicPlate = new AtomicLong(0);
private final ReentrantLock unloadLock = new ReentrantLock();
@Getter
private final KList<Long> toUnload = new KList<>();
/** /**
* Save & unload regions that have not been used for more than the * Save & unload regions that have not been used for more than the
* specified amount of milliseconds * specified amount of milliseconds
@ -424,84 +430,75 @@ public class Mantle {
} }
ioTrim.set(true); ioTrim.set(true);
unloadLock.lock();
try { try {
if (lastUse != null && IrisEngineSVC.instance != null) { double adjustedIdleDuration = this.adjustedIdleDuration.get();
if (!lastUse.isEmpty()) { Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration, 0));
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0));
for (long i : new ArrayList<>(lastUse.keySet())) {
double finalAdjustedIdleDuration = adjustedIdleDuration.get();
hyperLock.withLong(i, () -> {
Long lastUseTime = lastUse.get(i);
if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) {
toUnload.add(i);
Iris.debug("Tectonic Region added to unload");
IrisEngineSVC.instance.trimActiveAlive.reset();
}
});
}
}
}
if (lastUse.isEmpty()) return;
double unloadTime = M.ms() - adjustedIdleDuration;
for (long id : lastUse.keySet()) {
hyperLock.withLong(id, () -> {
Long lastUseTime = lastUse.get(id);
if (lastUseTime != null && lastUseTime < unloadTime) {
toUnload.add(id);
Iris.debug("Tectonic Region added to unload");
}
});
}
} catch (Throwable e) { } catch (Throwable e) {
Iris.reportError(e); Iris.reportError(e);
} finally { } finally {
ioTrim.set(false); ioTrim.set(false);
unloadLock.unlock();
} }
} }
public synchronized int unloadTectonicPlate(int tectonicLimit) { public synchronized int unloadTectonicPlate(int tectonicLimit) {
if (closed.get()) {
throw new RuntimeException("The Mantle is closed");
}
AtomicInteger i = new AtomicInteger(); AtomicInteger i = new AtomicInteger();
unloadLock.lock(); BurstExecutor burst = ioBurst.burst(toUnload.size());
BurstExecutor burst = null; burst.setMulticore(toUnload.size() > tectonicLimit);
if (IrisEngineSVC.instance != null) {
try { ioTectonicUnload.set(true);
KList<Long> copy = toUnload.copy(); try {
if (!disableClear) toUnload.clear(); for (long id : toUnload) {
burst = MultiBurst.burst.burst(copy.size()); burst.queue(() -> hyperLock.withLong(id, () -> {
burst.setMulticore(copy.size() > tectonicLimit); TectonicPlate m = loadedRegions.get(id);
for (int j = 0; j < copy.size(); j++) { if (m == null) {
Long id = copy.get(j); Iris.debug("Tectonic Plate was added to unload while not loaded " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
if (id == null) { toUnload.remove(id);
Iris.error("Null id in unloadTectonicPlate at index " + j); return;
continue;
} }
burst.queue(() -> if (m.inUse()) {
hyperLock.withLong(id, () -> { Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
TectonicPlate m = loadedRegions.get(id); lastUse.put(id, M.ms());
if (m != null) { toUnload.remove(id);
if (m.inUse()) { return;
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ()); }
if (disableClear) toUnload.remove(id);
lastUse.put(id, M.ms()); try {
return; m.write(fileForRegion(dataFolder, id, false));
} oldFileForRegion(dataFolder, id).delete();
try { loadedRegions.remove(id);
m.write(fileForRegion(dataFolder, id)); lastUse.remove(id);
loadedRegions.remove(id); toUnload.remove(id);
lastUse.remove(id); i.incrementAndGet();
if (disableClear) toUnload.remove(id); Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
i.incrementAndGet(); } catch (IOException e) {
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); Iris.reportError(e);
IrisEngineSVC.instance.unloadActiveAlive.reset(); }
} catch (IOException e) { }));
Iris.reportError(e);
}
}
}));
}
burst.complete();
} catch (Throwable e) {
e.printStackTrace();
if (burst != null)
burst.complete();
} finally {
unloadLock.unlock();
ioTectonicUnload.set(true);
} }
return i.get(); burst.complete();
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
burst.complete();
} finally {
ioTectonicUnload.set(false);
} }
return i.get(); return i.get();
} }
@ -517,7 +514,7 @@ public class Mantle {
*/ */
@RegionCoordinates @RegionCoordinates
private TectonicPlate get(int x, int z) { private TectonicPlate get(int x, int z) {
if (ioTrim.get()) { if (ioTrim.get() || ioTectonicUnload.get()) {
try { try {
return getSafe(x, z).get(); return getSafe(x, z).get();
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -577,7 +574,7 @@ public class Mantle {
if (file.exists()) { if (file.exists()) {
try { try {
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
region = TectonicPlate.read(worldHeight, file); region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv."));
if (region.getX() != x || region.getZ() != z) { if (region.getX() != x || region.getZ() != z) {
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z); Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
@ -627,6 +624,14 @@ public class Mantle {
return loadedRegions.size(); return loadedRegions.size();
} }
public int getUnloadRegionCount() {
return toUnload.size();
}
public double getAdjustedIdleDuration() {
return adjustedIdleDuration.get();
}
public <T> void set(int x, int y, int z, MatterSlice<T> slice) { public <T> void set(int x, int y, int z, MatterSlice<T> slice) {
if (slice.isEmpty()) { if (slice.isEmpty()) {
return; return;

View File

@ -19,6 +19,7 @@
package com.volmit.iris.util.mantle; package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.CountingDataInputStream;
@ -74,11 +75,12 @@ public class MantleChunk {
* @throws IOException shit happens * @throws IOException shit happens
* @throws ClassNotFoundException shit happens * @throws ClassNotFoundException shit happens
*/ */
public MantleChunk(int sectionHeight, CountingDataInputStream din) throws IOException { public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
this(sectionHeight, din.readByte(), din.readByte()); this(sectionHeight, din.readByte(), din.readByte());
int s = din.readByte(); int s = din.readByte();
int l = version < 0 ? flags.length() : Varint.readUnsignedVarInt(din);
for (int i = 0; i < flags.length(); i++) { for (int i = 0; i < flags.length() && i < l; i++) {
flags.set(i, din.readBoolean() ? 1 : 0); flags.set(i, din.readBoolean() ? 1 : 0);
} }
@ -87,6 +89,10 @@ public class MantleChunk {
long size = din.readInt(); long size = din.readInt();
if (size == 0) continue; if (size == 0) continue;
long start = din.count(); long start = din.count();
if (i >= sectionHeight) {
din.skipTo(start + size);
continue;
}
try { try {
sections.set(i, Matter.readDin(din)); sections.set(i, Matter.readDin(din));
@ -210,6 +216,7 @@ public class MantleChunk {
dos.writeByte(x); dos.writeByte(x);
dos.writeByte(z); dos.writeByte(z);
dos.writeByte(sections.length()); dos.writeByte(sections.length());
Varint.writeUnsignedVarInt(flags.length(), dos);
for (int i = 0; i < flags.length(); i++) { for (int i = 0; i < flags.length(); i++) {
dos.writeBoolean(flags.get(i) == 1); dos.writeBoolean(flags.get(i) == 1);

View File

@ -19,9 +19,10 @@
package com.volmit.iris.util.mantle; package com.volmit.iris.util.mantle;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.EnginePanic;
import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
@ -44,7 +45,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
* Tectonic Plates are fully atomic & thread safe * Tectonic Plates are fully atomic & thread safe
*/ */
public class TectonicPlate { public class TectonicPlate {
private static final KSet<Thread> errors = new KSet<>(); private static final ThreadLocal<Boolean> errors = ThreadLocal.withInitial(() -> false);
public static final int MISSING = -1;
public static final int CURRENT = 0;
private final int sectionHeight; private final int sectionHeight;
private final AtomicReferenceArray<MantleChunk> chunks; private final AtomicReferenceArray<MantleChunk> chunks;
@ -74,11 +77,12 @@ public class TectonicPlate {
* @param din the data input * @param din the data input
* @throws IOException shit happens yo * @throws IOException shit happens yo
*/ */
public TectonicPlate(int worldHeight, CountingDataInputStream din) throws IOException { public TectonicPlate(int worldHeight, CountingDataInputStream din, boolean versioned) throws IOException {
this(worldHeight, din.readInt(), din.readInt()); this(worldHeight, din.readInt(), din.readInt());
if (!din.markSupported()) if (!din.markSupported())
throw new IOException("Mark not supported!"); throw new IOException("Mark not supported!");
int v = versioned ? Varint.readUnsignedVarInt(din) : MISSING;
for (int i = 0; i < chunks.length(); i++) { for (int i = 0; i < chunks.length(); i++) {
long size = din.readInt(); long size = din.readInt();
if (size == 0) continue; if (size == 0) continue;
@ -86,7 +90,7 @@ public class TectonicPlate {
try { try {
Iris.addPanic("read-chunk", "Chunk[" + i + "]"); Iris.addPanic("read-chunk", "Chunk[" + i + "]");
chunks.set(i, new MantleChunk(sectionHeight, din)); chunks.set(i, new MantleChunk(v, sectionHeight, din));
EnginePanic.saveLast(); EnginePanic.saveLast();
} catch (Throwable e) { } catch (Throwable e) {
long end = start + size; long end = start + size;
@ -103,7 +107,7 @@ public class TectonicPlate {
} }
} }
public static TectonicPlate read(int worldHeight, File file) throws IOException { public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException {
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
fc.lock(); fc.lock();
@ -111,10 +115,10 @@ public class TectonicPlate {
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
BufferedInputStream bis = new BufferedInputStream(lz4); BufferedInputStream bis = new BufferedInputStream(lz4);
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) { try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
return new TectonicPlate(worldHeight, din); return new TectonicPlate(worldHeight, din, versioned);
} }
} finally { } finally {
if (errors.remove(Thread.currentThread())) { if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin"); File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) { try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
fc.lock(); fc.lock();
@ -124,6 +128,7 @@ public class TectonicPlate {
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING); Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
} }
} }
errors.remove();
} }
} }
@ -222,7 +227,7 @@ public class TectonicPlate {
write(dos); write(dos);
} }
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
temp.delete(); temp.delete();
} }
@ -235,6 +240,7 @@ public class TectonicPlate {
public void write(DataOutputStream dos) throws IOException { public void write(DataOutputStream dos) throws IOException {
dos.writeInt(x); dos.writeInt(x);
dos.writeInt(z); dos.writeInt(z);
Varint.writeUnsignedVarInt(CURRENT, dos);
var bytes = new ByteArrayOutputStream(8192); var bytes = new ByteArrayOutputStream(8192);
var sub = new DataOutputStream(bytes); var sub = new DataOutputStream(bytes);
@ -256,6 +262,6 @@ public class TectonicPlate {
} }
public static void addError() { public static void addError() {
errors.add(Thread.currentThread()); errors.set(true);
} }
} }

View File

@ -154,15 +154,16 @@ public interface Matter {
matter.putSlice(type, slice); matter.putSlice(type, slice);
} catch (Throwable e) { } catch (Throwable e) {
long end = start + size; long end = start + size;
Iris.error("Failed to read matter slice, skipping it."); if (!(e instanceof ClassNotFoundException)) {
Iris.addPanic("read.byte.range", start + " " + end); Iris.error("Failed to read matter slice, skipping it.");
Iris.addPanic("read.byte.current", din.count() + ""); Iris.addPanic("read.byte.range", start + " " + end);
Iris.reportError(e); Iris.addPanic("read.byte.current", din.count() + "");
e.printStackTrace(); Iris.reportError(e);
Iris.panic(); e.printStackTrace();
Iris.panic();
TectonicPlate.addError();
}
din.skipTo(end); din.skipTo(end);
TectonicPlate.addError();
} }
} }