Merge branch 'dev' into feat/kts

# Conflicts:
#	build.gradle
#	core/src/main/java/com/volmit/iris/Iris.java
#	core/src/main/java/com/volmit/iris/util/io/IO.java
This commit is contained in:
Julian Krings
2025-06-15 22:54:56 +02:00
81 changed files with 3990 additions and 3249 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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;
@@ -45,6 +46,7 @@ public class IrisSettings {
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
private IrisSettingsPregen pregen = new IrisSettingsPregen();
private IrisSettingsSentry sentry = new IrisSettingsSentry();
public static int getThreadCount(int c) {
return switch (c) {
@@ -130,28 +132,46 @@ public class IrisSettings {
public boolean markerEntitySpawningSystem = true;
public boolean effectSystem = true;
public boolean worldEditWandCUI = true;
public boolean globalPregenCache = true;
public boolean globalPregenCache = false;
}
@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 +203,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;
@@ -202,6 +223,13 @@ public class IrisSettings {
}
}
@Data
public static class IrisSettingsSentry {
public boolean includeServerId = true;
public boolean disableAutoReporting = false;
public boolean debug = false;
}
@Data
public static class IrisSettingsGUI {
public boolean useServerLaunchedGuis = true;
@@ -223,4 +251,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);
}
}
}

View File

@@ -71,10 +71,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);
}
}
@@ -84,10 +86,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);
}
}

View File

@@ -20,13 +20,13 @@ 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;
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.context.IrisContext;
import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.DecreeOrigin;
import com.volmit.iris.util.decree.annotations.Decree;
@@ -68,53 +68,15 @@ 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;
Iris.service(IrisEngineSVC.class)
.engineStatus(sender());
}
long lowestUnloadDuration = 0;
long highestUnloadDuration = 0;
for (World world : Bukkit.getWorlds()) {
try {
if (IrisToolbelt.access(world).getEngine() != null) {
IrisWorlds.add(world);
}
} catch (Exception e) {
// no
}
}
for (World world : IrisWorlds) {
Engine engine = IrisToolbelt.access(world).getEngine();
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
}
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
}
for (Chunk chunk : world.getLoadedChunks()) {
if (chunk.isLoaded()) {
TotalLoadedChunks++;
}
}
}
Iris.info("-------------------------");
Iris.info(C.DARK_PURPLE + "Engine Status");
Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks);
Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit());
Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates);
Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates);
Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates);
Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration));
Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration));
Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
Iris.info("-------------------------");
@Decree(description = "Send a test exception to sentry")
public void Sentry() {
Engine engine = engine();
if (engine != null) IrisContext.getOr(engine);
Iris.reportError(new Exception("This is a test"));
}
@Decree(description = "Test")
@@ -166,7 +128,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 +234,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 +252,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 +271,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();

View File

@@ -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;

View File

@@ -241,7 +241,8 @@ public class CommandObject implements DecreeExecutor {
Location[] b = WandSVC.getCuboid(player());
if (b == null) {
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
return;
}
Location a1 = b[0].clone();
@@ -417,6 +418,10 @@ public class CommandObject implements DecreeExecutor {
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
return;
}
Location a1 = b[0].clone();
Location a2 = b[1].clone();
Direction d = Direction.closest(player().getLocation().getDirection()).reverse();
@@ -477,6 +482,10 @@ public class CommandObject implements DecreeExecutor {
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
return;
}
Location a1 = b[0].clone();
Location a2 = b[1].clone();
Location a1x = b[0].clone();
@@ -524,6 +533,10 @@ public class CommandObject implements DecreeExecutor {
}
Location[] b = WandSVC.getCuboid(player());
if (b == null || b[0] == null || b[1] == null) {
sender().sendMessage("No area selected.");
return;
}
b[0].add(new Vector(0, 1, 0));
b[1].add(new Vector(0, 1, 0));
Location a1 = b[0].clone();

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -18,20 +18,33 @@
package com.volmit.iris.core.link;
import com.google.common.collect.Sets;
import com.volmit.iris.Iris;
import com.volmit.iris.core.tools.IrisToolbelt;
import io.lumine.mythic.api.adapters.AbstractLocation;
import io.lumine.mythic.api.config.MythicLineConfig;
import io.lumine.mythic.api.skills.conditions.ILocationCondition;
import io.lumine.mythic.bukkit.MythicBukkit;
import io.lumine.mythic.bukkit.adapters.BukkitWorld;
import io.lumine.mythic.bukkit.events.MythicConditionLoadEvent;
import io.lumine.mythic.core.skills.SkillCondition;
import io.lumine.mythic.core.utils.annotations.MythicCondition;
import io.lumine.mythic.core.utils.annotations.MythicField;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.plugin.Plugin;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.*;
public class MythicMobsLink {
public MythicMobsLink() {
if (getPlugin() == null) return;
Iris.instance.registerListener(new ConditionListener());
}
public boolean isEnabled() {
@@ -49,12 +62,70 @@ public class MythicMobsLink {
* @param location The location
* @return The mob, or null if it can't be spawned
*/
public @Nullable
Entity spawnMob(String mob, Location location) {
public @Nullable Entity spawnMob(String mob, Location location) {
return isEnabled() ? MythicBukkit.inst().getMobManager().spawnMob(mob, location).getEntity().getBukkitEntity() : null;
}
public Collection<String> getMythicMobTypes() {
return isEnabled() ? MythicBukkit.inst().getMobManager().getMobNames() : List.of();
}
private static class ConditionListener implements Listener {
@EventHandler
public void on(MythicConditionLoadEvent event) {
switch (event.getConditionName()) {
case "irisbiome" -> event.register(new IrisBiomeCondition(event.getConditionName(), event.getConfig()));
case "irisregion" -> event.register(new IrisRegionCondition(event.getConditionName(), event.getConfig()));
}
}
}
@MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes")
public static class IrisBiomeCondition extends SkillCondition implements ILocationCondition {
@MythicField(name = "biome", aliases = {"b"}, description = "A list of biomes to check")
private Set<String> biomes = Sets.newConcurrentHashSet();
@MythicField(name = "surface", aliases = {"s"}, description = "If the biome check should only be performed on the surface")
private boolean surface;
public IrisBiomeCondition(String line, MythicLineConfig mlc) {
super(line);
String b = mlc.getString(new String[]{"biome", "b"}, "");
biomes.addAll(Arrays.asList(b.split(",")));
surface = mlc.getBoolean(new String[]{"surface", "s"}, false);
}
@Override
public boolean check(AbstractLocation target) {
var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld());
if (access == null) return false;
var engine = access.getEngine();
if (engine == null) return false;
var biome = surface ?
engine.getSurfaceBiome(target.getBlockX(), target.getBlockZ()) :
engine.getBiomeOrMantle(target.getBlockX(), target.getBlockY() - engine.getMinHeight(), target.getBlockZ());
return biomes.contains(biome.getLoadKey());
}
}
@MythicCondition(author = "CrazyDev22", name = "irisbiome", description = "Tests if the target is within the given list of biomes")
public static class IrisRegionCondition extends SkillCondition implements ILocationCondition {
@MythicField(name = "region", aliases = {"r"}, description = "A list of regions to check")
private Set<String> regions = Sets.newConcurrentHashSet();
public IrisRegionCondition(String line, MythicLineConfig mlc) {
super(line);
String b = mlc.getString(new String[]{"region", "r"}, "");
regions.addAll(Arrays.asList(b.split(",")));
}
@Override
public boolean check(AbstractLocation target) {
var access = IrisToolbelt.access(((BukkitWorld) target.getWorld()).getBukkitWorld());
if (access == null) return false;
var engine = access.getEngine();
if (engine == null) return false;
var region = engine.getRegion(target.getBlockX(), target.getBlockZ());
return regions.contains(region.getLoadKey());
}
}
}

View File

@@ -47,6 +47,7 @@ public class WorldEditLink {
} catch (Throwable e) {
Iris.error("Could not get selection");
e.printStackTrace();
Iris.reportError(e);
active.reset();
active.aquire(() -> false);
}

View File

@@ -298,6 +298,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
return r;
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
Iris.error("Failed to create loader! " + registrant.getCanonicalName());
}

View File

@@ -45,6 +45,7 @@ import lombok.ToString;
import java.io.*;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -240,8 +241,10 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
for (String i : s) {
burst.queue(() -> {
T t = load(i);
if (t == null)
return;
if (t != null) {
synchronized (m) {
m.add(t);
}
});

View File

@@ -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"),

View File

@@ -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<>();
@@ -108,6 +109,7 @@ public class ChunkUpdater {
}
}
} catch (Exception e) {
Iris.reportError(e);
e.printStackTrace();
}
}, 0, 3, TimeUnit.SECONDS);
@@ -162,12 +164,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()) {
@@ -313,6 +315,7 @@ public class ChunkUpdater {
world.save();
}).get();
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
}
}

