mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-04-20 15:10:15 +00:00
Merge branch 'dev' into feat/byte_buddy
# Conflicts: # build.gradle # core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java
This commit is contained in:
@@ -573,9 +573,19 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
HandlerList.unregisterAll((Plugin) this);
|
||||
postShutdown.forEach(Runnable::run);
|
||||
services.clear();
|
||||
MultiBurst.burst.close();
|
||||
super.onDisable();
|
||||
|
||||
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
Bukkit.getWorlds()
|
||||
.stream()
|
||||
.map(IrisToolbelt::access)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(PlatformChunkGenerator::close);
|
||||
|
||||
MultiBurst.burst.close();
|
||||
services.clear();
|
||||
}));
|
||||
}
|
||||
|
||||
private void setupPapi() {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.json.JSONException;
|
||||
import com.volmit.iris.util.json.JSONObject;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@@ -136,22 +137,40 @@ public class IrisSettings {
|
||||
@Data
|
||||
public static class IrisSettingsConcurrency {
|
||||
public int parallelism = -1;
|
||||
public int worldGenParallelism = -1;
|
||||
|
||||
public int getWorldGenThreads() {
|
||||
return getThreadCount(worldGenParallelism);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsPregen {
|
||||
public boolean useCacheByDefault = true;
|
||||
public boolean useHighPriority = false;
|
||||
public boolean useVirtualThreads = false;
|
||||
public boolean useTicketQueue = false;
|
||||
public int maxConcurrency = 256;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsPerformance {
|
||||
private IrisSettingsEngineSVC engineSVC = new IrisSettingsEngineSVC();
|
||||
public boolean trimMantleInStudio = false;
|
||||
public int mantleKeepAlive = 30;
|
||||
public int cacheSize = 4_096;
|
||||
public int resourceLoaderCacheSize = 1_024;
|
||||
public int objectLoaderCacheSize = 4_096;
|
||||
public int scriptLoaderCacheSize = 512;
|
||||
public int tectonicPlateSize = -1;
|
||||
public int mantleCleanupDelay = 200;
|
||||
|
||||
public int getTectonicPlateSize() {
|
||||
if (tectonicPlateSize > 0)
|
||||
return tectonicPlateSize;
|
||||
|
||||
return (int) (getHardware.getProcessMemory() / 200L);
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -183,6 +202,7 @@ public class IrisSettings {
|
||||
public boolean DoomsdayAnnihilationSelfDestructMode = false;
|
||||
public boolean commandSounds = true;
|
||||
public boolean debug = false;
|
||||
public boolean dumpMantleOnError = false;
|
||||
public boolean disableNMS = false;
|
||||
public boolean pluginMetrics = true;
|
||||
public boolean splashLogoStartup = true;
|
||||
@@ -223,4 +243,14 @@ public class IrisSettings {
|
||||
public boolean disableTimeAndWeather = true;
|
||||
public boolean autoStartDefaultStudio = false;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class IrisSettingsEngineSVC {
|
||||
public boolean useVirtualThreads = true;
|
||||
public int priority = Thread.NORM_PRIORITY;
|
||||
|
||||
public int getPriority() {
|
||||
return Math.max(Math.min(priority, Thread.MAX_PRIORITY), Thread.MIN_PRIORITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +68,12 @@ public class ServerConfigurator {
|
||||
f.load(spigotConfig);
|
||||
long tt = f.getLong("settings.timeout-time");
|
||||
|
||||
if (tt < TimeUnit.MINUTES.toSeconds(5)) {
|
||||
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + TimeUnit.MINUTES.toSeconds(20) + " (5 minutes)");
|
||||
long spigotTimeout = TimeUnit.MINUTES.toSeconds(5);
|
||||
|
||||
if (tt < spigotTimeout) {
|
||||
Iris.warn("Updating spigot.yml timeout-time: " + tt + " -> " + spigotTimeout + " (5 minutes)");
|
||||
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
||||
f.set("settings.timeout-time", TimeUnit.MINUTES.toSeconds(5));
|
||||
f.set("settings.timeout-time", spigotTimeout);
|
||||
f.save(spigotConfig);
|
||||
}
|
||||
}
|
||||
@@ -81,10 +83,11 @@ public class ServerConfigurator {
|
||||
f.load(spigotConfig);
|
||||
long tt = f.getLong("watchdog.early-warning-delay");
|
||||
|
||||
if (tt < TimeUnit.MINUTES.toMillis(3)) {
|
||||
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + TimeUnit.MINUTES.toMillis(15) + " (3 minutes)");
|
||||
long watchdog = TimeUnit.MINUTES.toMillis(3);
|
||||
if (tt < watchdog) {
|
||||
Iris.warn("Updating paper.yml watchdog early-warning-delay: " + tt + " -> " + watchdog + " (3 minutes)");
|
||||
Iris.warn("You can disable this change (autoconfigureServer) in Iris settings, then change back the value.");
|
||||
f.set("watchdog.early-warning-delay", TimeUnit.MINUTES.toMillis(3));
|
||||
f.set("watchdog.early-warning-delay", watchdog);
|
||||
f.save(spigotConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ package com.volmit.iris.core.commands;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
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.service.IrisEngineSVC;
|
||||
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)
|
||||
public void EngineStatus() {
|
||||
List<World> IrisWorlds = new ArrayList<>();
|
||||
int TotalLoadedChunks = 0;
|
||||
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("-------------------------");
|
||||
Iris.service(IrisEngineSVC.class)
|
||||
.engineStatus(sender());
|
||||
}
|
||||
|
||||
@Decree(description = "Test")
|
||||
@@ -166,7 +120,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
|
||||
File tectonicplates = new File(folder, "mantle");
|
||||
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
|
||||
TectonicPlate.read(maxHeight, i);
|
||||
TectonicPlate.read(maxHeight, i, true);
|
||||
c++;
|
||||
Iris.info("Loaded count: " + c );
|
||||
|
||||
@@ -272,7 +226,8 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
@Param(description = "base IrisWorld") World world,
|
||||
@Param(description = "raw TectonicPlate File") String path,
|
||||
@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)) {
|
||||
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;
|
||||
@@ -289,7 +244,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
service.submit(() -> {
|
||||
try {
|
||||
CountingDataInputStream raw = CountingDataInputStream.wrap(new FileInputStream(file));
|
||||
TectonicPlate plate = new TectonicPlate(height, raw);
|
||||
TectonicPlate plate = new TectonicPlate(height, raw, versioned);
|
||||
raw.close();
|
||||
|
||||
double d1 = 0;
|
||||
@@ -308,7 +263,7 @@ public class CommandDeveloper implements DecreeExecutor {
|
||||
size = tmp.length();
|
||||
start = System.currentTimeMillis();
|
||||
CountingDataInputStream din = createInput(tmp, algorithm);
|
||||
new TectonicPlate(height, din);
|
||||
new TectonicPlate(height, din, true);
|
||||
din.close();
|
||||
d2 += System.currentTimeMillis() - start;
|
||||
tmp.delete();
|
||||
|
||||
@@ -72,7 +72,6 @@ public class CommandIris implements DecreeExecutor {
|
||||
private CommandWhat what;
|
||||
private CommandEdit edit;
|
||||
private CommandFind find;
|
||||
private CommandSupport support;
|
||||
private CommandDeveloper developer;
|
||||
public static boolean worldCreation = false;
|
||||
String WorldEngine;
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 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.commands;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||
import com.volmit.iris.util.decree.annotations.Decree;
|
||||
import com.volmit.iris.util.decree.annotations.Param;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||
import com.volmit.iris.util.misc.Hastebin;
|
||||
import com.volmit.iris.util.misc.Platform;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.nbt.mca.MCAFile;
|
||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
||||
import com.volmit.iris.util.plugin.VolmitSender;
|
||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||
import net.jpountz.lz4.LZ4FrameInputStream;
|
||||
import net.jpountz.lz4.LZ4FrameOutputStream;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import oshi.SystemInfo;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
@Decree(name = "Support", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"support"})
|
||||
public class CommandSupport implements DecreeExecutor {
|
||||
|
||||
@Decree(description = "report")
|
||||
public void report() {
|
||||
try {
|
||||
if (sender().isPlayer()) sender().sendMessage(C.GOLD + "Creating report..");
|
||||
if (!sender().isPlayer()) Iris.info(C.GOLD + "Creating report..");
|
||||
Hastebin.enviornment(sender());
|
||||
|
||||
} catch (Exception e) {
|
||||
Iris.info(C.RED + "Something went wrong: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ import java.awt.image.BufferedImage;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -55,7 +56,7 @@ public class PregeneratorJob implements PregenListener {
|
||||
private static final Color COLOR_NETWORK_GENERATING = parseColor("#836b8c");
|
||||
private static final Color COLOR_GENERATED = parseColor("#65c295");
|
||||
private static final Color COLOR_CLEANED = parseColor("#34eb93");
|
||||
public static PregeneratorJob instance;
|
||||
private static final AtomicReference<PregeneratorJob> instance = new AtomicReference<>();
|
||||
private final MemoryMonitor monitor;
|
||||
private final PregenTask task;
|
||||
private final boolean saving;
|
||||
@@ -73,8 +74,14 @@ public class PregeneratorJob implements PregenListener {
|
||||
private String[] info;
|
||||
|
||||
public PregeneratorJob(PregenTask task, PregeneratorMethod method, Engine engine) {
|
||||
instance.updateAndGet(old -> {
|
||||
if (old != null) {
|
||||
old.pregenerator.close();
|
||||
old.close();
|
||||
}
|
||||
return this;
|
||||
});
|
||||
this.engine = engine;
|
||||
instance = this;
|
||||
monitor = new MemoryMonitor(50);
|
||||
saving = false;
|
||||
info = new String[]{"Initializing..."};
|
||||
@@ -103,37 +110,40 @@ public class PregeneratorJob implements PregenListener {
|
||||
}
|
||||
|
||||
public static boolean shutdownInstance() {
|
||||
if (instance == null) {
|
||||
PregeneratorJob inst = instance.get();
|
||||
if (inst == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
J.a(() -> instance.pregenerator.close());
|
||||
J.a(inst.pregenerator::close);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static PregeneratorJob getInstance() {
|
||||
return instance;
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
public static boolean pauseResume() {
|
||||
if (instance == null) {
|
||||
PregeneratorJob inst = instance.get();
|
||||
if (inst == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isPaused()) {
|
||||
instance.pregenerator.resume();
|
||||
inst.pregenerator.resume();
|
||||
} else {
|
||||
instance.pregenerator.pause();
|
||||
inst.pregenerator.pause();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isPaused() {
|
||||
if (instance == null) {
|
||||
PregeneratorJob inst = instance.get();
|
||||
if (inst == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return instance.paused();
|
||||
return inst.paused();
|
||||
}
|
||||
|
||||
private static Color parseColor(String c) {
|
||||
@@ -183,7 +193,7 @@ public class PregeneratorJob implements PregenListener {
|
||||
J.a(() -> {
|
||||
pregenerator.close();
|
||||
close();
|
||||
instance = null;
|
||||
instance.compareAndSet(this, null);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -311,7 +321,7 @@ public class PregeneratorJob implements PregenListener {
|
||||
@Override
|
||||
public void onClose() {
|
||||
close();
|
||||
instance = null;
|
||||
instance.compareAndSet(this, null);
|
||||
whenDone.forEach(Runnable::run);
|
||||
service.shutdownNow();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public class INMS {
|
||||
"1.21.1", "v1_21_R1",
|
||||
"1.21.2", "v1_21_R2",
|
||||
"1.21.3", "v1_21_R2",
|
||||
"1.21.4", "v1_21_R3"
|
||||
"1.21.4", "v1_21_R3",
|
||||
"1.21.5", "v1_21_R4"
|
||||
);
|
||||
private static final List<Version> PACKS = List.of(
|
||||
new Version(21, 4, "31020"),
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class ChunkUpdater {
|
||||
private static final String REGION_PATH = "region" + File.separator + "r.";
|
||||
private final AtomicBoolean paused = new AtomicBoolean();
|
||||
private final AtomicBoolean cancelled = new AtomicBoolean();
|
||||
private final KMap<Long, Pair<Long, AtomicInteger>> lastUse = new KMap<>();
|
||||
@@ -162,12 +163,12 @@ public class ChunkUpdater {
|
||||
J.sleep(50);
|
||||
}
|
||||
|
||||
if (rX < dimensions.min.getX() || rX > dimensions.max.getX() || rZ < dimensions.min.getZ() || rZ > dimensions.max.getZ()) {
|
||||
return;
|
||||
}
|
||||
if (!new File(world.getWorldFolder(), "region" + File.separator + rX + "." + rZ + ".mca").exists()) {
|
||||
return;
|
||||
}
|
||||
if (rX < dimensions.min.getX() ||
|
||||
rX > dimensions.max.getX() ||
|
||||
rZ < dimensions.min.getZ() ||
|
||||
rZ > dimensions.max.getZ() ||
|
||||
!new File(world.getWorldFolder(), REGION_PATH + rX + "." + rZ + ".mca").exists()
|
||||
) return;
|
||||
|
||||
task.iterateChunks(rX, rZ, (x, z) -> {
|
||||
while (paused.get() && !cancelled.get()) {
|
||||
|
||||
@@ -66,8 +66,10 @@ public class IrisPregenerator {
|
||||
private final KSet<Position2> net;
|
||||
private final ChronoLatch cl;
|
||||
private final ChronoLatch saveLatch = new ChronoLatch(30000);
|
||||
private final IrisPackBenchmarking benchmarking;
|
||||
|
||||
public IrisPregenerator(PregenTask task, PregeneratorMethod generator, PregenListener listener) {
|
||||
benchmarking = IrisPackBenchmarking.getInstance();
|
||||
this.listener = listenify(listener);
|
||||
cl = new ChronoLatch(5000);
|
||||
generatedRegions = new KSet<>();
|
||||
@@ -135,7 +137,7 @@ public class IrisPregenerator {
|
||||
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
|
||||
|
||||
Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s",
|
||||
IrisPackBenchmarking.benchmarkInProgress ? "Benchmarking" : "Pregen",
|
||||
benchmarking != null ? "Benchmarking" : "Pregen",
|
||||
Form.f(generated.get()),
|
||||
Form.f(totalChunks.get()),
|
||||
percentage,
|
||||
@@ -174,10 +176,10 @@ public class IrisPregenerator {
|
||||
task.iterateRegions((x, z) -> visitRegion(x, z, false));
|
||||
Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
|
||||
shutdown();
|
||||
if (!IrisPackBenchmarking.benchmarkInProgress) {
|
||||
if (benchmarking == null) {
|
||||
Iris.info(C.IRIS + "Pregen stopped.");
|
||||
} else {
|
||||
IrisPackBenchmarking.instance.finishedBenchmark(chunksPerSecondHistory);
|
||||
benchmarking.finishedBenchmark(chunksPerSecondHistory);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,30 +32,32 @@ import io.papermc.lib.PaperLib;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
|
||||
private final World world;
|
||||
private final ExecutorService service;
|
||||
private final Executor executor;
|
||||
private final Semaphore semaphore;
|
||||
private final int threads;
|
||||
private final boolean urgent;
|
||||
private final Map<Chunk, Long> lastUse;
|
||||
|
||||
public AsyncPregenMethod(World world, int threads) {
|
||||
public AsyncPregenMethod(World world, int unusedThreads) {
|
||||
if (!PaperLib.isPaper()) {
|
||||
throw new UnsupportedOperationException("Cannot use PaperAsync on non paper!");
|
||||
}
|
||||
|
||||
this.world = world;
|
||||
service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
|
||||
Executors.newVirtualThreadPerTaskExecutor() :
|
||||
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||
this.executor = IrisSettings.get().getPregen().isUseTicketQueue() ? new TicketExecutor() : new ServiceExecutor();
|
||||
this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
|
||||
semaphore = new Semaphore(threads);
|
||||
this.semaphore = new Semaphore(this.threads, true);
|
||||
this.urgent = IrisSettings.get().getPregen().useHighPriority;
|
||||
this.lastUse = new KMap<>();
|
||||
}
|
||||
|
||||
@@ -67,13 +69,18 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Chunk i : new ArrayList<>(lastUse.keySet())) {
|
||||
Long lastUseTime = lastUse.get(i);
|
||||
if (!i.isLoaded() || (lastUseTime != null && M.ms() - lastUseTime >= 10000)) {
|
||||
i.unload();
|
||||
lastUse.remove(i);
|
||||
long minTime = M.ms() - 10_000;
|
||||
lastUse.entrySet().removeIf(i -> {
|
||||
final Chunk chunk = i.getKey();
|
||||
final Long lastUseTime = i.getValue();
|
||||
if (!chunk.isLoaded() || lastUseTime == null)
|
||||
return true;
|
||||
if (lastUseTime < minTime) {
|
||||
chunk.unload();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
world.save();
|
||||
}).get();
|
||||
} catch (Throwable e) {
|
||||
@@ -81,24 +88,10 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private void completeChunk(int x, int z, PregenListener listener) {
|
||||
try {
|
||||
PaperLib.getChunkAtAsync(world, x, z, true).thenAccept((i) -> {
|
||||
lastUse.put(i, M.ms());
|
||||
listener.onChunkGenerated(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
}).get();
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
unloadAndSaveAllChunks();
|
||||
increaseWorkerThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,7 +103,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
public void close() {
|
||||
semaphore.acquireUninterruptibly(threads);
|
||||
unloadAndSaveAllChunks();
|
||||
service.shutdown();
|
||||
executor.shutdown();
|
||||
resetWorkerThreads();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,7 +130,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
service.submit(() -> completeChunk(x, z, listener));
|
||||
executor.generate(x, z, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,4 +141,94 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void increaseWorkerThreads() {
|
||||
THREAD_COUNT.updateAndGet(i -> {
|
||||
if (i > 0) return 1;
|
||||
var adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
|
||||
try {
|
||||
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
|
||||
var pool = field.get(null);
|
||||
var threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length;
|
||||
if (threads >= adjusted) return 0;
|
||||
|
||||
pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted);
|
||||
return threads;
|
||||
} catch (Throwable e) {
|
||||
Iris.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted);
|
||||
Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads");
|
||||
if (e instanceof InvocationTargetException) e.printStackTrace();
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
public static void resetWorkerThreads() {
|
||||
THREAD_COUNT.updateAndGet(i -> {
|
||||
if (i == 0) return 0;
|
||||
try {
|
||||
var field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL");
|
||||
var pool = field.get(null);
|
||||
var method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class);
|
||||
method.invoke(pool, i);
|
||||
return 0;
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to reset worker threads");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return i;
|
||||
});
|
||||
}
|
||||
|
||||
private interface Executor {
|
||||
void generate(int x, int z, PregenListener listener);
|
||||
default void shutdown() {}
|
||||
}
|
||||
|
||||
private class ServiceExecutor implements Executor {
|
||||
private final ExecutorService service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
|
||||
Executors.newVirtualThreadPerTaskExecutor() :
|
||||
new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||
|
||||
public void generate(int x, int z, PregenListener listener) {
|
||||
service.submit(() -> {
|
||||
try {
|
||||
PaperLib.getChunkAtAsync(world, x, z, true, urgent).thenAccept((i) -> {
|
||||
listener.onChunkGenerated(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
if (i == null) return;
|
||||
lastUse.put(i, M.ms());
|
||||
}).get();
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
semaphore.release();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
service.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private class TicketExecutor implements Executor {
|
||||
@Override
|
||||
public void generate(int x, int z, PregenListener listener) {
|
||||
PaperLib.getChunkAtAsync(world, x, z, true, urgent)
|
||||
.exceptionally(e -> {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
})
|
||||
.thenAccept(i -> {
|
||||
semaphore.release();
|
||||
listener.onChunkGenerated(x, z);
|
||||
listener.onChunkCleaned(x, z);
|
||||
if (i == null) return;
|
||||
lastUse.put(i, M.ms());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,13 +175,9 @@ public class ServerBootSFG {
|
||||
}
|
||||
|
||||
public static boolean enoughDiskSpace() {
|
||||
File freeSpace = new File(Bukkit.getWorldContainer() + ".");
|
||||
File freeSpace = Bukkit.getWorldContainer();
|
||||
double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0);
|
||||
if (gigabytes > 3){
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return gigabytes > 3;
|
||||
}
|
||||
|
||||
private static boolean checkJavac(String path) {
|
||||
|
||||
@@ -1,317 +1,242 @@
|
||||
package com.volmit.iris.core.service;
|
||||
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
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.Form;
|
||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
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.PrecisionStopwatch;
|
||||
import lombok.Synchronized;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
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.WorldUnloadEvent;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisEngineSVC implements IrisService {
|
||||
public static IrisEngineSVC instance;
|
||||
public boolean isServerShuttingDown = false;
|
||||
public boolean isServerLoaded = false;
|
||||
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||
private ReentrantLock lastUseLock;
|
||||
private KMap<World, Long> lastUse;
|
||||
private List<World> IrisWorlds;
|
||||
private Looper cacheTicker;
|
||||
private Looper trimTicker;
|
||||
private Looper unloadTicker;
|
||||
private final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||
private final AtomicInteger tectonicPlates = new AtomicInteger();
|
||||
private final AtomicInteger queuedTectonicPlates = new AtomicInteger();
|
||||
private final AtomicInteger trimmerAlive = new AtomicInteger();
|
||||
private final AtomicInteger unloaderAlive = new AtomicInteger();
|
||||
private final AtomicInteger totalWorlds = new AtomicInteger();
|
||||
private final AtomicDouble maxIdleDuration = new AtomicDouble();
|
||||
private final AtomicDouble minIdleDuration = new AtomicDouble();
|
||||
private final AtomicLong loadedChunks = new AtomicLong();
|
||||
private final KMap<World, Registered> worlds = new KMap<>();
|
||||
private ScheduledExecutorService service;
|
||||
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
|
||||
public void onEnable() {
|
||||
this.cl = new ChronoLatch(5000);
|
||||
lastUse = new KMap<>();
|
||||
lastUseLock = new ReentrantLock();
|
||||
IrisWorlds = new ArrayList<>();
|
||||
IsUnloadAlive = new AtomicBoolean(true);
|
||||
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;
|
||||
|
||||
var settings = IrisSettings.get().getPerformance();
|
||||
var engine = settings.getEngineSVC();
|
||||
service = Executors.newScheduledThreadPool(0,
|
||||
(engine.isUseVirtualThreads()
|
||||
? Thread.ofVirtual()
|
||||
: Thread.ofPlatform().priority(engine.getPriority()))
|
||||
.name("Iris EngineSVC-", 0)
|
||||
.factory());
|
||||
tectonicLimit.set(settings.getTectonicPlateSize());
|
||||
Bukkit.getWorlds().forEach(this::add);
|
||||
setup();
|
||||
}
|
||||
|
||||
public void engineStatus() {
|
||||
boolean trimAlive = trimTicker.isAlive();
|
||||
boolean unloadAlive = unloadTicker.isAlive();
|
||||
Iris.info("Status:");
|
||||
Iris.info("- Trim: " + trimAlive);
|
||||
Iris.info("- Unload: " + unloadAlive);
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
service.shutdown();
|
||||
updateTicker.interrupt();
|
||||
worlds.keySet().forEach(this::remove);
|
||||
worlds.clear();
|
||||
}
|
||||
|
||||
public static int getTectonicLimit() {
|
||||
return tectonicLimit.get();
|
||||
public void engineStatus(VolmitSender sender) {
|
||||
sender.sendMessage(C.DARK_PURPLE + "-------------------------");
|
||||
sender.sendMessage(C.DARK_PURPLE + "Status:");
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Service: " + C.LIGHT_PURPLE + (service.isShutdown() ? "Shutdown" : "Running"));
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Updater: " + C.LIGHT_PURPLE + (updateTicker.isAlive() ? "Running" : "Stopped"));
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Trimmers: " + C.LIGHT_PURPLE + trimmerAlive.get());
|
||||
sender.sendMessage(C.DARK_PURPLE + "- Unloaders: " + C.LIGHT_PURPLE + unloaderAlive.get());
|
||||
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 + totalWorlds.get());
|
||||
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
|
||||
public void onWorldUnload(WorldUnloadEvent event) {
|
||||
updateWorlds();
|
||||
remove(event.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldLoad(WorldLoadEvent event) {
|
||||
updateWorlds();
|
||||
add(event.getWorld());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerBoot(ServerLoadEvent event) {
|
||||
isServerLoaded = true;
|
||||
private void remove(World world) {
|
||||
var entry = worlds.remove(world);
|
||||
if (entry == null) return;
|
||||
entry.close();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPluginDisable(PluginDisableEvent event) {
|
||||
if (event.getPlugin().equals(Iris.instance)) {
|
||||
isServerShuttingDown = true;
|
||||
}
|
||||
private void add(World world) {
|
||||
var access = IrisToolbelt.access(world);
|
||||
if (access == null) return;
|
||||
worlds.put(world, new Registered(world.getName(), access));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
private synchronized void setup() {
|
||||
if (updateTicker != null && updateTicker.isAlive())
|
||||
return;
|
||||
|
||||
updateTicker = new Looper() {
|
||||
@Override
|
||||
protected long loop() {
|
||||
try {
|
||||
TotalQueuedTectonicPlates.set(0);
|
||||
TotalNotQueuedTectonicPlates.set(0);
|
||||
TotalTectonicPlates.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();
|
||||
}
|
||||
}
|
||||
queuedTectonicPlates.set(0);
|
||||
tectonicPlates.set(0);
|
||||
loadedChunks.set(0);
|
||||
unloaderAlive.set(0);
|
||||
trimmerAlive.set(0);
|
||||
totalWorlds.set(0);
|
||||
|
||||
if (!unloadTicker.isAlive()) {
|
||||
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!");
|
||||
try {
|
||||
UnloadLogic();
|
||||
} catch (Exception e) {
|
||||
Iris.error("What happened?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
double maxDuration = Long.MIN_VALUE;
|
||||
double minDuration = Long.MAX_VALUE;
|
||||
for (var entry : worlds.entrySet()) {
|
||||
var registered = entry.getValue();
|
||||
if (registered.closed) continue;
|
||||
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
totalWorlds.incrementAndGet();
|
||||
unloaderAlive.addAndGet(registered.unloaderAlive() ? 1 : 0);
|
||||
trimmerAlive.addAndGet(registered.trimmerAlive() ? 1 : 0);
|
||||
|
||||
var engine = registered.getEngine();
|
||||
if (engine == null) continue;
|
||||
|
||||
queuedTectonicPlates.addAndGet((int) engine.getMantle().getUnloadRegionCount());
|
||||
tectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
||||
loadedChunks.addAndGet(entry.getKey().getLoadedChunks().length);
|
||||
|
||||
double duration = engine.getMantle().getAdjustedIdleDuration();
|
||||
if (duration > maxDuration) maxDuration = duration;
|
||||
if (duration < minDuration) minDuration = duration;
|
||||
}
|
||||
maxIdleDuration.set(maxDuration);
|
||||
minIdleDuration.set(minDuration);
|
||||
|
||||
worlds.values().forEach(Registered::update);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
};
|
||||
updateTicker.start();
|
||||
}
|
||||
public void TrimLogic() {
|
||||
if (trimTicker == null || !trimTicker.isAlive()) {
|
||||
trimTicker = new Looper() {
|
||||
private final Supplier<Engine> supplier = createSupplier();
|
||||
|
||||
@Override
|
||||
protected long loop() {
|
||||
long start = System.currentTimeMillis();
|
||||
trimAlive.reset();
|
||||
private final class Registered {
|
||||
private final String name;
|
||||
private final PlatformChunkGenerator access;
|
||||
private transient ScheduledFuture<?> trimmer;
|
||||
private transient ScheduledFuture<?> unloader;
|
||||
private transient boolean closed;
|
||||
|
||||
private Registered(String name, PlatformChunkGenerator access) {
|
||||
this.name = name;
|
||||
this.access = access;
|
||||
update();
|
||||
}
|
||||
|
||||
private boolean unloaderAlive() {
|
||||
return unloader != null && !unloader.isDone() && !unloader.isCancelled();
|
||||
}
|
||||
|
||||
private boolean trimmerAlive() {
|
||||
return trimmer != null && !trimmer.isDone() && !trimmer.isCancelled();
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
private void update() {
|
||||
if (closed || service == null || service.isShutdown())
|
||||
return;
|
||||
|
||||
if (trimmer == null || trimmer.isDone() || trimmer.isCancelled()) {
|
||||
trimmer = service.scheduleAtFixedRate(() -> {
|
||||
Engine engine = getEngine();
|
||||
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
return;
|
||||
|
||||
try {
|
||||
Engine engine = supplier.get();
|
||||
if (engine != null) {
|
||||
engine.getMantle().trim(tectonicLimit.get() / lastUse.size());
|
||||
engine.getMantle().trim(tectonicLimit.get() / worlds.size());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.error("EngineSVC: Failed to trim for " + name);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
||||
unloader = service.scheduleAtFixedRate(() -> {
|
||||
Engine engine = getEngine();
|
||||
if (engine == null || !engine.getMantle().getMantle().shouldReduce(engine))
|
||||
return;
|
||||
|
||||
try {
|
||||
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 trim.");
|
||||
Iris.error("EngineSVC: Failed to unload for " + name);
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int size = lastUse.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
|
||||
protected long loop() {
|
||||
long start = System.currentTimeMillis();
|
||||
unloadAlive.reset();
|
||||
try {
|
||||
Engine engine = supplier.get();
|
||||
if (engine != null) {
|
||||
long unloadStart = System.currentTimeMillis();
|
||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.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;
|
||||
}
|
||||
|
||||
int size = lastUse.size();
|
||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
||||
if (time <= 0)
|
||||
return 0;
|
||||
return time;
|
||||
}
|
||||
};
|
||||
unloadTicker.start();
|
||||
}
|
||||
}
|
||||
|
||||
private Supplier<Engine> createSupplier() {
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
return () -> {
|
||||
List<World> worlds = Bukkit.getWorlds();
|
||||
if (i.get() >= worlds.size()) {
|
||||
i.set(0);
|
||||
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
try {
|
||||
for (int j = 0; j < worlds.size(); j++) {
|
||||
World world = worlds.get(i.getAndIncrement());
|
||||
PlatformChunkGenerator generator = IrisToolbelt.access(world);
|
||||
if (i.get() >= worlds.size()) {
|
||||
i.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (generator != null) {
|
||||
Engine engine = generator.getEngine();
|
||||
boolean closed = engine.getMantle().getData().isClosed();
|
||||
if (engine != null && !engine.isStudio() && !closed) {
|
||||
lastUseLock.lock();
|
||||
lastUse.put(world, System.currentTimeMillis());
|
||||
lastUseLock.unlock();
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.info(C.RED + "EngineSVC: Failed to create supplier.");
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
@Synchronized
|
||||
private void close() {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
|
||||
if (trimmer != null) {
|
||||
trimmer.cancel(false);
|
||||
trimmer = null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
cacheTicker.interrupt();
|
||||
trimTicker.interrupt();
|
||||
unloadTicker.interrupt();
|
||||
lastUse.clear();
|
||||
if (unloader != null) {
|
||||
unloader.cancel(false);
|
||||
unloader = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Engine getEngine() {
|
||||
if (closed) return null;
|
||||
return access.getEngine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,51 +9,43 @@ import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.exceptions.IrisException;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.time.Clock;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
|
||||
|
||||
public class IrisPackBenchmarking {
|
||||
@Getter
|
||||
public static IrisPackBenchmarking instance;
|
||||
public static boolean benchmarkInProgress = false;
|
||||
private static final ThreadLocal<IrisPackBenchmarking> instance = new ThreadLocal<>();
|
||||
private final PrecisionStopwatch stopwatch = new PrecisionStopwatch();
|
||||
private final IrisDimension dimension;
|
||||
private final int radius;
|
||||
private final boolean gui;
|
||||
|
||||
public IrisPackBenchmarking(IrisDimension dimension, int radius, boolean gui) {
|
||||
instance = this;
|
||||
this.dimension = dimension;
|
||||
this.radius = radius;
|
||||
this.gui = gui;
|
||||
runBenchmark();
|
||||
}
|
||||
|
||||
public static IrisPackBenchmarking getInstance() {
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
private void runBenchmark() {
|
||||
Thread.ofVirtual()
|
||||
.name("PackBenchmarking")
|
||||
.start(() -> {
|
||||
Iris.info("Setting up benchmark environment ");
|
||||
benchmarkInProgress = true;
|
||||
File file = new File("benchmark");
|
||||
if (file.exists()) {
|
||||
deleteDirectory(file.toPath());
|
||||
}
|
||||
IO.delete(new File(Bukkit.getWorldContainer(), "benchmark"));
|
||||
createBenchmark();
|
||||
while (!IrisToolbelt.isIrisWorld(Bukkit.getWorld("benchmark"))) {
|
||||
J.sleep(1000);
|
||||
@@ -66,13 +58,9 @@ public class IrisPackBenchmarking {
|
||||
|
||||
}
|
||||
|
||||
public boolean getBenchmarkInProgress() {
|
||||
return benchmarkInProgress;
|
||||
}
|
||||
|
||||
public void finishedBenchmark(KList<Integer> cps) {
|
||||
try {
|
||||
String time = Form.duration(stopwatch.getMillis());
|
||||
String time = Form.duration((long) stopwatch.getMilliseconds());
|
||||
Engine engine = IrisToolbelt.access(Bukkit.getWorld("benchmark")).getEngine();
|
||||
Iris.info("-----------------");
|
||||
Iris.info("Results:");
|
||||
@@ -83,11 +71,7 @@ public class IrisPackBenchmarking {
|
||||
Iris.info(" - Lowest CPS: " + findLowest(cps));
|
||||
Iris.info("-----------------");
|
||||
Iris.info("Creating a report..");
|
||||
File profilers = new File("plugins" + File.separator + "Iris" + File.separator + "packbenchmarks");
|
||||
profilers.mkdir();
|
||||
|
||||
File results = new File(profilers, dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
||||
results.getParentFile().mkdirs();
|
||||
File results = Iris.instance.getDataFile("packbenchmarks", dimension.getName() + " " + LocalDateTime.now(Clock.systemDefaultZone()).toString().replace(':', '-') + ".txt");
|
||||
KMap<String, Double> metrics = engine.getMetrics().pull();
|
||||
try (FileWriter writer = new FileWriter(results)) {
|
||||
writer.write("-----------------\n");
|
||||
@@ -143,13 +127,18 @@ public class IrisPackBenchmarking {
|
||||
}
|
||||
|
||||
private void startBenchmark() {
|
||||
IrisToolbelt.pregenerate(PregenTask
|
||||
.builder()
|
||||
.gui(gui)
|
||||
.radiusX(radius)
|
||||
.radiusZ(radius)
|
||||
.build(), Bukkit.getWorld("benchmark")
|
||||
);
|
||||
try {
|
||||
instance.set(this);
|
||||
IrisToolbelt.pregenerate(PregenTask
|
||||
.builder()
|
||||
.gui(gui)
|
||||
.radiusX(radius)
|
||||
.radiusZ(radius)
|
||||
.build(), Bukkit.getWorld("benchmark")
|
||||
);
|
||||
} finally {
|
||||
instance.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateAverage(KList<Integer> list) {
|
||||
@@ -178,26 +167,4 @@ public class IrisPackBenchmarking {
|
||||
private int findHighest(KList<Integer> list) {
|
||||
return Collections.max(list);
|
||||
}
|
||||
|
||||
private boolean deleteDirectory(Path dir) {
|
||||
try {
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +142,7 @@ public class IrisToolbelt {
|
||||
* @return the pregenerator job (already started)
|
||||
*/
|
||||
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) {
|
||||
return pregenerate(task, method, engine, true);
|
||||
return pregenerate(task, method, engine, IrisSettings.get().getPregen().useCacheByDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,7 +180,10 @@ public class IrisEngine implements Engine {
|
||||
File[] roots = getData().getLoaders()
|
||||
.values()
|
||||
.stream()
|
||||
.map(ResourceLoader::getRoot)
|
||||
.map(ResourceLoader::getFolderName)
|
||||
.map(n -> new File(getData().getDataFolder(), n))
|
||||
.filter(File::exists)
|
||||
.filter(File::isDirectory)
|
||||
.toArray(File[]::new);
|
||||
hash32.complete(IO.hashRecursive(roots));
|
||||
});
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -367,7 +368,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
private void spawn(IrisPosition pos, IrisEntitySpawn i) {
|
||||
IrisSpawner ref = i.getReferenceSpawner();
|
||||
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ()))
|
||||
if (!ref.canSpawn(getEngine(), pos.getX() >> 4, pos.getZ() >> 4))
|
||||
return;
|
||||
|
||||
int s = i.spawn(getEngine(), pos, RNG.r);
|
||||
@@ -422,9 +423,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
energy += 0.3;
|
||||
fixEnergy();
|
||||
getEngine().cleanupMantleChunk(e.getX(), e.getZ());
|
||||
var ref = new WeakReference<>(e.getWorld());
|
||||
int x = e.getX(), z = e.getZ();
|
||||
J.s(() -> {
|
||||
World world = ref.get();
|
||||
if (world == null || !world.isChunkLoaded(x, z))
|
||||
return;
|
||||
energy += 0.3;
|
||||
fixEnergy();
|
||||
getEngine().cleanupMantleChunk(x, z);
|
||||
}, IrisSettings.get().getPerformance().mantleCleanupDelay);
|
||||
|
||||
if (generated) {
|
||||
//INMS.get().injectBiomesFromMantle(e, getMantle());
|
||||
|
||||
@@ -288,76 +288,79 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
return;
|
||||
}
|
||||
|
||||
var chunk = mantle.getChunk(c);
|
||||
if (chunk.isFlagged(MantleFlag.ETCHED)) return;
|
||||
chunk.flag(MantleFlag.ETCHED, true);
|
||||
var chunk = mantle.getChunk(c).use();
|
||||
try {
|
||||
Semaphore semaphore = new Semaphore(3);
|
||||
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
|
||||
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
||||
int betterY = y + getWorld().minHeight();
|
||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
||||
});
|
||||
})));
|
||||
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||
});
|
||||
})));
|
||||
|
||||
Semaphore semaphore = new Semaphore(3);
|
||||
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
||||
int betterY = y + getWorld().minHeight();
|
||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
||||
});
|
||||
})));
|
||||
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||
});
|
||||
})));
|
||||
|
||||
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
KMap<Long, Integer> updates = new KMap<>();
|
||||
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
||||
return;
|
||||
}
|
||||
boolean u = false;
|
||||
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
||||
u = true;
|
||||
}
|
||||
|
||||
if (u) {
|
||||
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
||||
if (vv != null) {
|
||||
return Math.max(vv, y);
|
||||
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
KMap<Long, Integer> updates = new KMap<>();
|
||||
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
||||
return;
|
||||
}
|
||||
boolean u = false;
|
||||
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
||||
u = true;
|
||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
||||
u = true;
|
||||
}
|
||||
|
||||
return y;
|
||||
if (u) {
|
||||
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
||||
if (vv != null) {
|
||||
return Math.max(vv, y);
|
||||
}
|
||||
|
||||
return y;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (v != null && v.isUpdate()) {
|
||||
int vx = x & 15;
|
||||
int vz = z & 15;
|
||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
||||
updateLighting(x, y, z, c);
|
||||
}
|
||||
}
|
||||
});
|
||||
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||
}, RNG.r.i(0, 20))));
|
||||
});
|
||||
|
||||
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
||||
int y = yf + getWorld().minHeight();
|
||||
if (v != null && v.isUpdate()) {
|
||||
int vx = x & 15;
|
||||
int vz = z & 15;
|
||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
||||
updateLighting(x, y, z, c);
|
||||
}
|
||||
}
|
||||
});
|
||||
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||
}, RNG.r.i(0, 20))));
|
||||
|
||||
try {
|
||||
semaphore.acquire(3);
|
||||
} catch (InterruptedException ignored) {}
|
||||
try {
|
||||
semaphore.acquire(3);
|
||||
} catch (InterruptedException ignored) {}
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static Runnable run(Semaphore semaphore, Runnable runnable) {
|
||||
|
||||
@@ -289,23 +289,25 @@ public interface EngineMantle extends IObjectPlacer {
|
||||
}
|
||||
|
||||
default void cleanupChunk(int x, int z) {
|
||||
if (!getMantle().hasFlag(x, z, MantleFlag.CLEANED) && isCovered(x, z)) {
|
||||
getMantle().raiseFlag(x, z, MantleFlag.CLEANED, () -> {
|
||||
getMantle().deleteChunkSlice(x, z, BlockData.class);
|
||||
getMantle().deleteChunkSlice(x, z, String.class);
|
||||
getMantle().deleteChunkSlice(x, z, MatterCavern.class);
|
||||
getMantle().deleteChunkSlice(x, z, MatterFluidBody.class);
|
||||
if (!isCovered(x, z)) return;
|
||||
MantleChunk chunk = getMantle().getChunk(x, z).use();
|
||||
try {
|
||||
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
|
||||
chunk.deleteSlices(BlockData.class);
|
||||
chunk.deleteSlices(String.class);
|
||||
chunk.deleteSlices(MatterCavern.class);
|
||||
chunk.deleteSlices(MatterFluidBody.class);
|
||||
});
|
||||
} finally {
|
||||
chunk.release();
|
||||
}
|
||||
}
|
||||
|
||||
default long getToUnload(){
|
||||
return getMantle().getToUnload().size();
|
||||
default long getUnloadRegionCount() {
|
||||
return getMantle().getUnloadRegionCount();
|
||||
}
|
||||
default long getNotQueuedLoadedRegions(){
|
||||
return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size();
|
||||
}
|
||||
default double getTectonicDuration(){
|
||||
return getMantle().getAdjustedIdleDuration().get();
|
||||
|
||||
default double getAdjustedIdleDuration() {
|
||||
return getMantle().getAdjustedIdleDuration();
|
||||
}
|
||||
}
|
||||
@@ -60,8 +60,9 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
|
||||
for (int i = -radius; i <= radius; i++) {
|
||||
for (int j = -radius; j <= radius; j++) {
|
||||
int r = radius / 4;
|
||||
for (int i = -r; i <= r; i++) {
|
||||
for (int j = -r; j <= r; j++) {
|
||||
cachedChunks.put(Cache.key(i + x, j + z), mantle.getChunk(i + x, j + z).use());
|
||||
}
|
||||
}
|
||||
@@ -143,7 +144,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
|
||||
if (cx >= this.x - radius && cx <= this.x + radius
|
||||
&& cz >= this.z - radius && cz <= this.z + radius) {
|
||||
MantleChunk chunk = cachedChunks.get(Cache.key(cx, cz));
|
||||
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
|
||||
|
||||
if (chunk == null) {
|
||||
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
|
||||
@@ -152,6 +153,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
|
||||
Matter matter = chunk.getOrCreate(y >> 4);
|
||||
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
|
||||
} else {
|
||||
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -639,9 +642,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
cachedChunks.values().removeIf(c -> {
|
||||
c.release();
|
||||
return true;
|
||||
});
|
||||
var iterator = cachedChunks.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next().release();
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,6 @@ import org.bukkit.block.data.BlockData;
|
||||
public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
||||
private final RNG rng;
|
||||
private final BlockData AIR = Material.CAVE_AIR.createBlockData();
|
||||
private final BlockData WATER = Material.WATER.createBlockData();
|
||||
private final BlockData LAVA = Material.LAVA.createBlockData();
|
||||
private final IrisDecorantActuator decorant;
|
||||
|
||||
@@ -103,7 +102,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
||||
}
|
||||
|
||||
if (c.isWater()) {
|
||||
output.set(rx, yy, rz, WATER);
|
||||
output.set(rx, yy, rz, context.getFluid().get(rx, rz));
|
||||
} else if (c.isLava()) {
|
||||
output.set(rx, yy, rz, LAVA);
|
||||
} else {
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.math.Vector3d;
|
||||
import com.volmit.iris.util.matter.MatterMarker;
|
||||
import com.volmit.iris.util.matter.slices.MarkerMatter;
|
||||
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -38,9 +37,6 @@ import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
|
||||
@Snippet("entity-spawn")
|
||||
@Accessors(chain = true)
|
||||
@@ -116,8 +112,8 @@ public class IrisEntitySpawn implements IRare {
|
||||
World world = gen.getWorld().realWorld();
|
||||
if (spawns > 0) {
|
||||
|
||||
if (referenceMarker != null) {
|
||||
gen.getMantle().getMantle().remove(c.getX(), c.getY(), c.getZ(), MatterMarker.class);
|
||||
if (referenceMarker != null && referenceMarker.shouldExhaust()) {
|
||||
gen.getMantle().getMantle().remove(c.getX(), c.getY() - gen.getWorld().minHeight(), c.getZ(), MatterMarker.class);
|
||||
}
|
||||
|
||||
for (int id = 0; id < spawns; id++) {
|
||||
|
||||
@@ -51,10 +51,10 @@ public class IrisMarker extends IrisRegistrant {
|
||||
private boolean emptyAbove = true;
|
||||
|
||||
@Desc("If this marker is used, what is the chance it removes itself. For example 25% (0.25) would mean that on average 4 uses will remove a specific marker. Set this below 0 (-1) to never exhaust & set this to 1 or higher to always exhaust on first use.")
|
||||
private double exhaustionChance = 0.33;
|
||||
private double exhaustionChance = 0;
|
||||
|
||||
public boolean shouldExhaust() {
|
||||
return RNG.r.chance(exhaustionChance);
|
||||
return exhaustionChance > RNG.r.nextDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.volmit.iris.util.io.ReactiveFolder;
|
||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import com.volmit.iris.util.scheduling.Looper;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Setter;
|
||||
@@ -88,12 +89,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
private final AtomicInteger a = new AtomicInteger(0);
|
||||
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
|
||||
private final AtomicCache<EngineTarget> targetCache = new AtomicCache<>();
|
||||
private Engine engine;
|
||||
private Looper hotloader;
|
||||
private StudioMode lastMode;
|
||||
private DummyBiomeProvider dummyBiomeProvider;
|
||||
private volatile Engine engine;
|
||||
private volatile Looper hotloader;
|
||||
private volatile StudioMode lastMode;
|
||||
private volatile DummyBiomeProvider dummyBiomeProvider;
|
||||
@Setter
|
||||
private StudioGenerator studioGenerator;
|
||||
private volatile StudioGenerator studioGenerator;
|
||||
|
||||
private boolean initialized = false;
|
||||
|
||||
@@ -112,20 +113,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
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)
|
||||
public void onWorldInit(WorldInitEvent event) {
|
||||
try {
|
||||
@@ -159,6 +146,20 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getFixedSpawnLocation(@NotNull World world, @NotNull Random random) {
|
||||
Location location = new Location(world, 0, 64, 0);
|
||||
PaperLib.getChunkAtAsync(location)
|
||||
.thenAccept(c -> {
|
||||
World w = c.getWorld();
|
||||
if (!w.getSpawnLocation().equals(location))
|
||||
return;
|
||||
w.setSpawnLocation(location.add(0, w.getHighestBlockYAt(location) - 64, 0));
|
||||
});
|
||||
return location;
|
||||
}
|
||||
|
||||
private void setupEngine() {
|
||||
lastMode = StudioMode.NORMAL;
|
||||
engine = new IrisEngine(getTarget(), studio);
|
||||
@@ -313,7 +314,9 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
hotloader.interrupt();
|
||||
}
|
||||
|
||||
getEngine().close();
|
||||
final Engine engine = getEngine();
|
||||
if (engine != null && !engine.isClosed())
|
||||
engine.close();
|
||||
folder.clear();
|
||||
populators.clear();
|
||||
|
||||
|
||||
@@ -31,11 +31,11 @@ import com.volmit.iris.util.data.DoubleArrayUtils;
|
||||
*/
|
||||
public class AtomicAverage {
|
||||
protected final AtomicDoubleArray values;
|
||||
protected int cursor;
|
||||
private double average;
|
||||
private double lastSum;
|
||||
private boolean dirty;
|
||||
private boolean brandNew;
|
||||
protected transient int cursor;
|
||||
private transient double average;
|
||||
private transient double lastSum;
|
||||
private transient boolean dirty;
|
||||
private transient boolean brandNew;
|
||||
|
||||
/**
|
||||
* Create an average holder
|
||||
@@ -57,7 +57,7 @@ public class AtomicAverage {
|
||||
*
|
||||
* @param i the value
|
||||
*/
|
||||
public void put(double i) {
|
||||
public synchronized void put(double i) {
|
||||
|
||||
try {
|
||||
dirty = true;
|
||||
|
||||
@@ -18,29 +18,67 @@
|
||||
|
||||
package com.volmit.iris.util.collection;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
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 final ConcurrentHashMap<T, Boolean> map;
|
||||
|
||||
public KSet() {
|
||||
super();
|
||||
map = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public KSet(Collection<? extends T> c) {
|
||||
super(c);
|
||||
this();
|
||||
addAll(c);
|
||||
}
|
||||
|
||||
public KSet(int initialCapacity, float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
map = new ConcurrentHashMap<>(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
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() {
|
||||
return new KSet<T>(this);
|
||||
return new KSet<>(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
|
||||
package com.volmit.iris.util.io;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
|
||||
@@ -134,8 +138,7 @@ public class IO {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (var fin = new FileInputStream(file)) {
|
||||
var din = new CheckedInputStream(fin, crc);
|
||||
try (var din = new CheckedInputStream(readDeterministic(file), crc)) {
|
||||
fullTransfer(din, new VoidOutputStream(), 8192);
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
@@ -152,10 +155,43 @@ public class IO {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static InputStream readDeterministic(File file) throws IOException {
|
||||
if (!file.getName().endsWith(".json"))
|
||||
return new FileInputStream(file);
|
||||
|
||||
JsonElement json;
|
||||
try (FileReader reader = new FileReader(file)) {
|
||||
json = JsonParser.parseReader(reader);
|
||||
}
|
||||
|
||||
var queue = new LinkedList<JsonElement>();
|
||||
queue.add(json);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
var element = queue.pop();
|
||||
Collection<JsonElement> add = List.of();
|
||||
|
||||
if (element instanceof JsonObject obj) {
|
||||
var map = obj.asMap();
|
||||
var sorted = new TreeMap<>(map);
|
||||
map.clear();
|
||||
map.putAll(sorted);
|
||||
|
||||
add = sorted.values();
|
||||
} else if (element instanceof JsonArray array) {
|
||||
add = array.asList();
|
||||
}
|
||||
|
||||
add.stream().filter(e -> e.isJsonObject() || e.isJsonArray()).forEach(queue::add);
|
||||
}
|
||||
|
||||
return toInputStream(json.toString());
|
||||
}
|
||||
|
||||
public static String hash(File b) {
|
||||
try {
|
||||
MessageDigest d = MessageDigest.getInstance("SHA-256");
|
||||
DigestInputStream din = new DigestInputStream(new FileInputStream(b), d);
|
||||
DigestInputStream din = new DigestInputStream(readDeterministic(b), d);
|
||||
fullTransfer(din, new VoidOutputStream(), 8192);
|
||||
din.close();
|
||||
return bytesToHex(din.getMessageDigest().digest());
|
||||
|
||||
@@ -31,16 +31,22 @@ public class JarScanner {
|
||||
private final KSet<Class<?>> classes;
|
||||
private final File jar;
|
||||
private final String superPackage;
|
||||
private final boolean report;
|
||||
|
||||
/**
|
||||
* Create a scanner
|
||||
*
|
||||
* @param jar the path to the jar
|
||||
*/
|
||||
public JarScanner(File jar, String superPackage) {
|
||||
public JarScanner(File jar, String superPackage, boolean report) {
|
||||
this.jar = jar;
|
||||
this.classes = new KSet<>();
|
||||
this.superPackage = superPackage;
|
||||
this.report = report;
|
||||
}
|
||||
|
||||
public JarScanner(File jar, String superPackage) {
|
||||
this(jar, superPackage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,7 +71,8 @@ public class JarScanner {
|
||||
try {
|
||||
Class<?> clazz = Class.forName(c);
|
||||
classes.add(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
} catch (Throwable e) {
|
||||
if (!report) continue;
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ package com.volmit.iris.util.mantle;
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.mantle.EngineMantle;
|
||||
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.KSet;
|
||||
import com.volmit.iris.util.documentation.BlockCoordinates;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.documentation.RegionCoordinates;
|
||||
@@ -51,8 +50,6 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
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.
|
||||
@@ -60,18 +57,18 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
|
||||
public class Mantle {
|
||||
private static final boolean disableClear = System.getProperty("disableClear", "false").equals("true");
|
||||
private final File dataFolder;
|
||||
@Getter
|
||||
private final int worldHeight;
|
||||
private final Map<Long, Long> lastUse;
|
||||
@Getter
|
||||
private final Map<Long, TectonicPlate> loadedRegions;
|
||||
private final HyperLock hyperLock;
|
||||
private final AtomicBoolean closed;
|
||||
private final MultiBurst ioBurst;
|
||||
private final AtomicBoolean ioTrim;
|
||||
private final AtomicBoolean ioTectonicUnload;
|
||||
private final AtomicDouble adjustedIdleDuration;
|
||||
private final KSet<Long> toUnload;
|
||||
|
||||
/**
|
||||
* Create a new mantle
|
||||
@@ -91,6 +88,8 @@ public class Mantle {
|
||||
loadedRegions = new KMap<>();
|
||||
lastUse = new KMap<>();
|
||||
ioBurst = MultiBurst.burst;
|
||||
adjustedIdleDuration = new AtomicDouble(0);
|
||||
toUnload = new KSet<>();
|
||||
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
@@ -103,7 +102,7 @@ public class Mantle {
|
||||
* @return the file
|
||||
*/
|
||||
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
|
||||
* @return the file
|
||||
*/
|
||||
public static File fileForRegion(File folder, Long key) {
|
||||
File f = new File(folder, "p." + key + ".ttp.lz4b");
|
||||
if (!f.getParentFile().exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
public static File fileForRegion(File folder, Long key, boolean convert) {
|
||||
File f = oldFileForRegion(folder, key);
|
||||
File fv = new File(folder, "pv." + key + ".ttp.lz4b");
|
||||
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
|
||||
public boolean hasTectonicPlate(int x, int z) {
|
||||
Long k = key(x, z);
|
||||
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k).exists();
|
||||
return loadedRegions.containsKey(k) || fileForRegion(dataFolder, k, true).exists();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -359,16 +374,19 @@ public class Mantle {
|
||||
}
|
||||
|
||||
closed.set(true);
|
||||
BurstExecutor b = ioBurst.burst(loadedRegions.size());
|
||||
for (Long i : loadedRegions.keySet()) {
|
||||
b.queue(() -> {
|
||||
try {
|
||||
loadedRegions.get(i).write(fileForRegion(dataFolder, i));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
hyperLock.disable();
|
||||
BurstExecutor b = ioBurst.burst(toUnload.size());
|
||||
loadedRegions.forEach((i, plate) -> b.queue(() -> {
|
||||
try {
|
||||
plate.close();
|
||||
plate.write(fileForRegion(dataFolder, i, false));
|
||||
oldFileForRegion(dataFolder, i).delete();
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
|
||||
e.printStackTrace();
|
||||
}
|
||||
}));
|
||||
loadedRegions.clear();
|
||||
|
||||
try {
|
||||
b.complete();
|
||||
@@ -376,7 +394,6 @@ public class Mantle {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
|
||||
loadedRegions.clear();
|
||||
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
@@ -392,16 +409,6 @@ public class Mantle {
|
||||
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
|
||||
* specified amount of milliseconds
|
||||
@@ -414,93 +421,81 @@ public class Mantle {
|
||||
}
|
||||
|
||||
adjustedIdleDuration.set(baseIdleDuration);
|
||||
|
||||
if (loadedRegions != null) {
|
||||
if (loadedRegions.size() > tectonicLimit) {
|
||||
// todo update this correctly and maybe do something when its above a 100%
|
||||
adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
|
||||
}
|
||||
if (loadedRegions.size() > tectonicLimit) {
|
||||
// todo update this correctly and maybe do something when its above a 100%
|
||||
adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000));
|
||||
}
|
||||
|
||||
ioTrim.set(true);
|
||||
unloadLock.lock();
|
||||
try {
|
||||
if (lastUse != null && IrisEngineSVC.instance != null) {
|
||||
if (!lastUse.isEmpty()) {
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
double adjustedIdleDuration = this.adjustedIdleDuration.get();
|
||||
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration, 0));
|
||||
|
||||
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) {
|
||||
Iris.reportError(e);
|
||||
} finally {
|
||||
ioTrim.set(false);
|
||||
unloadLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int unloadTectonicPlate(int tectonicLimit) {
|
||||
if (closed.get()) {
|
||||
throw new RuntimeException("The Mantle is closed");
|
||||
}
|
||||
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
unloadLock.lock();
|
||||
BurstExecutor burst = null;
|
||||
if (IrisEngineSVC.instance != null) {
|
||||
try {
|
||||
KList<Long> copy = toUnload.copy();
|
||||
if (!disableClear) toUnload.clear();
|
||||
burst = MultiBurst.burst.burst(copy.size());
|
||||
burst.setMulticore(copy.size() > tectonicLimit);
|
||||
for (int j = 0; j < copy.size(); j++) {
|
||||
Long id = copy.get(j);
|
||||
if (id == null) {
|
||||
Iris.error("Null id in unloadTectonicPlate at index " + j);
|
||||
continue;
|
||||
BurstExecutor burst = ioBurst.burst(toUnload.size());
|
||||
burst.setMulticore(toUnload.size() > tectonicLimit);
|
||||
|
||||
ioTectonicUnload.set(true);
|
||||
try {
|
||||
for (long id : toUnload) {
|
||||
burst.queue(() -> hyperLock.withLong(id, () -> {
|
||||
TectonicPlate m = loadedRegions.get(id);
|
||||
if (m == null) {
|
||||
Iris.debug("Tectonic Plate was added to unload while not loaded " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
toUnload.remove(id);
|
||||
return;
|
||||
}
|
||||
|
||||
burst.queue(() ->
|
||||
hyperLock.withLong(id, () -> {
|
||||
TectonicPlate m = loadedRegions.get(id);
|
||||
if (m != null) {
|
||||
if (m.inUse()) {
|
||||
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());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m.write(fileForRegion(dataFolder, id));
|
||||
loadedRegions.remove(id);
|
||||
lastUse.remove(id);
|
||||
if (disableClear) toUnload.remove(id);
|
||||
i.incrementAndGet();
|
||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
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);
|
||||
if (m.inUse()) {
|
||||
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
||||
lastUse.put(id, M.ms());
|
||||
toUnload.remove(id);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
m.write(fileForRegion(dataFolder, id, false));
|
||||
oldFileForRegion(dataFolder, id).delete();
|
||||
loadedRegions.remove(id);
|
||||
lastUse.remove(id);
|
||||
toUnload.remove(id);
|
||||
i.incrementAndGet();
|
||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
return i.get();
|
||||
burst.complete();
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
burst.complete();
|
||||
} finally {
|
||||
ioTectonicUnload.set(false);
|
||||
}
|
||||
return i.get();
|
||||
}
|
||||
@@ -516,7 +511,7 @@ public class Mantle {
|
||||
*/
|
||||
@RegionCoordinates
|
||||
private TectonicPlate get(int x, int z) {
|
||||
if (ioTrim.get()) {
|
||||
if (ioTrim.get() || ioTectonicUnload.get()) {
|
||||
try {
|
||||
return getSafe(x, z).get();
|
||||
} catch (InterruptedException e) {
|
||||
@@ -576,7 +571,7 @@ public class Mantle {
|
||||
if (file.exists()) {
|
||||
try {
|
||||
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) {
|
||||
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
|
||||
@@ -626,6 +621,14 @@ public class Mantle {
|
||||
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) {
|
||||
if (slice.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package com.volmit.iris.util.mantle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.util.data.Varint;
|
||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||
import com.volmit.iris.util.function.Consumer4;
|
||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||
@@ -30,7 +31,8 @@ import lombok.Getter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
@@ -45,7 +47,8 @@ public class MantleChunk {
|
||||
private final int z;
|
||||
private final AtomicIntegerArray flags;
|
||||
private final AtomicReferenceArray<Matter> sections;
|
||||
private final AtomicInteger ref = new AtomicInteger();
|
||||
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
|
||||
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||
|
||||
/**
|
||||
* Create a mantle chunk
|
||||
@@ -72,11 +75,12 @@ public class MantleChunk {
|
||||
* @throws IOException 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());
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -85,6 +89,10 @@ public class MantleChunk {
|
||||
long size = din.readInt();
|
||||
if (size == 0) continue;
|
||||
long start = din.count();
|
||||
if (i >= sectionHeight) {
|
||||
din.skipTo(start + size);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
sections.set(i, Matter.readDin(din));
|
||||
@@ -103,20 +111,27 @@ public class MantleChunk {
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
closed.set(true);
|
||||
ref.acquire(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public boolean inUse() {
|
||||
return ref.get() > 0;
|
||||
return ref.availablePermits() < Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
public MantleChunk use() {
|
||||
ref.incrementAndGet();
|
||||
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
|
||||
ref.acquireUninterruptibly();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void release() {
|
||||
ref.decrementAndGet();
|
||||
ref.release();
|
||||
}
|
||||
|
||||
public void flag(MantleFlag flag, boolean f) {
|
||||
if (closed.get()) throw new IllegalStateException("Chunk is closed!");
|
||||
flags.set(flag.ordinal(), f ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -201,6 +216,7 @@ public class MantleChunk {
|
||||
dos.writeByte(x);
|
||||
dos.writeByte(z);
|
||||
dos.writeByte(sections.length());
|
||||
Varint.writeUnsignedVarInt(flags.length(), dos);
|
||||
|
||||
for (int i = 0; i < flags.length(); i++) {
|
||||
dos.writeBoolean(flags.get(i) == 1);
|
||||
|
||||
@@ -19,9 +19,10 @@
|
||||
package com.volmit.iris.util.mantle;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.engine.EnginePanic;
|
||||
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.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
@@ -44,7 +45,9 @@ import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
* Tectonic Plates are fully atomic & thread safe
|
||||
*/
|
||||
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 AtomicReferenceArray<MantleChunk> chunks;
|
||||
@@ -74,11 +77,12 @@ public class TectonicPlate {
|
||||
* @param din the data input
|
||||
* @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());
|
||||
if (!din.markSupported())
|
||||
throw new IOException("Mark not supported!");
|
||||
|
||||
int v = versioned ? Varint.readUnsignedVarInt(din) : MISSING;
|
||||
for (int i = 0; i < chunks.length(); i++) {
|
||||
long size = din.readInt();
|
||||
if (size == 0) continue;
|
||||
@@ -86,7 +90,7 @@ public class TectonicPlate {
|
||||
|
||||
try {
|
||||
Iris.addPanic("read-chunk", "Chunk[" + i + "]");
|
||||
chunks.set(i, new MantleChunk(sectionHeight, din));
|
||||
chunks.set(i, new MantleChunk(v, sectionHeight, din));
|
||||
EnginePanic.saveLast();
|
||||
} catch (Throwable e) {
|
||||
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)) {
|
||||
fc.lock();
|
||||
|
||||
@@ -111,10 +115,10 @@ public class TectonicPlate {
|
||||
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
|
||||
BufferedInputStream bis = new BufferedInputStream(lz4);
|
||||
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
|
||||
return new TectonicPlate(worldHeight, din);
|
||||
return new TectonicPlate(worldHeight, din, versioned);
|
||||
}
|
||||
} finally {
|
||||
if (errors.remove(Thread.currentThread())) {
|
||||
if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
|
||||
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
|
||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
||||
fc.lock();
|
||||
@@ -124,6 +128,7 @@ public class TectonicPlate {
|
||||
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
errors.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +141,15 @@ public class TectonicPlate {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void close() throws InterruptedException {
|
||||
for (int i = 0; i < chunks.length(); i++) {
|
||||
MantleChunk chunk = chunks.get(i);
|
||||
if (chunk != null) {
|
||||
chunk.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a chunk exists in this plate or not (same as get(x, z) != null)
|
||||
*
|
||||
@@ -208,15 +222,13 @@ public class TectonicPlate {
|
||||
*/
|
||||
public void write(File file) throws IOException {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) {
|
||||
fc.lock();
|
||||
|
||||
OutputStream fos = Channels.newOutputStream(fc);
|
||||
try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(fos))) {
|
||||
write(dos);
|
||||
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||
}
|
||||
File temp = File.createTempFile("iris-tectonic-plate", ".bin");
|
||||
try (DataOutputStream dos = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(temp)))) {
|
||||
write(dos);
|
||||
}
|
||||
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||
temp.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,6 +240,7 @@ public class TectonicPlate {
|
||||
public void write(DataOutputStream dos) throws IOException {
|
||||
dos.writeInt(x);
|
||||
dos.writeInt(z);
|
||||
Varint.writeUnsignedVarInt(CURRENT, dos);
|
||||
|
||||
var bytes = new ByteArrayOutputStream(8192);
|
||||
var sub = new DataOutputStream(bytes);
|
||||
@@ -249,6 +262,6 @@ public class TectonicPlate {
|
||||
}
|
||||
|
||||
public static void addError() {
|
||||
errors.add(Thread.currentThread());
|
||||
errors.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,15 +154,16 @@ public interface Matter {
|
||||
matter.putSlice(type, slice);
|
||||
} catch (Throwable e) {
|
||||
long end = start + size;
|
||||
Iris.error("Failed to read matter slice, skipping it.");
|
||||
Iris.addPanic("read.byte.range", start + " " + end);
|
||||
Iris.addPanic("read.byte.current", din.count() + "");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.panic();
|
||||
|
||||
if (!(e instanceof ClassNotFoundException)) {
|
||||
Iris.error("Failed to read matter slice, skipping it.");
|
||||
Iris.addPanic("read.byte.range", start + " " + end);
|
||||
Iris.addPanic("read.byte.current", din.count() + "");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.panic();
|
||||
TectonicPlate.addError();
|
||||
}
|
||||
din.skipTo(end);
|
||||
TectonicPlate.addError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
package com.volmit.iris.util.matter.slices;
|
||||
|
||||
import com.volmit.iris.util.data.B;
|
||||
import com.volmit.iris.util.data.IrisCustomData;
|
||||
import com.volmit.iris.util.data.palette.Palette;
|
||||
import com.volmit.iris.util.matter.Sliced;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
@@ -63,6 +63,6 @@ public class BlockMatter extends RawMatter<BlockData> {
|
||||
|
||||
@Override
|
||||
public BlockData readNode(DataInputStream din) throws IOException {
|
||||
return Bukkit.createBlockData(din.readUTF());
|
||||
return B.get(din.readUTF());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
package com.volmit.iris.util.misc;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import oshi.SystemInfo;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
|
||||
public class Hastebin {
|
||||
|
||||
public static void enviornment(CommandSender sender) {
|
||||
// Construct the server information
|
||||
StringBuilder sb = new StringBuilder();
|
||||
SystemInfo systemInfo = new SystemInfo();
|
||||
KList<String> disks = new KList<>(getHardware.getDisk());
|
||||
KList<String> interfaces = new KList<>(getHardware.getInterfaces());
|
||||
KList<String> displays = new KList<>(getHardware.getEDID());
|
||||
KList<String> sensors = new KList<>(getHardware.getSensors());
|
||||
KList<String> gpus = new KList<>(getHardware.getGraphicsCards());
|
||||
KList<String> powersources = new KList<>(getHardware.getPowerSources());
|
||||
|
||||
KList<World> IrisWorlds = new KList<>();
|
||||
KList<World> BukkitWorlds = new KList<>();
|
||||
|
||||
for (World w : Bukkit.getServer().getWorlds()) {
|
||||
try {
|
||||
Engine engine = IrisToolbelt.access(w).getEngine();
|
||||
if (engine != null) {
|
||||
IrisWorlds.add(w);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
BukkitWorlds.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
sb.append(" -- == Iris Info == -- \n");
|
||||
sb.append("Iris Version Version: ").append(Iris.instance.getDescription().getVersion()).append("\n");
|
||||
sb.append("- Iris Worlds");
|
||||
for (World w : IrisWorlds.copy()) {
|
||||
sb.append(" - ").append(w.getName());
|
||||
}
|
||||
sb.append("- Bukkit Worlds");
|
||||
for (World w : BukkitWorlds.copy()) {
|
||||
sb.append(" - ").append(w.getName());
|
||||
}
|
||||
sb.append(" -- == Platform Overview == -- " + "\n");
|
||||
sb.append("Server Type: ").append(Bukkit.getVersion()).append("\n");
|
||||
sb.append("Server Uptime: ").append(Form.stampTime(systemInfo.getOperatingSystem().getSystemUptime())).append("\n");
|
||||
sb.append("Version: ").append(Platform.getVersion()).append(" - Platform: ").append(Platform.getName()).append("\n");
|
||||
sb.append("Java Vendor: ").append(Platform.ENVIRONMENT.getJavaVendor()).append(" - Java Version: ").append(Platform.ENVIRONMENT.getJavaVersion()).append("\n");
|
||||
sb.append(" -- == Processor Overview == -- " + "\n");
|
||||
sb.append("CPU Model: ").append(getHardware.getCPUModel());
|
||||
sb.append("CPU Architecture: ").append(Platform.CPU.getArchitecture()).append(" Available Processors: ").append(Platform.CPU.getAvailableProcessors()).append("\n");
|
||||
sb.append("CPU Load: ").append(Form.pc(Platform.CPU.getCPULoad())).append(" CPU Live Process Load: ").append(Form.pc(Platform.CPU.getLiveProcessCPULoad())).append("\n");
|
||||
sb.append("-=" + " Graphics " + "=- " + "\n");
|
||||
for (String gpu : gpus) {
|
||||
sb.append(" ").append(gpu).append("\n");
|
||||
}
|
||||
sb.append(" -- == Memory Information == -- " + "\n");
|
||||
sb.append("Physical Memory - Total: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.PHYSICAL.getUsedMemory())).append("\n");
|
||||
sb.append("Virtual Memory - Total: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getTotalMemory())).append(" Free: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getFreeMemory())).append(" Used: ").append(Form.memSize(Platform.MEMORY.VIRTUAL.getUsedMemory())).append("\n");
|
||||
sb.append(" -- == Storage Information == -- " + "\n");
|
||||
for (String disk : disks) {
|
||||
sb.append(" ").append(sb.append(disk)).append("\n");
|
||||
}
|
||||
sb.append(" -- == Interface Information == -- "+ "\n" );
|
||||
for (String inter : interfaces) {
|
||||
sb.append(" ").append(inter).append("\n");
|
||||
}
|
||||
sb.append(" -- == Display Information == -- "+ "\n" );
|
||||
for (String display : displays) {
|
||||
sb.append(display).append("\n");
|
||||
}
|
||||
sb.append(" -- == Sensor Information == -- " + "\n");
|
||||
for (String sensor : sensors) {
|
||||
sb.append(" ").append(sensor).append("\n");
|
||||
}
|
||||
sb.append(" -- == Power Information == -- " + "\n");
|
||||
for (String power : powersources) {
|
||||
sb.append(" ").append(power).append("\n");
|
||||
}
|
||||
|
||||
try {
|
||||
String hastebinUrl = uploadToHastebin(sb.toString());
|
||||
|
||||
// Create the clickable message
|
||||
TextComponent message = new TextComponent("[Link]");
|
||||
TextComponent link = new TextComponent(hastebinUrl);
|
||||
link.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, hastebinUrl));
|
||||
message.addExtra(link);
|
||||
|
||||
// Send the clickable message to the player
|
||||
sender.spigot().sendMessage(message);
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(C.DARK_RED + "Failed to upload server information to Hastebin.");
|
||||
}
|
||||
}
|
||||
|
||||
private static String uploadToHastebin(String content) throws Exception {
|
||||
URL url = new URL("https://paste.bytecode.ninja/documents");
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "text/plain");
|
||||
conn.setDoOutput(true);
|
||||
|
||||
DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
|
||||
wr.writeBytes(content);
|
||||
wr.flush();
|
||||
wr.close();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String response = br.readLine();
|
||||
br.close();
|
||||
|
||||
return "https://paste.bytecode.ninja/" + response.split("\"")[3];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
package com.volmit.iris.util.misc;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class Platform {
|
||||
public static String getVersion() {
|
||||
return getSystem().getVersion();
|
||||
}
|
||||
|
||||
public static String getName() {
|
||||
return getSystem().getName();
|
||||
}
|
||||
|
||||
private static OperatingSystemMXBean getSystem() {
|
||||
return (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
|
||||
}
|
||||
|
||||
public static class ENVIRONMENT {
|
||||
public static boolean canRunBatch() {
|
||||
return getSystem().getName().toLowerCase().contains("windows");
|
||||
}
|
||||
|
||||
public static String getJavaHome() {
|
||||
return System.getProperty("java.home");
|
||||
}
|
||||
|
||||
public static String getJavaVendor() {
|
||||
return System.getProperty("java.vendor");
|
||||
}
|
||||
|
||||
public static String getJavaVersion() {
|
||||
return System.getProperty("java.version");
|
||||
}
|
||||
}
|
||||
|
||||
public static class STORAGE {
|
||||
public static long getAbsoluteTotalSpace() {
|
||||
long t = 0;
|
||||
|
||||
for (File i : getRoots()) {
|
||||
t += getTotalSpace(i);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static long getTotalSpace() {
|
||||
return getTotalSpace(new File("."));
|
||||
}
|
||||
|
||||
public static long getTotalSpace(File root) {
|
||||
return root.getTotalSpace();
|
||||
}
|
||||
|
||||
public static long getAbsoluteFreeSpace() {
|
||||
long t = 0;
|
||||
|
||||
for (File i : getRoots()) {
|
||||
t += getFreeSpace(i);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static long getFreeSpace() {
|
||||
return getFreeSpace(new File("."));
|
||||
}
|
||||
|
||||
public static long getFreeSpace(File root) {
|
||||
return root.getFreeSpace();
|
||||
}
|
||||
|
||||
public static long getUsedSpace() {
|
||||
return getTotalSpace() - getFreeSpace();
|
||||
}
|
||||
|
||||
public static long getUsedSpace(File root) {
|
||||
return getTotalSpace(root) - getFreeSpace(root);
|
||||
}
|
||||
|
||||
public static long getAbsoluteUsedSpace() {
|
||||
return getAbsoluteTotalSpace() - getAbsoluteFreeSpace();
|
||||
}
|
||||
|
||||
public static File[] getRoots() {
|
||||
return File.listRoots();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MEMORY {
|
||||
public static class PHYSICAL {
|
||||
public static long getTotalMemory() {
|
||||
return getSystem().getTotalPhysicalMemorySize();
|
||||
}
|
||||
|
||||
public static long getFreeMemory() {
|
||||
return getSystem().getFreePhysicalMemorySize();
|
||||
}
|
||||
|
||||
public static long getUsedMemory() {
|
||||
return getTotalMemory() - getFreeMemory();
|
||||
}
|
||||
}
|
||||
|
||||
public static class VIRTUAL {
|
||||
public static long getTotalMemory() {
|
||||
return getSystem().getTotalSwapSpaceSize();
|
||||
}
|
||||
|
||||
public static long getFreeMemory() {
|
||||
return getSystem().getFreeSwapSpaceSize();
|
||||
}
|
||||
|
||||
public static long getUsedMemory() {
|
||||
return getTotalMemory() - getFreeMemory();
|
||||
}
|
||||
|
||||
public static long getCommittedVirtualMemory() {
|
||||
return getSystem().getCommittedVirtualMemorySize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CPU {
|
||||
public static int getAvailableProcessors() {
|
||||
return getSystem().getAvailableProcessors();
|
||||
}
|
||||
|
||||
public static double getCPULoad() {
|
||||
return getSystem().getSystemCpuLoad();
|
||||
}
|
||||
|
||||
public static double getLiveProcessCPULoad() {
|
||||
return getSystem().getProcessCpuLoad();
|
||||
}
|
||||
|
||||
public static String getArchitecture() {
|
||||
return getSystem().getArch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,5 +143,6 @@ public class HyperLock {
|
||||
|
||||
public void disable() {
|
||||
enabled = false;
|
||||
locks.values().forEach(ReentrantLock::lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class MultiBurst implements ExecutorService {
|
||||
private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000);
|
||||
public static final MultiBurst burst = new MultiBurst();
|
||||
private final AtomicLong last;
|
||||
private final String name;
|
||||
@@ -231,7 +232,7 @@ public class MultiBurst implements ExecutorService {
|
||||
try {
|
||||
while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
|
||||
Iris.info("Still waiting to shutdown burster...");
|
||||
if (p.getMilliseconds() > 7000) {
|
||||
if (p.getMilliseconds() > TIMEOUT) {
|
||||
Iris.warn("Forcing Shutdown...");
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user