View File

@@ -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);
}
}

View File

@@ -7,6 +7,7 @@ import com.volmit.iris.Iris;
import com.volmit.iris.util.data.Varint;
import com.volmit.iris.util.documentation.ChunkCoordinates;
import com.volmit.iris.util.documentation.RegionCoordinates;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.parallel.HyperLock;
import lombok.RequiredArgsConstructor;
import net.jpountz.lz4.LZ4BlockInputStream;
@@ -70,6 +71,7 @@ class PregenCacheImpl implements PregenCache {
return new Plate(key, in);
} catch (IOException e){
Iris.error("Failed to read pregen cache " + file);
Iris.reportError(e);
e.printStackTrace();
return new Plate(key);
}
@@ -82,10 +84,11 @@ class PregenCacheImpl implements PregenCache {
hyperLock.lock(plate.pos.x, plate.pos.z);
try {
File file = fileForPlate(plate.pos);
try (var out = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) {
plate.write(out);
try {
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), plate::write);
} catch (IOException e) {
Iris.error("Failed to write pregen cache " + file);
Iris.reportError(e);
e.printStackTrace();
}
} finally {

View File

@@ -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,100 @@ 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) {
Iris.reportError(e);
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.reportError(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) {
Iris.reportError(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 -> {
Iris.reportError(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());
});
}
}
}

View File

@@ -20,5 +20,15 @@ public class IrisSafeguard {
Iris.instance.splash();
UtilsSFG.splash();
}
public static String mode() {
if (unstablemode) {
return "unstable";
} else if (warningmode) {
return "warning";
} else {
return "stable";
}
}
}

View File

@@ -160,13 +160,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) {

View File

@@ -23,9 +23,11 @@ public class GlobalCacheSVC implements IrisService {
private static final Cache<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build();
private final KMap<String, PregenCache> globalCache = new KMap<>();
private transient boolean lastState;
private static boolean disabled = true;
@Override
public void onEnable() {
disabled = false;
lastState = !IrisSettings.get().getWorld().isGlobalPregenCache();
if (lastState) return;
Bukkit.getWorlds().forEach(this::createCache);
@@ -33,7 +35,8 @@ public class GlobalCacheSVC implements IrisService {
@Override
public void onDisable() {
globalCache.values().forEach(PregenCache::write);
disabled = true;
globalCache.qclear((world, cache) -> cache.write());
}
@Nullable
@@ -99,6 +102,7 @@ public class GlobalCacheSVC implements IrisService {
}
private static PregenCache createDefault0(String worldName) {
if (disabled) return PregenCache.EMPTY;
return PregenCache.create(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pregen"))).sync();
}
}

View File

@@ -1,317 +1,246 @@
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());
} 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());
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);
private int tectonicLimit() {
return tectonicLimit.get() / Math.max(worlds.size(), 1);
}
@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();
}
}
}

View File

@@ -51,6 +51,7 @@ import org.bukkit.util.Vector;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
@@ -80,6 +81,8 @@ public class WandSVC implements IrisService {
try {
Location[] f = getCuboid(p);
if (f == null || f[0] == null || f[1] == null)
return null;
Cuboid c = new Cuboid(f[0], f[1]);
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ());
@@ -198,7 +201,9 @@ public class WandSVC implements IrisService {
public static Location stringToLocation(String s) {
try {
String[] f = s.split("\\Q in \\E");
if (f.length != 2) return null;
String[] g = f[0].split("\\Q,\\E");
if (g.length != 3) return null;
return new Location(Bukkit.getWorld(f[1]), Integer.parseInt(g[0]), Integer.parseInt(g[1]), Integer.parseInt(g[2]));
} catch (Throwable e) {
Iris.reportError(e);
@@ -357,6 +362,7 @@ public class WandSVC implements IrisService {
try {
if ((IrisSettings.get().getWorld().worldEditWandCUI && isHoldingWand(p)) || isWand(p.getInventory().getItemInMainHand())) {
Location[] d = getCuboid(p);
if (d == null || d[0] == null || d[1] == null) return;
new WandSelection(new Cuboid(d[0], d[1]), p).draw();
}
} catch (Throwable e) {

View File

@@ -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;
}
}
}

View File

@@ -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);
}
/**

View File

@@ -96,7 +96,6 @@ public class IrisEngine implements Engine {
private ExecutionEnvironment.Engine execution;
private EngineWorldManager worldManager;
private volatile int parallelism;
private volatile int minHeight;
private boolean failing;
private boolean closed;
private int cacheId;
@@ -129,7 +128,6 @@ public class IrisEngine implements Engine {
getData().setEngine(this);
getData().loadPrefetch(this);
Iris.info("Initializing Engine: " + target.getWorld().name() + "/" + target.getDimension().getLoadKey() + " (" + target.getDimension().getDimensionHeight() + " height) Seed: " + getSeedManager().getSeed());
minHeight = 0;
failing = false;
closed = false;
art = J.ar(this::tickRandomPlayer, 0);
@@ -180,7 +178,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));
});
@@ -472,7 +473,7 @@ public class IrisEngine implements Engine {
getEngineData().getStatistics().generatedChunk();
try {
PrecisionStopwatch p = PrecisionStopwatch.start();
Hunk<BlockData> blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y + getMinHeight(), z + zz, t));
Hunk<BlockData> blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
for (int i = 0; i < 16; i++) {

View File

@@ -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());

View File

@@ -78,7 +78,6 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.awt.Color;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
@@ -141,7 +140,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getTarget().getWorld().minHeight();
}
void setMinHeight(int min);
default void setMinHeight(int min) {
getTarget().getWorld().minHeight(min);
}
@BlockCoordinates
default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException {
@@ -288,76 +289,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) {
@@ -454,14 +458,11 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
}
}
for (int i = 0; i < 4; i++) {
try {
Arrays.parallelSort(nitems, (a, b) -> rng.nextInt());
break;
} catch (Throwable e) {
Iris.reportError(e);
}
for (int i = nitems.length; i > 1; i--) {
int j = rng.nextInt(i);
ItemStack tmp = nitems[i - 1];
nitems[i - 1] = nitems[j];
nitems[j] = tmp;
}
inventory.setContents(nitems);

View File

@@ -47,9 +47,7 @@ public class EnginePlayer {
}
public void tick() {
sample();
if (!IrisSettings.get().getWorld().isEffectSystem())
if (sample() || !IrisSettings.get().getWorld().isEffectSystem())
return;
J.a(() -> {
@@ -81,22 +79,22 @@ public class EnginePlayer {
return M.ms() - lastSample;
}
public void sample() {
public boolean sample() {
Location current = player.getLocation().clone();
if (current.getWorld() != engine.getWorld().realWorld())
return true;
try {
if (ticksSinceLastSample() > 55 && player.getLocation().distanceSquared(lastLocation) > 9 * 9) {
lastLocation = player.getLocation().clone();
if (ticksSinceLastSample() > 55 && current.distanceSquared(lastLocation) > 9 * 9) {
lastLocation = current;
lastSample = M.ms();
sampleBiomeRegion();
biome = engine.getBiome(current);
region = engine.getRegion(current);
}
return false;
} catch (Throwable e) {
Iris.reportError(e);
}
}
private void sampleBiomeRegion() {
Location l = player.getLocation();
biome = engine.getBiome(l);
region = engine.getRegion(l);
return true;
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}
}

View File

@@ -58,21 +58,21 @@ public class MantleCarvingComponent extends IrisMantleComponent {
@ChunkCoordinates
private void carve(IrisCarving carving, MantleWriter writer, RNG rng, int cx, int cz) {
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4);
carving.doCarving(writer, rng, getEngineMantle().getEngine(), cx << 4, -1, cz << 4, 0);
}
private int computeRadius() {
var dimension = getDimension();
int max = 0;
max = Math.max(max, dimension.getCarving().getMaxRange(getData()));
max = Math.max(max, dimension.getCarving().getMaxRange(getData(), 0));
for (var i : dimension.getAllRegions(this::getData)) {
max = Math.max(max, i.getCarving().getMaxRange(getData()));
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
}
for (var i : dimension.getAllBiomes(this::getData)) {
max = Math.max(max, i.getCarving().getMaxRange(getData()));
max = Math.max(max, i.getCarving().getMaxRange(getData(), 0));
}
return max;

View File

@@ -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 {

View File

@@ -19,6 +19,7 @@
package com.volmit.iris.engine.object;
import com.volmit.iris.Iris;
import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.loader.IrisRegistrant;
import com.volmit.iris.core.nms.INMS;
@@ -34,8 +35,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import java.util.Map;
@@ -202,6 +203,14 @@ public class IrisBlockData extends IrisRegistrant {
public TileData tryGetTile(IrisData data) {
//TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities.
var type = getBlockData(data).getMaterial();
if (type == Material.SPAWNER && this.data.containsKey("entitySpawn")) {
String id = (String) this.data.get("entitySpawn");
if (tileData == null) tileData = new KMap<>();
KMap<String, Object> spawnData = (KMap<String, Object>) tileData.computeIfAbsent("SpawnData", k -> new KMap<>());
KMap<String, Object> entity = (KMap<String, Object>) spawnData.computeIfAbsent("entity", k -> new KMap<>());
entity.putIfAbsent("id", Identifier.fromString(id).toString());
}
if (!INMS.get().hasTile(type) || tileData == null || tileData.isEmpty())
return null;
return new TileData(type, this.tileData);

View File

@@ -61,21 +61,25 @@ public class IrisCarving {
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
doCarving(writer, rng, engine, x, y, z, -1);
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int depth) {
doCarving(writer, rng, engine, x, y, z, depth, -1);
}
@BlockCoordinates
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void doCarving(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
int nextRecursion = recursion + 1;
if (caves.isNotEmpty()) {
for (IrisCavePlacer i : caves) {
i.generateCave(writer, rng, engine, x, y, z, waterHint);
if (recursion > i.getMaxRecursion()) continue;
i.generateCave(writer, rng, engine, x, y, z, nextRecursion, waterHint);
}
}
if (ravines.isNotEmpty()) {
for (IrisRavinePlacer i : ravines) {
i.generateRavine(writer, rng, engine, x, y, z, waterHint);
if (recursion > i.getMaxRecursion()) continue;
i.generateRavine(writer, rng, engine, x, y, z, nextRecursion, waterHint);
}
}
@@ -104,15 +108,18 @@ public class IrisCarving {
}
}
public int getMaxRange(IrisData data) {
public int getMaxRange(IrisData data, int recursion) {
int max = 0;
int nextRecursion = recursion + 1;
for (IrisCavePlacer i : caves) {
max = Math.max(max, i.getSize(data));
if (recursion > i.getMaxRecursion()) continue;
max = Math.max(max, i.getSize(data, nextRecursion));
}
for (IrisRavinePlacer i : ravines) {
max = Math.max(max, i.getSize(data));
if (recursion > i.getMaxRecursion()) continue;
max = Math.max(max, i.getSize(data, nextRecursion));
}
if (elipsoids.isNotEmpty()) {

View File

@@ -66,10 +66,10 @@ public class IrisCave extends IrisRegistrant {
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
generate(writer, rng, engine, x, y, z, -1);
generate(writer, rng, engine, x, y, z, 0, -1);
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
double girth = getWorm().getGirth().get(rng, x, z, engine.getData());
KList<IrisPosition> points = getWorm().generate(rng, engine.getData(), writer, verticalRange, x, y, z, (at) -> {
@@ -92,7 +92,7 @@ public class IrisCave extends IrisRegistrant {
int h = Math.min(Math.max(highestWater, waterHint), engine.getDimension().getFluidHeight());
for (IrisPosition i : points) {
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), h);
fork.doCarving(writer, rng, engine, i.getX(), i.getY(), i.getZ(), recursion, h);
}
MatterCavern c = new MatterCavern(true, customBiome, (byte) 0);
@@ -108,7 +108,7 @@ public class IrisCave extends IrisRegistrant {
}
public int getMaxSize(IrisData data) {
return getWorm().getMaxDistance() + fork.getMaxRange(data);
public int getMaxSize(IrisData data, int depth) {
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
}
}

View File

@@ -50,6 +50,10 @@ public class IrisCavePlacer implements IRare {
@Desc("The cave to place")
@RegistryListResource(IrisCave.class)
private String cave;
@MinNumber(1)
@MaxNumber(256)
@Desc("The maximum recursion depth")
private int maxRecursion = 16;
@Desc("If set to true, this cave is allowed to break the surface")
private boolean breakSurface = true;
@Desc("The height range this cave can spawn at. If breakSurface is false, the output of this range will be clamped by the current world height to prevent surface breaking.")
@@ -60,10 +64,10 @@ public class IrisCavePlacer implements IRare {
}
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
generateCave(mantle, rng, engine, x, y, z, -1);
generateCave(mantle, rng, engine, x, y, z, 0, -1);
}
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void generateCave(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
if (fail.get()) {
return;
}
@@ -92,18 +96,18 @@ public class IrisCavePlacer implements IRare {
}
try {
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), waterHint);
cave.generate(mantle, rng, engine, x + rng.nextInt(15), y, z + rng.nextInt(15), recursion, waterHint);
} catch (Throwable e) {
e.printStackTrace();
fail.set(true);
}
}
public int getSize(IrisData data) {
public int getSize(IrisData data, int depth) {
IrisCave cave = getRealCave(data);
if (cave != null) {
return cave.getMaxSize(data);
return cave.getMaxSize(data, depth);
}
return 32;

View File

@@ -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++) {

View File

@@ -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

View File

@@ -93,10 +93,10 @@ public class IrisRavine extends IrisRegistrant {
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z) {
generate(writer, rng, engine, x, y, z, -1);
generate(writer, rng, engine, x, y, z, 0, -1);
}
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void generate(MantleWriter writer, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
KList<IrisPosition> pos = getWorm().generate(rng, engine.getData(), writer, null, x, y, z, (at) -> {
});
CNG dg = depthStyle.getGenerator().createNoCache(rng, engine.getData());
@@ -135,7 +135,7 @@ public class IrisRavine extends IrisRegistrant {
int width = (int) Math.round(bw.fitDouble(baseWidthStyle.getMin(), baseWidthStyle.getMax(), p.getX(), p.getZ()));
int surface = (int) Math.round(rsurface - depth * 0.45);
fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), Math.max(highestWater, waterHint));
fork.doCarving(writer, rng, engine, p.getX(), rng.i(surface - depth, surface), p.getZ(), recursion, Math.max(highestWater, waterHint));
for (int i = surface + depth; i >= surface; i--) {
if (i % ribThickness == 0) {
@@ -184,7 +184,7 @@ public class IrisRavine extends IrisRegistrant {
}
public int getMaxSize(IrisData data) {
return getWorm().getMaxDistance() + fork.getMaxRange(data);
public int getMaxSize(IrisData data, int depth) {
return getWorm().getMaxDistance() + fork.getMaxRange(data, depth);
}
}

View File

@@ -50,16 +50,20 @@ public class IrisRavinePlacer implements IRare {
@Desc("The ravine to place")
@RegistryListResource(IrisRavine.class)
private String ravine;
@MinNumber(1)
@MaxNumber(256)
@Desc("The maximum recursion depth")
private int maxRecursion = 100;
public IrisRavine getRealRavine(IrisData data) {
return ravineCache.aquire(() -> data.getRavineLoader().load(getRavine()));
}
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z) {
generateRavine(mantle, rng, engine, x, y, z, -1);
generateRavine(mantle, rng, engine, x, y, z, 0, -1);
}
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int waterHint) {
public void generateRavine(MantleWriter mantle, RNG rng, Engine engine, int x, int y, int z, int recursion, int waterHint) {
if (fail.get()) {
return;
}
@@ -80,14 +84,14 @@ public class IrisRavinePlacer implements IRare {
try {
int xx = x + rng.nextInt(15);
int zz = z + rng.nextInt(15);
ravine.generate(mantle, rng, engine, xx, y, zz, waterHint);
ravine.generate(mantle, rng, engine, xx, y, zz, recursion, waterHint);
} catch (Throwable e) {
e.printStackTrace();
fail.set(true);
}
}
public int getSize(IrisData data) {
return getRealRavine(data).getMaxSize(data);
public int getSize(IrisData data, int depth) {
return getRealRavine(data).getMaxSize(data, depth);
}
}

View File

@@ -40,6 +40,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;
@@ -86,12 +87,12 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0);
private final CompletableFuture<Integer> spawnChunks = new CompletableFuture<>();
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;
@@ -110,20 +111,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 {
@@ -158,6 +145,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() {
IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
@@ -301,7 +302,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();

View File

@@ -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;

View File

@@ -23,11 +23,9 @@ import com.volmit.iris.util.function.Consumer2;
import com.volmit.iris.util.function.Consumer3;
import com.volmit.iris.util.scheduling.Queue;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@SuppressWarnings("ALL")
@@ -373,6 +371,20 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
return g;
}
public KMap<K, V> qclear(BiConsumer<K, V> action) {
final Iterator<Map.Entry<K, V>> it = entrySet().iterator();
while (it.hasNext()) {
final Map.Entry<K, V> entry = it.next();
it.remove();
try {
action.accept(entry.getKey(), entry.getValue());
} catch (Throwable e) {
Iris.reportError(e);
}
}
return this;
}
/**
* Create a keypair queue
*

View File

@@ -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);
}
}

View File

@@ -87,4 +87,21 @@ public class IrisContext {
public IrisComplex getComplex() {
return engine.getComplex();
}
public KMap<String, Object> asContext() {
var hash32 = engine.getHash32().getNow(null);
var dimension = engine.getDimension();
var mantle = engine.getMantle();
return new KMap<String, Object>()
.qput("studio", engine.isStudio())
.qput("closed", engine.isClosed())
.qput("pack", new KMap<>()
.qput("key", dimension == null ? "" : dimension.getLoadKey())
.qput("version", dimension == null ? "" : dimension.getVersion())
.qput("hash", hash32 == null ? "" : Long.toHexString(hash32)))
.qput("mantle", new KMap<>()
.qput("idle", mantle.getAdjustedIdleDuration())
.qput("loaded", mantle.getLoadedRegionCount())
.qput("queued", mantle.getUnloadRegionCount()));
}
}

View File

@@ -160,7 +160,8 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter {
}
J.aBukkit(() -> {
if (!call(new VolmitSender(sender), args)) {
var volmit = new VolmitSender(sender);
if (!call(volmit, args)) {
if (IrisSettings.get().getGeneral().isCommandSounds()) {
if (sender instanceof Player) {
@@ -169,7 +170,7 @@ public interface DecreeSystem extends CommandExecutor, TabCompleter {
}
}
sender.sendMessage(C.RED + "Unknown Iris Command");
volmit.sendMessage(C.RED + "Unknown Iris Command");
} else {
if (IrisSettings.get().getGeneral().isCommandSounds()) {
if (sender instanceof Player) {

View File

@@ -376,6 +376,28 @@ public enum C {
return "#" + Integer.toHexString(spin(color.awtColor(), h, s, b).getRGB()).substring(2);
}
public static String mini(String s) {
String msg = compress(s);
StringBuilder b = new StringBuilder();
boolean c = false;
for (char i : msg.toCharArray()) {
if (!c) {
if (i == C.COLOR_CHAR) {
c = true;
continue;
}
b.append(i);
} else {
c = false;
C o = C.getByChar(i);
b.append(o.token);
}
}
return b.toString();
}
public static String aura(String s, int hrad, int srad, int vrad) {
return aura(s, hrad, srad, vrad, 0.3D);
}

View File

@@ -18,8 +18,14 @@
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;
import org.apache.commons.io.function.IOConsumer;
import org.apache.commons.io.function.IOFunction;
import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
@@ -29,6 +35,8 @@ import org.dom4j.io.XMLWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -140,8 +148,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);
@@ -158,10 +165,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());
@@ -1626,4 +1666,19 @@ public class IO {
.addAttribute("version", "4");
return doc;
}
public static <T extends OutputStream> void write(File file, IOFunction<FileOutputStream, T> builder, IOConsumer<T> action) throws IOException {
File dir = new File(file.getParentFile(), ".tmp");
dir.mkdirs();
dir.deleteOnExit();
File temp = File.createTempFile("iris",".bin", dir);
try {
try (var out = builder.apply(new FileOutputStream(temp))) {
action.accept(out);
}
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
} finally {
temp.delete();
}
}
}

View File

@@ -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();
}

View File

@@ -21,20 +21,20 @@ 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;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.function.Consumer4;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.math.M;
import com.volmit.iris.util.matter.Matter;
import com.volmit.iris.util.matter.MatterSlice;
@@ -51,8 +51,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 +58,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
@@ -87,10 +85,11 @@ public class Mantle {
this.worldHeight = worldHeight;
this.ioTrim = new AtomicBoolean(false);
this.ioTectonicUnload = new AtomicBoolean(false);
dataFolder.mkdirs();
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();
}
/**
@@ -354,21 +369,24 @@ public class Mantle {
*/
public synchronized void close() {
Iris.debug("Closing The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
if (closed.get()) {
if (closed.getAndSet(true)) {
return;
}
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));
Iris.reportError(e);
e.printStackTrace();
}
}));
loadedRegions.clear();
try {
b.complete();
@@ -376,7 +394,7 @@ public class Mantle {
Iris.reportError(e);
}
loadedRegions.clear();
IO.delete(new File(dataFolder, ".tmp"));
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
}
@@ -392,16 +410,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 +422,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 +512,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 +572,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 +622,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;

View File

@@ -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);

View File

@@ -19,13 +19,15 @@
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;
import com.volmit.iris.util.io.CountingDataInputStream;
import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import lombok.Getter;
import net.jpountz.lz4.LZ4BlockInputStream;
@@ -44,7 +46,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 +78,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 +91,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 +108,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 +116,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 +129,7 @@ public class TectonicPlate {
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
errors.remove();
}
}
@@ -136,6 +142,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 +223,8 @@ 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));
}
}
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write);
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
}
/**
@@ -228,6 +236,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 +258,6 @@ public class TectonicPlate {
}
public static void addError() {
errors.add(Thread.currentThread());
errors.set(true);
}
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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];
}
}

View File

@@ -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();
}
}
}

View File

@@ -143,5 +143,6 @@ public class HyperLock {
public void disable() {
enabled = false;
locks.values().forEach(ReentrantLock::lock);
}
}

View File

@@ -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 {

View File

@@ -1,179 +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.util.particle;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
/**
* Simple Bukkit Particles API with 1.7 to 1.13.2 support !
* <p>
* You can find the project on <a href="https://github.com/MrMicky-FR/FastParticles">GitHub</a>
*
* @author MrMicky
*/
public final class FastParticle {
private static final ParticleSender PARTICLE_SENDER;
static {
if (FastReflection.optionalClass("org.bukkit.Particle$DustOptions").isPresent()) {
PARTICLE_SENDER = new ParticleSender.ParticleSender1_13();
} else if (FastReflection.optionalClass("org.bukkit.Particle").isPresent()) {
PARTICLE_SENDER = new ParticleSender.ParticleSenderImpl();
} else {
PARTICLE_SENDER = new ParticleSenderLegacy();
}
}
private FastParticle() {
throw new UnsupportedOperationException();
}
/*
* Worlds methods
*/
public static void spawnParticle(World world, ParticleType particle, Location location, int count) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count);
}
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count) {
spawnParticle(world, particle, x, y, z, count, null);
}
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count, T data) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, data);
}
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
T data) {
spawnParticle(world, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data);
}
public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
}
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ) {
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
}
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY,
offsetZ, data);
}
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data);
}
public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ, double extra) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
}
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra) {
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
}
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
}
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
sendParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
/*
* Player methods
*/
public static void spawnParticle(Player player, ParticleType particle, Location location, int count) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count);
}
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count) {
spawnParticle(player, particle, x, y, z, count, null);
}
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count, T data) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, data);
}
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
T data) {
spawnParticle(player, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data);
}
public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
}
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ) {
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
}
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data);
}
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data);
}
public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ, double extra) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
}
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra) {
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
}
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
}
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
sendParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
private static void sendParticle(Object receiver, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra, Object data) {
if (!particle.isSupported()) {
throw new IllegalArgumentException("The particle '" + particle + "' is not compatible with your server version");
}
PARTICLE_SENDER.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
}

View File

@@ -1,79 +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.util.particle;
import com.volmit.iris.Iris;
import org.bukkit.Bukkit;
import java.util.Optional;
/**
* Small reflection class to use CraftBukkit and NMS
*
* @author MrMicky
*/
public final class FastReflection {
public static final String OBC_PACKAGE = "org.bukkit.craftbukkit";
public static final String NMS_PACKAGE = "net.minecraft.server";
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1);
private FastReflection() {
throw new UnsupportedOperationException();
}
public static String nmsClassName(String className) {
return NMS_PACKAGE + '.' + VERSION + '.' + className;
}
public static Class<?> nmsClass(String className) throws ClassNotFoundException {
return Class.forName(nmsClassName(className));
}
public static Optional<Class<?>> nmsOptionalClass(String className) {
return optionalClass(nmsClassName(className));
}
public static String obcClassName(String className) {
return OBC_PACKAGE + '.' + VERSION + '.' + className;
}
public static Class<?> obcClass(String className) throws ClassNotFoundException {
return Class.forName(obcClassName(className));
}
public static Optional<Class<?>> obcOptionalClass(String className) {
return optionalClass(obcClassName(className));
}
public static Optional<Class<?>> optionalClass(String className) {
try {
return Optional.of(Class.forName(className));
} catch (ClassNotFoundException e) {
Iris.reportError(e);
return Optional.empty();
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static Object enumValueOf(Class<?> enumClass, String enumName) {
return Enum.valueOf((Class<Enum>) enumClass, enumName.toUpperCase());
}
}

View File

@@ -1,124 +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.util.particle;
import com.volmit.iris.Iris;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.material.MaterialData;
/**
* Particle sender using the Bukkit particles API for 1.9+ servers
*
* @author MrMicky
*/
@SuppressWarnings("deprecation")
interface ParticleSender {
void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data);
Object getParticle(ParticleType particle);
boolean isValidData(Object particle, Object data);
default double color(double color) {
return color / 255.0;
}
class ParticleSenderImpl implements ParticleSender {
@Override
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) {
Particle bukkitParticle = Particle.valueOf(particle.toString());
if (data instanceof Color) {
if (particle.getDataType() == Color.class) {
Color color = (Color) data;
count = 0;
offsetX = color(color.getRed());
offsetY = color(color.getGreen());
offsetZ = color(color.getBlue());
extra = 1.0;
}
data = null;
}
if (receiver instanceof World) {
((World) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
} else if (receiver instanceof Player) {
((Player) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
}
@Override
public Particle getParticle(ParticleType particle) {
try {
return Particle.valueOf(particle.toString());
} catch (IllegalArgumentException e) {
Iris.reportError(e);
return null;
}
}
@Override
public boolean isValidData(Object particle, Object data) {
return isValidDataBukkit((Particle) particle, data);
}
public boolean isValidDataBukkit(Particle particle, Object data) {
return particle.getDataType() == Void.class || particle.getDataType().isInstance(data);
}
}
class ParticleSender1_13 extends ParticleSenderImpl {
@Override
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data) {
Particle bukkitParticle = Particle.valueOf(particle.toString());
if (bukkitParticle.getDataType() == Particle.DustOptions.class) {
if (data instanceof Color) {
data = new Particle.DustOptions((Color) data, 1);
} else if (data == null) {
data = new Particle.DustOptions(Color.RED, 1);
}
} else if (bukkitParticle.getDataType() == BlockData.class && data instanceof MaterialData) {
data = Bukkit.createBlockData(((MaterialData) data).getItemType());
}
super.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
@Override
public boolean isValidDataBukkit(Particle particle, Object data) {
if (particle.getDataType() == Particle.DustOptions.class && data instanceof Color) {
return true;
}
if (particle.getDataType() == BlockData.class && data instanceof MaterialData) {
return true;
}
return super.isValidDataBukkit(particle, data);
}
}
}

View File

@@ -1,185 +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.util.particle;
import com.volmit.iris.Iris;
import org.bukkit.Color;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Legacy particle sender with NMS for 1.7/1.8 servers
*
* @author MrMicky
*/
@SuppressWarnings({"deprecation", "JavaReflectionInvocation"})
class ParticleSenderLegacy implements ParticleSender {
private static final boolean SERVER_IS_1_8;
private static final Constructor<?> PACKET_PARTICLE;
private static final Class<?> ENUM_PARTICLE;
private static final Method WORLD_GET_HANDLE;
private static final Method WORLD_SEND_PARTICLE;
private static final Method PLAYER_GET_HANDLE;
private static final Field PLAYER_CONNECTION;
private static final Method SEND_PACKET;
private static final int[] EMPTY = new int[0];
static {
ENUM_PARTICLE = FastReflection.nmsOptionalClass("EnumParticle").orElse(null);
SERVER_IS_1_8 = ENUM_PARTICLE != null;
try {
Class<?> packetParticleClass = FastReflection.nmsClass("PacketPlayOutWorldParticles");
Class<?> playerClass = FastReflection.nmsClass("EntityPlayer");
Class<?> playerConnectionClass = FastReflection.nmsClass("PlayerConnection");
Class<?> worldClass = FastReflection.nmsClass("WorldServer");
Class<?> entityPlayerClass = FastReflection.nmsClass("EntityPlayer");
Class<?> craftPlayerClass = FastReflection.obcClass("entity.CraftPlayer");
Class<?> craftWorldClass = FastReflection.obcClass("CraftWorld");
if (SERVER_IS_1_8) {
PACKET_PARTICLE = packetParticleClass.getConstructor(ENUM_PARTICLE, boolean.class, float.class,
float.class, float.class, float.class, float.class, float.class, float.class, int.class,
int[].class);
WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("sendParticles", entityPlayerClass, ENUM_PARTICLE,
boolean.class, double.class, double.class, double.class, int.class, double.class, double.class,
double.class, double.class, int[].class);
} else {
PACKET_PARTICLE = packetParticleClass.getConstructor(String.class, float.class, float.class, float.class,
float.class, float.class, float.class, float.class, int.class);
WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("a", String.class, double.class, double.class,
double.class, int.class, double.class, double.class, double.class, double.class);
}
WORLD_GET_HANDLE = craftWorldClass.getDeclaredMethod("getHandle");
PLAYER_GET_HANDLE = craftPlayerClass.getDeclaredMethod("getHandle");
PLAYER_CONNECTION = playerClass.getField("playerConnection");
SEND_PACKET = playerConnectionClass.getMethod("sendPacket", FastReflection.nmsClass("Packet"));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY,
double offsetZ, double extra, Object data) {
try {
int[] datas = toData(particle, data);
if (data instanceof Color) {
if (particle.getDataType() == Color.class) {
Color color = (Color) data;
count = 0;
offsetX = color(color.getRed());
offsetY = color(color.getGreen());
offsetZ = color(color.getBlue());
extra = 1.0;
}
}
if (receiver instanceof World) {
Object worldServer = WORLD_GET_HANDLE.invoke(receiver);
if (SERVER_IS_1_8) {
WORLD_SEND_PARTICLE.invoke(worldServer, null, getEnumParticle(particle), true, x, y, z, count, offsetX, offsetY, offsetZ, extra, datas);
} else {
String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]);
WORLD_SEND_PARTICLE.invoke(worldServer, particleName, x, y, z, count, offsetX, offsetY, offsetZ, extra);
}
} else if (receiver instanceof Player) {
Object packet;
if (SERVER_IS_1_8) {
packet = PACKET_PARTICLE.newInstance(getEnumParticle(particle), true, (float) x, (float) y,
(float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, datas);
} else {
String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]);
packet = PACKET_PARTICLE.newInstance(particleName, (float) x, (float) y, (float) z,
(float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
}
Object entityPlayer = PLAYER_GET_HANDLE.invoke(receiver);
Object playerConnection = PLAYER_CONNECTION.get(entityPlayer);
SEND_PACKET.invoke(playerConnection, packet);
}
} catch (ReflectiveOperationException e) {
Iris.reportError(e);
throw new RuntimeException(e);
}
}
@Override
public boolean isValidData(Object particle, Object data) {
return true;
}
@Override
public Object getParticle(ParticleType particle) {
if (!SERVER_IS_1_8) {
return particle.getLegacyName();
}
try {
return getEnumParticle(particle);
} catch (IllegalArgumentException e) {
Iris.reportError(e);
return null;
}
}
private Object getEnumParticle(ParticleType particleType) {
return FastReflection.enumValueOf(ENUM_PARTICLE, particleType.toString());
}
private int[] toData(ParticleType particle, Object data) {
Class<?> dataType = particle.getDataType();
if (dataType == ItemStack.class) {
if (!(data instanceof ItemStack itemStack)) {
return SERVER_IS_1_8 ? new int[2] : new int[]{1, 0};
}
return new int[]{itemStack.getType().getId(), itemStack.getDurability()};
}
if (dataType == MaterialData.class) {
if (!(data instanceof MaterialData materialData)) {
return SERVER_IS_1_8 ? new int[1] : new int[]{1, 0};
}
if (SERVER_IS_1_8) {
return new int[]{materialData.getItemType().getId() + (materialData.getData() << 12)};
} else {
return new int[]{materialData.getItemType().getId(), materialData.getData()};
}
}
return EMPTY;
}
}

View File

@@ -1,192 +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.util.particle;
import com.volmit.iris.Iris;
import org.bukkit.Color;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
/**
* @author MrMicky
*/
@SuppressWarnings("deprecation")
public enum ParticleType {
// 1.7+
EXPLOSION_NORMAL("explode", "poof"),
EXPLOSION_LARGE("largeexplode", "explosion"),
EXPLOSION_HUGE("hugeexplosion", "explosion_emitter"),
FIREWORKS_SPARK("fireworksSpark", "firework"),
WATER_BUBBLE("bubble", "bubble"),
WATER_SPLASH("splash", "splash"),
WATER_WAKE("wake", "fishing"),
SUSPENDED("suspended", "underwater"),
SUSPENDED_DEPTH("depthsuspend", "underwater"),
CRIT("crit", "crit"),
CRIT_MAGIC("magicCrit", "enchanted_hit"),
SMOKE_NORMAL("smoke", "smoke"),
SMOKE_LARGE("largesmoke", "large_smoke"),
SPELL("spell", "effect"),
SPELL_INSTANT("instantSpell", "instant_effect"),
SPELL_MOB("mobSpell", "entity_effect"),
SPELL_MOB_AMBIENT("mobSpellAmbient", "ambient_entity_effect"),
SPELL_WITCH("witchMagic", "witch"),
DRIP_WATER("dripWater", "dripping_water"),
DRIP_LAVA("dripLava", "dripping_lava"),
VILLAGER_ANGRY("angryVillager", "angry_villager"),
VILLAGER_HAPPY("happyVillager", "happy_villager"),
TOWN_AURA("townaura", "mycelium"),
NOTE("note", "note"),
PORTAL("portal", "portal"),
ENCHANTMENT_TABLE("enchantmenttable", "enchant"),
FLAME("flame", "flame"),
LAVA("lava", "lava"),
// FOOTSTEP("footstep", null),
CLOUD("cloud", "cloud"),
REDSTONE("reddust", "dust"),
SNOWBALL("snowballpoof", "item_snowball"),
SNOW_SHOVEL("snowshovel", "item_snowball"),
SLIME("slime", "item_slime"),
HEART("heart", "heart"),
ITEM_CRACK("iconcrack", "item"),
BLOCK_CRACK("blockcrack", "block"),
BLOCK_DUST("blockdust", "block"),
// 1.8+
BARRIER("barrier", "barrier", 8),
WATER_DROP("droplet", "rain", 8),
MOB_APPEARANCE("mobappearance", "elder_guardian", 8),
// ITEM_TAKE("take", null, 8),
// 1.9+
DRAGON_BREATH("dragonbreath", "dragon_breath", 9),
END_ROD("endRod", "end_rod", 9),
DAMAGE_INDICATOR("damageIndicator", "damage_indicator", 9),
SWEEP_ATTACK("sweepAttack", "sweep_attack", 9),
// 1.10+
FALLING_DUST("fallingdust", "falling_dust", 10),
// 1.11+
TOTEM("totem", "totem_of_undying", 11),
SPIT("spit", "spit", 11),
// 1.13+
SQUID_INK(13),
BUBBLE_POP(13),
CURRENT_DOWN(13),
BUBBLE_COLUMN_UP(13),
NAUTILUS(13),
DOLPHIN(13),
// 1.14+
SNEEZE(14),
CAMPFIRE_COSY_SMOKE(14),
CAMPFIRE_SIGNAL_SMOKE(14),
COMPOSTER(14),
FLASH(14),
FALLING_LAVA(14),
LANDING_LAVA(14),
FALLING_WATER(14),
// 1.15+
DRIPPING_HONEY(15),
FALLING_HONEY(15),
LANDING_HONEY(15),
FALLING_NECTAR(15);
private static final int SERVER_VERSION_ID;
static {
String ver = FastReflection.VERSION;
SERVER_VERSION_ID = ver.charAt(4) == '_' ? Character.getNumericValue(ver.charAt(3)) : Integer.parseInt(ver.substring(3, 5));
}
private final String legacyName;
private final String name;
private final int minimumVersion;
// 1.7 particles
ParticleType(String legacyName, String name) {
this(legacyName, name, -1);
}
// 1.13+ particles
ParticleType(int minimumVersion) {
this.legacyName = null;
this.name = name().toLowerCase();
this.minimumVersion = minimumVersion;
}
// 1.8-1.12 particles
ParticleType(String legacyName, String name, int minimumVersion) {
this.legacyName = legacyName;
this.name = name;
this.minimumVersion = minimumVersion;
}
public static ParticleType getParticle(String particleName) {
try {
return ParticleType.valueOf(particleName.toUpperCase());
} catch (IllegalArgumentException e) {
Iris.reportError(e);
for (ParticleType particle : values()) {
if (particle.getName().equalsIgnoreCase(particleName)) {
return particle;
}
if (particle.hasLegacyName() && particle.getLegacyName().equalsIgnoreCase(particleName)) {
return particle;
}
}
}
return null;
}
public boolean hasLegacyName() {
return legacyName != null;
}
public String getLegacyName() {
if (!hasLegacyName()) {
throw new IllegalStateException("Particle " + name() + " don't have legacy name");
}
return legacyName;
}
public String getName() {
return name;
}
public boolean isSupported() {
return minimumVersion <= 0 || SERVER_VERSION_ID >= minimumVersion;
}
public Class<?> getDataType() {
return switch (this) {
case ITEM_CRACK -> ItemStack.class;
case BLOCK_CRACK, BLOCK_DUST, FALLING_DUST ->
//noinspection deprecation
MaterialData.class;
case REDSTONE -> Color.class;
default -> Void.class;
};
}
}

View File

@@ -264,7 +264,7 @@ public class VolmitSender implements CommandSender {
private Component createNoPrefixComponent(String message) {
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(message));
return MiniMessage.miniMessage().deserialize(t);
return MiniMessage.miniMessage().deserialize(C.mini(t));
}
String t = C.translateAlternateColorCodes('&', message);
@@ -273,13 +273,13 @@ public class VolmitSender implements CommandSender {
}
private Component createNoPrefixComponentNoProcessing(String message) {
return MiniMessage.builder().postProcessor(c -> c).build().deserialize(message);
return MiniMessage.builder().postProcessor(c -> c).build().deserialize(C.mini(message));
}
private Component createComponent(String message) {
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message));
return MiniMessage.miniMessage().deserialize(t);
return MiniMessage.miniMessage().deserialize(C.mini(t));
}
String t = C.translateAlternateColorCodes('&', getTag() + message);
@@ -290,11 +290,11 @@ public class VolmitSender implements CommandSender {
private Component createComponentRaw(String message) {
if (!IrisSettings.get().getGeneral().canUseCustomColors(this)) {
String t = C.translateAlternateColorCodes('&', MiniMessage.miniMessage().stripTags(getTag() + message));
return MiniMessage.miniMessage().deserialize(t);
return MiniMessage.miniMessage().deserialize(C.mini(t));
}
String t = C.translateAlternateColorCodes('&', getTag() + message);
return MiniMessage.miniMessage().deserialize(t);
return MiniMessage.miniMessage().deserialize(C.mini(t));
}
public <T> void showWaiting(String passive, CompletableFuture<T> f) {

View File

@@ -36,7 +36,6 @@ public abstract class Looper extends Thread {
//noinspection BusyWait
Thread.sleep(m);
} catch (InterruptedException e) {
Iris.reportError(e);
break;
} catch (Throwable e) {
Iris.reportError(e);

View File

@@ -0,0 +1,41 @@
package com.volmit.iris.util.sentry;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.volmit.iris.util.collection.KMap;
import io.sentry.Attachment;
import org.bukkit.Bukkit;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Callable;
public class Attachments {
private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();
public static final Attachment PLUGINS = jsonProvider(Attachments::plugins, "plugins.json");
public static Attachment json(Object object, String name) {
return new Attachment(GSON.toJson(object).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true);
}
public static Attachment jsonProvider(Callable<Object> object, String name) {
return new Attachment(() -> GSON.toJson(object.call()).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true);
}
private static KMap<String, Object> plugins() {
KMap<String, String> enabled = new KMap<>();
KMap<String, String> disabled = new KMap<>();
var pm = Bukkit.getPluginManager();
for (var plugin : pm.getPlugins()) {
if (plugin.isEnabled()) {
enabled.put(plugin.getName(), plugin.getDescription().getVersion());
} else {
disabled.put(plugin.getName(), plugin.getDescription().getVersion());
}
}
return new KMap<String, Object>()
.qput("enabled", enabled)
.qput("disabled", disabled);
}
}

View File

@@ -0,0 +1,47 @@
package com.volmit.iris.util.sentry;
import com.volmit.iris.Iris;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.PrintWriter;
import java.io.StringWriter;
public class IrisLogger implements ILogger {
@Override
public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args) {
Iris.msg(String.format("%s: %s", level, String.format(message, args)));
}
@Override
public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable) {
if (throwable == null) {
log(level, message);
} else {
Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable)));
}
}
@Override
public void log(@NotNull SentryLevel level, @Nullable Throwable throwable, @NotNull String message, @Nullable Object... args) {
if (throwable == null) {
log(level, message, args);
} else {
Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable)));
}
}
@Override
public boolean isEnabled(@Nullable SentryLevel level) {
return true;
}
private @NotNull String captureStackTrace(@NotNull Throwable throwable) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
throwable.printStackTrace(printWriter);
return stringWriter.toString();
}
}

View File

@@ -0,0 +1,59 @@
package com.volmit.iris.util.sentry;
import com.volmit.iris.util.io.IO;
import io.sentry.protocol.User;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import oshi.SystemInfo;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class ServerID {
public static final String ID = calculate();
public static User asUser() {
User u = new User();
u.setId(ID);
return u;
}
@SneakyThrows
private static String calculate() {
Digest md = new Digest();
md.update(System.getProperty("java.vm.name"));
md.update(System.getProperty("java.vm.version"));
md.update(new SystemInfo().getHardware().getProcessor().toString());
md.update(Runtime.getRuntime().maxMemory());
for (var p : Bukkit.getPluginManager().getPlugins()) {
md.update(p.getName());
}
return IO.bytesToHex(md.digest());
}
private static final class Digest {
private final MessageDigest md = MessageDigest.getInstance("SHA-256");
private final byte[] buffer = new byte[8];
private final ByteBuffer wrapped = ByteBuffer.wrap(buffer);
private Digest() throws NoSuchAlgorithmException {
}
public void update(String string) {
if (string == null) return;
md.update(string.getBytes(StandardCharsets.UTF_8));
}
public void update(long Long) {
wrapped.putLong(0, Long);
md.update(buffer, 0, 8);
}
public byte[] digest() {
return md.digest();
}
}
}

View File

@@ -23,5 +23,5 @@ libraries:
commands:
iris:
aliases: [ ir, irs ]
api-version: '${apiversion}'
api-version: '${apiVersion}'
hotload-dependencies: false