Merge branch 'dev' into feat/folia

# Conflicts:
#	core/build.gradle.kts
#	core/src/main/java/com/volmit/iris/Iris.java
#	core/src/main/java/com/volmit/iris/core/commands/CommandIris.java
#	gradle/libs.versions.toml
This commit is contained in:
Julian Krings 2025-08-02 21:25:40 +02:00
commit bd722fdacb
No known key found for this signature in database
GPG Key ID: 208C6E08C3B718D2
27 changed files with 538 additions and 434 deletions

View File

@ -1,4 +1,4 @@
import io.github.slimjar.func.slimjar import io.github.slimjar.func.slimjarHelper
import io.github.slimjar.resolver.data.Mirror import io.github.slimjar.resolver.data.Mirror
import org.ajoberstar.grgit.Grgit import org.ajoberstar.grgit.Grgit
import java.net.URI import java.net.URI
@ -67,7 +67,7 @@ dependencies {
compileOnly(libs.multiverseCore) compileOnly(libs.multiverseCore)
// Shaded // Shaded
implementation(slimjar()) implementation(slimjarHelper("spigot"))
implementation(rootProject.libs.platformUtils) { implementation(rootProject.libs.platformUtils) {
isTransitive = false isTransitive = false
} }
@ -161,6 +161,7 @@ tasks {
mergeServiceFiles() mergeServiceFiles()
//minimize() //minimize()
relocate("io.github.slimjar", "$lib.slimjar") relocate("io.github.slimjar", "$lib.slimjar")
exclude("modules/loader-agent.isolated-jar")
} }
} }

View File

@ -22,6 +22,7 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.ServerConfigurator;
import com.volmit.iris.core.link.IrisPapiExpansion; import com.volmit.iris.core.link.IrisPapiExpansion;
import com.volmit.iris.core.link.MultiverseCoreLink; import com.volmit.iris.core.link.MultiverseCoreLink;
@ -67,9 +68,6 @@ import org.bukkit.*;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.*; import org.bukkit.event.*;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
@ -84,6 +82,7 @@ import java.lang.annotation.Annotation;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -439,8 +438,7 @@ public class Iris extends VolmitPlugin implements Listener {
public Iris() { public Iris() {
instance = this; instance = this;
SlimJar.debug(IrisSettings.get().getSentry().isDebug()); SlimJar.load();
SlimJar.load(getDataFolder("cache", "libraries"));
} }
private void enable() { private void enable() {
@ -471,48 +469,34 @@ public class Iris extends VolmitPlugin implements Listener {
IrisSafeguard.splash(false); IrisSafeguard.splash(false);
autoStartStudio(); autoStartStudio();
checkForBukkitWorlds(); checkForBukkitWorlds(s -> true);
IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName());
IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName());
}); });
} }
private void checkForBukkitWorlds() { public void checkForBukkitWorlds(Predicate<String> filter) {
FileConfiguration fc = new YamlConfiguration();
try { try {
fc.load(new File("bukkit.yml")); IrisWorlds.readBukkitWorlds().forEach((s, generator) -> {
ConfigurationSection section = fc.getConfigurationSection("worlds"); try {
if (section == null) { if (Bukkit.getWorld(s) != null || !filter.test(s)) return;
return;
}
for (String s : section.getKeys(false)) { Iris.info("Loading World: %s | Generator: %s", s, generator);
ConfigurationSection entry = section.getConfigurationSection(s); var gen = getDefaultWorldGenerator(s, generator);
if (!entry.contains("generator", true)) { var dim = loadDimension(s, generator);
continue; assert dim != null && gen != null;
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
WorldCreator c = new WorldCreator(s)
.generator(gen)
.environment(dim.getEnvironment());
INMS.get().createWorld(c);
Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!");
} catch (Throwable e) {
Iris.error("Failed to load world " + s + "!");
e.printStackTrace();
} }
});
String generator = entry.getString("generator");
if (generator.startsWith("Iris:")) {
generator = generator.split("\\Q:\\E")[1];
} else if (generator.equalsIgnoreCase("Iris")) {
generator = IrisSettings.get().getGenerator().getDefaultWorldType();
} else {
continue;
}
if (Bukkit.getWorld(s) != null)
continue;
Iris.info("Loading World: %s | Generator: %s", s, generator);
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
WorldCreator c = new WorldCreator(s)
.generator(getDefaultWorldGenerator(s, generator))
.environment(IrisData.loadAnyDimension(generator).getEnvironment());
INMS.get().createWorld(c);
Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!");
}
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
reportError(e); reportError(e);

View File

@ -23,7 +23,6 @@ import com.volmit.iris.Iris;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONException; import com.volmit.iris.util.json.JSONException;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.misc.SlimJar;
import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.misc.getHardware;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -87,7 +86,6 @@ public class IrisSettings {
Iris.error("Configuration Error in settings.json! " + ee.getClass().getSimpleName() + ": " + ee.getMessage()); Iris.error("Configuration Error in settings.json! " + ee.getClass().getSimpleName() + ": " + ee.getMessage());
} }
} }
SlimJar.debug(settings.general.debug);
return settings; return settings;
} }

View File

@ -4,10 +4,14 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.io.IO; import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.misc.ServerProperties;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -25,6 +29,7 @@ public class IrisWorlds {
private IrisWorlds(KMap<String, String> worlds) { private IrisWorlds(KMap<String, String> worlds) {
this.worlds = worlds; this.worlds = worlds;
readBukkitWorlds().forEach(this::put0);
save(); save();
} }
@ -50,14 +55,33 @@ public class IrisWorlds {
} }
public void put(String name, String type) { public void put(String name, String type) {
String old = worlds.put(name, type); put0(name, type);
if (!type.equals(old))
dirty = true;
save(); save();
} }
public Stream<File> getFolders() { private void put0(String name, String type) {
return worlds.keySet().stream().map(k -> new File(Bukkit.getWorldContainer(), k)); String old = worlds.put(name, type);
if (!type.equals(old))
dirty = true;
}
public KMap<String, String> getWorlds() {
return readBukkitWorlds().put(worlds);
}
public Stream<IrisData> getPacks() {
return getDimensions()
.map(IrisDimension::getLoader)
.filter(Objects::nonNull);
}
public Stream<IrisDimension> getDimensions() {
return readBukkitWorlds()
.put(worlds)
.entrySet()
.stream()
.map(entry -> Iris.loadDimension(entry.getKey(), entry.getValue()))
.filter(Objects::nonNull);
} }
public void clean() { public void clean() {
@ -76,4 +100,27 @@ public class IrisWorlds {
Iris.reportError(e); Iris.reportError(e);
} }
} }
public static KMap<String, String> readBukkitWorlds() {
var bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
var worlds = bukkit.getConfigurationSection("worlds");
if (worlds == null) return new KMap<>();
var result = new KMap<String, String>();
for (String world : worlds.getKeys(false)) {
var gen = worlds.getString(world + ".generator");
if (gen == null) continue;
String loadKey;
if (gen.equalsIgnoreCase("iris")) {
loadKey = IrisSettings.get().getGenerator().getDefaultWorldType();
} else if (gen.startsWith("Iris:")) {
loadKey = gen.substring(5);
} else continue;
result.put(world, loadKey);
}
return result;
}
} }

View File

@ -240,14 +240,13 @@ public class ServerConfigurator {
} }
public static Stream<IrisData> allPacks() { public static Stream<IrisData> allPacks() {
return Stream.concat(listFiles(Iris.instance.getDataFolder("packs")), return Stream.concat(listFiles(Iris.instance.getDataFolder("packs"))
IrisWorlds.get().getFolders().map(w -> new File(w, "iris/pack")))
.filter(File::isDirectory) .filter(File::isDirectory)
.filter( base -> { .filter( base -> {
var content = new File(base, "dimensions").listFiles(); var content = new File(base, "dimensions").listFiles();
return content != null && content.length > 0; return content != null && content.length > 0;
}) })
.map(IrisData::get); .map(IrisData::get), IrisWorlds.get().getPacks());
} }
@Nullable @Nullable

View File

@ -20,15 +20,12 @@ package com.volmit.iris.core.commands;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.pregenerator.ChunkUpdater; import com.volmit.iris.core.pregenerator.ChunkUpdater;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.engine.object.IrisWorld;
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeExecutor;
import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.DecreeOrigin;
@ -44,20 +41,16 @@ import com.volmit.iris.util.scheduling.J;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.generator.ChunkGenerator;
import java.io.*; import java.io.*;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static com.volmit.iris.Iris.service;
import static com.volmit.iris.core.service.EditSVC.deletingWorld; import static com.volmit.iris.core.service.EditSVC.deletingWorld;
import static com.volmit.iris.util.misc.ServerProperties.BUKKIT_YML;
import static org.bukkit.Bukkit.getServer; import static org.bukkit.Bukkit.getServer;
@Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") @Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command")
@ -129,6 +122,7 @@ public class CommandIris implements DecreeExecutor {
} }
worldCreation = false; worldCreation = false;
sender().sendMessage(C.GREEN + "Successfully created your world!"); sender().sendMessage(C.GREEN + "Successfully created your world!");
if (main) sender().sendMessage(C.GREEN + "Your world will automatically be set as the main world when the server restarts.");
} }
@SneakyThrows @SneakyThrows
@ -388,17 +382,19 @@ public class CommandIris implements DecreeExecutor {
sender().sendMessage(C.GREEN + "Set debug to: " + to); sender().sendMessage(C.GREEN + "Set debug to: " + to);
} }
//TODO fix pack trimming
@Decree(description = "Download a project.", aliases = "dl") @Decree(description = "Download a project.", aliases = "dl")
public void download( public void download(
@Param(name = "pack", description = "The pack to download", defaultValue = "overworld", aliases = "project") @Param(name = "pack", description = "The pack to download", defaultValue = "overworld", aliases = "project")
String pack, String pack,
@Param(name = "branch", description = "The branch to download from", defaultValue = "main") @Param(name = "branch", description = "The branch to download from", defaultValue = "main")
String branch, String branch,
@Param(name = "trim", description = "Whether or not to download a trimmed version (do not enable when editing)", defaultValue = "false") //@Param(name = "trim", description = "Whether or not to download a trimmed version (do not enable when editing)", defaultValue = "false")
boolean trim, //boolean trim,
@Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false") @Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false")
boolean overwrite boolean overwrite
) { ) {
boolean trim = false;
sender().sendMessage(C.GREEN + "Downloading pack: " + pack + "/" + branch + (trim ? " trimmed" : "") + (overwrite ? " overwriting" : "")); sender().sendMessage(C.GREEN + "Downloading pack: " + pack + "/" + branch + (trim ? " trimmed" : "") + (overwrite ? " overwriting" : ""));
if (pack.equals("overworld")) { if (pack.equals("overworld")) {
String url = "https://github.com/IrisDimensions/overworld/releases/download/" + INMS.OVERWORLD_TAG + "/overworld.zip"; String url = "https://github.com/IrisDimensions/overworld/releases/download/" + INMS.OVERWORLD_TAG + "/overworld.zip";
@ -496,7 +492,6 @@ public class CommandIris implements DecreeExecutor {
return; return;
} }
File BUKKIT_YML = new File("bukkit.yml");
String pathtodim = world + File.separator +"iris"+File.separator +"pack"+File.separator +"dimensions"+File.separator; String pathtodim = world + File.separator +"iris"+File.separator +"pack"+File.separator +"dimensions"+File.separator;
File directory = new File(Bukkit.getWorldContainer(), pathtodim); File directory = new File(Bukkit.getWorldContainer(), pathtodim);
@ -534,7 +529,7 @@ public class CommandIris implements DecreeExecutor {
return; return;
} }
} }
checkForBukkitWorlds(world); Iris.instance.checkForBukkitWorlds(world::equals);
sender().sendMessage(C.GREEN + world + " loaded successfully."); sender().sendMessage(C.GREEN + world + " loaded successfully.");
} }
@Decree(description = "Evacuate an iris world", origin = DecreeOrigin.PLAYER, sync = true) @Decree(description = "Evacuate an iris world", origin = DecreeOrigin.PLAYER, sync = true)
@ -555,85 +550,4 @@ public class CommandIris implements DecreeExecutor {
File worldDirectory = new File(worldContainer, worldName); File worldDirectory = new File(worldContainer, worldName);
return worldDirectory.exists() && worldDirectory.isDirectory(); return worldDirectory.exists() && worldDirectory.isDirectory();
} }
private void checkForBukkitWorlds(String world) {
FileConfiguration fc = new YamlConfiguration();
try {
fc.load(new File("bukkit.yml"));
ConfigurationSection section = fc.getConfigurationSection("worlds");
if (section == null) {
return;
}
List<String> worldsToLoad = Collections.singletonList(world);
for (String s : section.getKeys(false)) {
if (!worldsToLoad.contains(s)) {
continue;
}
ConfigurationSection entry = section.getConfigurationSection(s);
if (!entry.contains("generator", true)) {
continue;
}
String generator = entry.getString("generator");
if (generator.startsWith("Iris:")) {
generator = generator.split("\\Q:\\E")[1];
} else if (generator.equalsIgnoreCase("Iris")) {
generator = IrisSettings.get().getGenerator().getDefaultWorldType();
} else {
continue;
}
Iris.info("2 World: %s | Generator: %s", s, generator);
if (Bukkit.getWorlds().stream().anyMatch(w -> w.getName().equals(s))) {
continue;
}
Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "...");
WorldCreator c = new WorldCreator(s)
.generator(getDefaultWorldGenerator(s, generator))
.environment(IrisData.loadAnyDimension(generator).getEnvironment());
INMS.get().createWorld(c);
Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!");
}
} catch (Throwable e) {
e.printStackTrace();
}
}
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id);
IrisDimension dim;
if (id == null || id.isEmpty()) {
dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType());
} else {
dim = IrisData.loadAnyDimension(id);
}
Iris.debug("Generator ID: " + id + " requested by bukkit/plugin");
if (dim == null) {
Iris.warn("Unable to find dimension type " + id + " Looking for online packs...");
service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true);
dim = IrisData.loadAnyDimension(id);
if (dim == null) {
throw new RuntimeException("Can't find dimension " + id + "!");
} else {
Iris.info("Resolved missing dimension, proceeding with generation.");
}
}
Iris.debug("Assuming IrisDimension: " + dim.getName());
IrisWorld w = IrisWorld.builder()
.name(worldName)
.seed(1337)
.environment(dim.getEnvironment())
.worldFolder(new File(Bukkit.getWorldContainer(), worldName))
.minHeight(dim.getMinHeight())
.maxHeight(dim.getMaxHeight())
.build();
Iris.debug("Generator Config: " + w.toString());
File ff = new File(w.worldFolder(), "iris/pack");
if (!ff.exists() || ff.listFiles().length == 0) {
ff.mkdirs();
service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile());
}
return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey());
}
} }

View File

@ -92,18 +92,19 @@ public class CommandStudio implements DecreeExecutor {
return duration.toString().substring(2).replaceAll("(\\d[HMS])(?!$)", "$1 ").toLowerCase(); return duration.toString().substring(2).replaceAll("(\\d[HMS])(?!$)", "$1 ").toLowerCase();
} }
//TODO fix pack trimming
@Decree(description = "Download a project.", aliases = "dl") @Decree(description = "Download a project.", aliases = "dl")
public void download( public void download(
@Param(name = "pack", description = "The pack to download", defaultValue = "overworld", aliases = "project") @Param(name = "pack", description = "The pack to download", defaultValue = "overworld", aliases = "project")
String pack, String pack,
@Param(name = "branch", description = "The branch to download from", defaultValue = "master") @Param(name = "branch", description = "The branch to download from", defaultValue = "master")
String branch, String branch,
@Param(name = "trim", description = "Whether or not to download a trimmed version (do not enable when editing)", defaultValue = "false") //@Param(name = "trim", description = "Whether or not to download a trimmed version (do not enable when editing)", defaultValue = "false")
boolean trim, //boolean trim,
@Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false") @Param(name = "overwrite", description = "Whether or not to overwrite the pack with the downloaded one", aliases = "force", defaultValue = "false")
boolean overwrite boolean overwrite
) { ) {
new CommandIris().download(pack, branch, trim, overwrite); new CommandIris().download(pack, branch, overwrite);
} }
@Decree(description = "Open a new studio world", aliases = "o", sync = true) @Decree(description = "Open a new studio world", aliases = "o", sync = true)

View File

@ -36,7 +36,7 @@ import com.volmit.iris.util.format.C;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.reflect.OldEnum; import com.volmit.iris.util.reflect.KeyedType;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.Data; import lombok.Data;
@ -341,14 +341,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
this.imageLoader = registerLoader(IrisImage.class); this.imageLoader = registerLoader(IrisImage.class);
this.scriptLoader = registerLoader(IrisScript.class); this.scriptLoader = registerLoader(IrisScript.class);
this.matterObjectLoader = registerLoader(IrisMatterObject.class); this.matterObjectLoader = registerLoader(IrisMatterObject.class);
if (OldEnum.exists()) { builder.registerTypeAdapterFactory(KeyedType::createTypeAdapter);
builder.registerTypeAdapterFactory(new TypeAdapterFactory() {
@Override
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
return (TypeAdapter<T>) OldEnum.create(type.getRawType());
}
});
}
gson = builder.create(); gson = builder.create();
} }

View File

@ -31,7 +31,7 @@ import com.volmit.iris.util.collection.KMap;
import com.volmit.iris.util.data.B; import com.volmit.iris.util.data.B;
import com.volmit.iris.util.json.JSONArray; import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.reflect.OldEnum; import com.volmit.iris.util.reflect.KeyedType;
import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.Enchantment;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -336,10 +336,10 @@ public class SchemaBuilder {
prop.put("$ref", "#/definitions/" + key); prop.put("$ref", "#/definitions/" + key);
description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)"); description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)");
} else if (KeyedType.isKeyed(k.getType())) {
fancyType = addEnum(k.getType(), prop, description, KeyedType.values(k.getType()), Function.identity());
} else if (k.getType().isEnum()) { } else if (k.getType().isEnum()) {
fancyType = addEnum(k.getType(), prop, description, k.getType().getEnumConstants(), o -> ((Enum<?>) o).name()); fancyType = addEnum(k.getType(), prop, description, k.getType().getEnumConstants(), o -> ((Enum<?>) o).name());
} else if (OldEnum.isOldEnum(k.getType())) {
fancyType = addEnum(k.getType(), prop, description, OldEnum.values(k.getType()), OldEnum::name);
} }
} }
case "object" -> { case "object" -> {
@ -504,10 +504,10 @@ public class SchemaBuilder {
items.put("$ref", "#/definitions/" + key); items.put("$ref", "#/definitions/" + key);
prop.put("items", items); prop.put("items", items);
description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)"); description.add(SYMBOL_TYPE__N + " Must be a valid Potion Effect Type (use ctrl+space for auto complete!)");
} else if (KeyedType.isKeyed(t.type())) {
fancyType = addEnumList(prop, description, t, KeyedType.values(t.type()), Function.identity());
} else if (t.type().isEnum()) { } else if (t.type().isEnum()) {
fancyType = addEnumList(prop, description, t, t.type().getEnumConstants(), o -> ((Enum<?>) o).name()); fancyType = addEnumList(prop, description, t, t.type().getEnumConstants(), o -> ((Enum<?>) o).name());
} else if (OldEnum.isOldEnum(t.type())) {
fancyType = addEnumList(prop, description, t, OldEnum.values(t.type()), OldEnum::name);
} }
} }
} }
@ -548,7 +548,7 @@ public class SchemaBuilder {
if (value instanceof List) { if (value instanceof List) {
d.add(" "); d.add(" ");
d.add("* Default Value is an empty list"); d.add("* Default Value is an empty list");
} else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !OldEnum.isOldEnum(cl)) { } else if (!cl.isPrimitive() && !(value instanceof Number) && !(value instanceof String) && !(cl.isEnum()) && !KeyedType.isKeyed(cl)) {
d.add(" "); d.add(" ");
d.add("* Default Value is a default object (create this object to see default properties)"); d.add("* Default Value is a default object (create this object to see default properties)");
} else { } else {
@ -596,7 +596,7 @@ public class SchemaBuilder {
} }
@NotNull @NotNull
private String addEnumList(JSONObject prop, KList<String> description, ArrayType t, Object[] values, Function<Object, String> function) { private <T> String addEnumList(JSONObject prop, KList<String> description, ArrayType t, T[] values, Function<T, String> function) {
JSONObject items = new JSONObject(); JSONObject items = new JSONObject();
var s = addEnum(t.type(), items, description, values, function); var s = addEnum(t.type(), items, description, values, function);
prop.put("items", items); prop.put("items", items);
@ -605,10 +605,10 @@ public class SchemaBuilder {
} }
@NotNull @NotNull
private String addEnum(Class<?> type, JSONObject prop, KList<String> description, Object[] values, Function<Object, String> function) { private <T> String addEnum(Class<?> type, JSONObject prop, KList<String> description, T[] values, Function<T, String> function) {
JSONArray a = new JSONArray(); JSONArray a = new JSONArray();
boolean advanced = type.isAnnotationPresent(Desc.class); boolean advanced = type.isAnnotationPresent(Desc.class);
for (Object gg : values) { for (T gg : values) {
if (advanced) { if (advanced) {
try { try {
JSONObject j = new JSONObject(); JSONObject j = new JSONObject();
@ -652,7 +652,7 @@ public class SchemaBuilder {
return "boolean"; return "boolean";
} }
if (c.equals(String.class) || c.isEnum() || OldEnum.isOldEnum(c) || c.equals(Enchantment.class) || c.equals(PotionEffectType.class)) { if (c.equals(String.class) || c.isEnum() || KeyedType.isKeyed(c)) {
return "string"; return "string";
} }

View File

@ -1,15 +1,13 @@
package com.volmit.iris.core.safeguard; package com.volmit.iris.core.safeguard;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisWorlds;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.nms.v1X.NMSBinding1X;
import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisDimension;
import com.volmit.iris.util.agent.Agent; import com.volmit.iris.util.agent.Agent;
import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.misc.ServerProperties;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
@ -21,6 +19,7 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.*; import java.util.*;
import java.util.stream.Collectors;
import static com.volmit.iris.Iris.getJavaVersion; import static com.volmit.iris.Iris.getJavaVersion;
import static com.volmit.iris.core.safeguard.IrisSafeguard.*; import static com.volmit.iris.core.safeguard.IrisSafeguard.*;
@ -187,27 +186,9 @@ public class ServerBootSFG {
} }
private static KSet<String> getDimensionTypes() { private static KSet<String> getDimensionTypes() {
var bukkit = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML); return IrisWorlds.get()
var worlds = bukkit.getConfigurationSection("worlds"); .getDimensions()
if (worlds == null) return new KSet<>(); .map(IrisDimension::getDimensionTypeKey)
.collect(Collectors.toCollection(KSet::new));
var types = new KSet<String>();
for (String world : worlds.getKeys(false)) {
var gen = worlds.getString(world + ".generator");
if (gen == null) continue;
String loadKey;
if (gen.equalsIgnoreCase("iris")) {
loadKey = IrisSettings.get().getGenerator().getDefaultWorldType();
} else if (gen.startsWith("Iris:")) {
loadKey = gen.substring(5);
} else continue;
IrisDimension dimension = Iris.loadDimension(world, loadKey);
if (dimension == null) continue;
types.add(dimension.getDimensionTypeKey());
}
return types;
} }
} }

View File

@ -46,13 +46,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier; import java.util.function.Supplier;
import static com.volmit.iris.util.misc.ServerProperties.BUKKIT_YML;
/** /**
* Makes it a lot easier to setup an engine, world, studio or whatever * Makes it a lot easier to setup an engine, world, studio or whatever
*/ */
@Data @Data
@Accessors(fluent = true, chain = true) @Accessors(fluent = true, chain = true)
public class IrisCreator { public class IrisCreator {
private static final File BUKKIT_YML = new File("bukkit.yml");
/** /**
* Specify an area to pregenerate during creation * Specify an area to pregenerate during creation
*/ */

View File

@ -21,13 +21,10 @@ package com.volmit.iris.core.tools;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.engine.object.*; import com.volmit.iris.engine.object.*;
import com.volmit.iris.engine.platform.BukkitChunkGenerator; import com.volmit.iris.engine.platform.BukkitChunkGenerator;
import com.volmit.iris.util.reflect.WrappedField;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.WorldCreator; import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import sun.misc.Unsafe;
import java.io.File; import java.io.File;

View File

@ -47,6 +47,7 @@ import com.volmit.iris.util.mantle.MantleFlag;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.MatterStructurePOI; import com.volmit.iris.util.matter.MatterStructurePOI;
import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
@ -224,6 +225,12 @@ public class IrisEngine implements Engine {
return getMantle().getJigsawComponent().guess(x, z); return getMantle().getJigsawComponent().guess(x, z);
} }
@Override
public IrisJigsawStructure getStructureAt(int x, int y, int z) {
var container = getMantle().getMantle().get(x, y, z, JigsawStructureContainer.class);
return container == null ? null : container.load(getData());
}
private void warmupChunk(int x, int z) { private void warmupChunk(int x, int z) {
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) { for (int j = 0; j < 16; j++) {

View File

@ -230,6 +230,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@ChunkCoordinates @ChunkCoordinates
IrisJigsawStructure getStructureAt(int x, int z); IrisJigsawStructure getStructureAt(int x, int z);
@BlockCoordinates
IrisJigsawStructure getStructureAt(int x, int y, int z);
@BlockCoordinates @BlockCoordinates
default IrisBiome getCaveBiome(int x, int z) { default IrisBiome getCaveBiome(int x, int z) {
return getComplex().getCaveBiomeStream().get(x, z); return getComplex().getCaveBiomeStream().get(x, z);

View File

@ -29,6 +29,7 @@ import com.volmit.iris.util.mantle.Mantle;
import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer; import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer;
import com.volmit.iris.util.matter.slices.container.JigsawStructureContainer;
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import lombok.Data; import lombok.Data;
@ -149,11 +150,13 @@ public class PlannedStructure {
} }
int id = rng.i(0, Integer.MAX_VALUE); int id = rng.i(0, Integer.MAX_VALUE);
JigsawPieceContainer container = JigsawPieceContainer.toContainer(i.getPiece()); JigsawPieceContainer piece = JigsawPieceContainer.toContainer(i.getPiece());
JigsawStructureContainer structure = JigsawStructureContainer.toContainer(getStructure());
i.setRealPositions(xx, height, zz, placer); i.setRealPositions(xx, height, zz, placer);
return v.place(xx, height, zz, placer, options, rng, (b, data) -> { return v.place(xx, height, zz, placer, options, rng, (b, data) -> {
e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id); e.set(b.getX(), b.getY(), b.getZ(), v.getLoadKey() + "@" + id);
e.set(b.getX(), b.getY(), b.getZ(), container); e.set(b.getX(), b.getY(), b.getZ(), structure);
e.set(b.getX(), b.getY(), b.getZ(), piece);
}, null, getData().getEngine() != null ? getData() : eng.getData()) != -1; }, null, getData().getEngine() != null ? getData() : eng.getData()) != -1;
} }

View File

@ -19,6 +19,7 @@
package com.volmit.iris.util.collection; package com.volmit.iris.util.collection;
import com.google.common.util.concurrent.AtomicDoubleArray; import com.google.common.util.concurrent.AtomicDoubleArray;
import com.volmit.iris.util.function.NastyFunction;
import com.volmit.iris.util.json.JSONArray; import com.volmit.iris.util.json.JSONArray;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
@ -306,6 +307,18 @@ public class KList<T> extends ArrayList<T> implements List<T> {
return v; return v;
} }
/**
* Convert this list into another list type. Such as GList<Integer> to
* GList<String>. list.convertNasty((i) -> "" + i);
*/
public <V> KList<V> convertNasty(NastyFunction<T, V> converter) throws Throwable {
KList<V> v = new KList<V>(size());
for (final var t : this) {
v.addNonNull(converter.run(t));
}
return v;
}
public KList<T> removeWhere(Predicate<T> t) { public KList<T> removeWhere(Predicate<T> t) {
for (T i : copy()) { for (T i : copy()) {
if (t.test(i)) { if (t.test(i)) {

View File

@ -0,0 +1,137 @@
package com.volmit.iris.util.data.registry;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public interface KeyedRegistry<T> {
@NonNull Map<NamespacedKey, T> map();
@Nullable T get(@NonNull NamespacedKey key);
@Nullable NamespacedKey keyOf(@NonNull T value);
default boolean isEmpty() {
return map().isEmpty();
}
@NonNull
default Optional<T> find(@NonNull String @NonNull ... keys) {
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key");
for (final String key : keys) {
final T t = get(NamespacedKey.minecraft(key));
if (t != null) {
return Optional.of(t);
}
}
return Optional.empty();
}
@NonNull
default Optional<T> find(@NonNull NamespacedKey @NonNull ... keys) {
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key");
for (final NamespacedKey key : keys) {
final T t = get(key);
if (t != null) {
return Optional.of(t);
}
}
return Optional.empty();
}
@Contract(value = "null -> null; !null -> new", pure = true)
static <T> KeyedRegistry<T> wrapped(Map<NamespacedKey, T> map) {
if (map == null) return null;
return new MappedRegistry<>(map);
}
@Contract(value = "null -> null; !null -> new", pure = true)
static <T extends Keyed> KeyedRegistry<T> wrapped(Registry<T> registry) {
if (registry == null) return null;
return new BukkitRegistry<>(registry);
}
@Contract(value = "_ -> new", pure = true)
static <T> KeyedRegistry<T> wrapped(@NonNull Collection<@NonNull KeyedRegistry<T>> registries) {
return new CompoundRegistry<>(registries);
}
record MappedRegistry<T>(Map<NamespacedKey, T> map) implements KeyedRegistry<T> {
@Override
public @Nullable T get(@NonNull NamespacedKey key) {
return map.get(key);
}
@Override
public @Nullable NamespacedKey keyOf(@NonNull T value) {
return map.entrySet().stream()
.filter(e -> e.getValue().equals(value))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
}
}
record BukkitRegistry<T extends Keyed>(Registry<T> registry) implements KeyedRegistry<T> {
@Override
public @NonNull Map<NamespacedKey, T> map() {
return registry.stream().collect(Collectors.toMap(Keyed::getKey, Function.identity()));
}
@Override
public @Nullable T get(@NonNull NamespacedKey key) {
return registry.get(key);
}
@Override
public @NonNull NamespacedKey keyOf(@NonNull T value) {
return value.getKey();
}
}
record CompoundRegistry<T>(Collection<KeyedRegistry<T>> registries) implements KeyedRegistry<T> {
@Override
public @NonNull Map<NamespacedKey, T> map() {
final KMap<NamespacedKey, T> m = new KMap<>();
for (final KeyedRegistry<T> registry : registries) {
m.put(registry.map());
}
return m;
}
@Override
public @Nullable T get(@NonNull NamespacedKey key) {
for (final KeyedRegistry<T> registry : registries) {
final T t = registry.get(key);
if (t != null) {
return t;
}
}
return null;
}
@Override
public @Nullable NamespacedKey keyOf(@NonNull T value) {
for (final KeyedRegistry<T> registry : registries) {
final NamespacedKey key = registry.keyOf(value);
if (key != null) {
return key;
}
}
return null;
}
@Override
public boolean isEmpty() {
return registries.isEmpty() || registries.stream().allMatch(KeyedRegistry::isEmpty);
}
}
}

View File

@ -0,0 +1,42 @@
package com.volmit.iris.util.data.registry;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import lombok.NonNull;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
public class RegistryTypeAdapter<T> extends TypeAdapter<T> {
private final KeyedRegistry<T> registry;
private RegistryTypeAdapter(KeyedRegistry<T> type) {
this.registry = type;
}
@Nullable
public static <T> RegistryTypeAdapter<T> of(@NonNull Class<T> type) {
final var registry = RegistryUtil.lookup(type);
return registry.isEmpty() ? null : new RegistryTypeAdapter<>(registry);
}
@Override
public void write(JsonWriter out, T value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
final var key = registry.keyOf(value);
if (key == null) out.nullValue();
else out.value(key.toString());
}
@Override
public T read(JsonReader in) throws IOException {
final NamespacedKey key = NamespacedKey.fromString(in.nextString());
return key == null ? null : registry.get(key);
}
}

View File

@ -2,6 +2,8 @@ package com.volmit.iris.util.data.registry;
import com.volmit.iris.core.nms.container.Pair; import com.volmit.iris.core.nms.container.Pair;
import com.volmit.iris.engine.data.cache.AtomicCache; import com.volmit.iris.engine.data.cache.AtomicCache;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.collection.KMap;
import lombok.NonNull; import lombok.NonNull;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Keyed; import org.bukkit.Keyed;
@ -13,116 +15,78 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Arrays; import java.util.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static com.volmit.iris.util.data.registry.KeyedRegistry.wrapped;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class RegistryUtil { public class RegistryUtil {
private static final AtomicCache<RegistryLookup> registryLookup = new AtomicCache<>(); private static final AtomicCache<RegistryLookup> registryLookup = new AtomicCache<>();
private static final Map<Class<?>, Map<NamespacedKey, Keyed>> KEYED_REGISTRY = new HashMap<>(); private static final KMap<Class<?>, KeyedRegistry<Object>> CACHE = new KMap<>();
private static final Map<Class<?>, Map<NamespacedKey, Object>> ENUM_REGISTRY = new HashMap<>();
private static final Map<Class<?>, Registry<Keyed>> REGISTRY = new HashMap<>();
@NonNull @NonNull
public static <T> T find(@NonNull Class<T> typeClass, @NonNull String... keys) { public static <T> T find(@NonNull Class<T> typeClass, @NonNull String... keys) {
return find(typeClass, defaultLookup(), keys); return find(typeClass, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new));
} }
@NonNull @NonNull
public static <T> T find(@NonNull Class<T> typeClass, @Nullable Lookup<T> lookup, @NonNull String... keys) {
return find(typeClass, lookup, Arrays.stream(keys).map(NamespacedKey::minecraft).toArray(NamespacedKey[]::new));
}
public static <T> T find(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) { public static <T> T find(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) {
return find(typeClass, defaultLookup(), keys); return lookup(typeClass).find(keys).orElseThrow(() -> new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys)));
} }
@NonNull @NonNull
public static <T> T find(@NonNull Class<T> typeClass, @Nullable Lookup<T> lookup, @NonNull NamespacedKey... keys) { public static <T> KeyedRegistry<T> lookup(@NonNull Class<T> typeClass) {
if (keys.length == 0) throw new IllegalArgumentException("Need at least one key"); return (KeyedRegistry<T>) CACHE.computeIfAbsent(typeClass, $ -> {
Registry<Keyed> registry = null; final var registries = new KList<KeyedRegistry<Object>>();
if (Keyed.class.isAssignableFrom(typeClass)) { if (Keyed.class.isAssignableFrom(typeClass)) {
registry = getRegistry(typeClass.asSubclass(Keyed.class)); var bukkit = wrapped(getRegistry(typeClass.asSubclass(Keyed.class)));
} if (bukkit == null) {
if (registry == null) { bukkit = Arrays.stream(Registry.class.getDeclaredFields())
registry = REGISTRY.computeIfAbsent(typeClass, t -> Arrays.stream(Registry.class.getDeclaredFields()) .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers()))
.filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) .filter(field -> Registry.class.isAssignableFrom(field.getType()))
.filter(field -> Registry.class.isAssignableFrom(field.getType())) .filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(typeClass))
.filter(field -> ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0].equals(t)) .map(field -> {
.map(field -> { try {
try { return (Registry<Keyed>) field.get(null);
return (Registry<Keyed>) field.get(null); } catch (IllegalAccessException e) {
} catch (IllegalAccessException e) { return null;
return null; }
} })
}) .filter(Objects::nonNull)
.filter(Objects::nonNull) .findFirst()
.findFirst() .map(KeyedRegistry::wrapped)
.orElse(null)); .orElse(null);
} }
if (registry != null) { registries.addNonNull((KeyedRegistry<Object>) (Object) bukkit);
for (NamespacedKey key : keys) {
Keyed value = registry.get(key);
if (value != null)
return (T) value;
} }
} registries.add(getKeyedValues(typeClass));
registries.add(getEnumValues(typeClass));
if (lookup != null) return wrapped(registries);
return lookup.find(typeClass, keys); });
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
} }
@NonNull private static KeyedRegistry<Object> getKeyedValues(@NonNull Class<?> typeClass) {
public static <T> T findByField(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) { return wrapped(Arrays.stream(typeClass.getDeclaredFields())
var values = KEYED_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getKeyedValues);
for (NamespacedKey key : keys) {
var value = values.get(key);
if (value != null)
return (T) value;
}
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
}
@NonNull
public static <T> T findByEnum(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys) {
var values = ENUM_REGISTRY.computeIfAbsent(typeClass, RegistryUtil::getEnumValues);
for (NamespacedKey key : keys) {
var value = values.get(key);
if (value != null)
return (T) value;
}
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
}
@NonNull
public static <T> Lookup<T> defaultLookup() {
return Lookup.combine(RegistryUtil::findByField, RegistryUtil::findByEnum);
}
private static Map<NamespacedKey, Keyed> getKeyedValues(@NonNull Class<?> typeClass) {
return Arrays.stream(typeClass.getDeclaredFields())
.filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()))
.filter(field -> Keyed.class.isAssignableFrom(field.getType())) .filter(field -> Keyed.class.isAssignableFrom(field.getType()))
.map(field -> { .map(field -> {
try { try {
return (Keyed) field.get(null); final var value = (Keyed) field.get(null);
return new Pair<>(value.getKey(), value);
} catch (Throwable e) { } catch (Throwable e) {
return null; return null;
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toMap(Keyed::getKey, Function.identity())); .collect(Collectors.toMap(Pair::getA, Pair::getB)));
} }
private static Map<NamespacedKey, Object> getEnumValues(@NonNull Class<?> typeClass) { private static KeyedRegistry<Object> getEnumValues(@NonNull Class<?> typeClass) {
return Arrays.stream(typeClass.getDeclaredFields()) return wrapped(Arrays.stream(typeClass.getDeclaredFields())
.filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers()))
.filter(field -> typeClass.isAssignableFrom(field.getType())) .filter(field -> typeClass.isAssignableFrom(field.getType()))
.map(field -> { .map(field -> {
@ -133,28 +97,10 @@ public class RegistryUtil {
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toMap(Pair::getA, Pair::getB)); .collect(Collectors.toMap(Pair::getA, Pair::getB)));
} }
@FunctionalInterface
public interface Lookup<T> {
@NonNull
T find(@NonNull Class<T> typeClass, @NonNull NamespacedKey... keys);
static <T> Lookup<T> combine(@NonNull Lookup<T>... lookups) {
if (lookups.length == 0) throw new IllegalArgumentException("Need at least one lookup");
return (typeClass, keys) -> {
for (Lookup<T> lookup : lookups) {
try {
return lookup.find(typeClass, keys);
} catch (IllegalArgumentException ignored) {}
}
throw new IllegalArgumentException("No element found for keys: " + Arrays.toString(keys));
};
}
}
@Nullable @Nullable
private static <T extends Keyed> Registry<T> getRegistry(@NotNull Class<T> type) { private static <T extends Keyed> Registry<T> getRegistry(@NotNull Class<T> type) {
RegistryLookup lookup = registryLookup.aquire(() -> { RegistryLookup lookup = registryLookup.aquire(() -> {

View File

@ -18,6 +18,7 @@
package com.volmit.iris.util.function; package com.volmit.iris.util.function;
@FunctionalInterface
public interface NastyFunction<T, R> { public interface NastyFunction<T, R> {
R run(T t); R run(T t) throws Throwable;
} }

View File

@ -0,0 +1,35 @@
package com.volmit.iris.util.matter.slices;
import com.volmit.iris.util.data.palette.Palette;
import com.volmit.iris.util.matter.Sliced;
import com.volmit.iris.util.matter.slices.container.JigsawPieceContainer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
@Sliced
public class JigsawStructureMatter extends RawMatter<JigsawPieceContainer> {
public JigsawStructureMatter() {
this(1,1,1);
}
public JigsawStructureMatter(int width, int height, int depth) {
super(width, height, depth, JigsawPieceContainer.class);
}
@Override
public Palette<JigsawPieceContainer> getGlobalPalette() {
return null;
}
@Override
public void writeNode(JigsawPieceContainer b, DataOutputStream dos) throws IOException {
dos.writeUTF(b.getLoadKey());
}
@Override
public JigsawPieceContainer readNode(DataInputStream din) throws IOException {
return new JigsawPieceContainer(din.readUTF());
}
}

View File

@ -0,0 +1,13 @@
package com.volmit.iris.util.matter.slices.container;
import com.volmit.iris.engine.object.IrisJigsawStructure;
public class JigsawStructureContainer extends RegistrantContainer<IrisJigsawStructure> {
public JigsawStructureContainer(String loadKey) {
super(IrisJigsawStructure.class, loadKey);
}
public static JigsawStructureContainer toContainer(IrisJigsawStructure structure) {
return new JigsawStructureContainer(structure.getLoadKey());
}
}

View File

@ -1,5 +1,8 @@
package com.volmit.iris.util.misc; package com.volmit.iris.util.misc;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
@ -22,12 +25,13 @@ public class ServerProperties {
String bukkitYml = "bukkit.yml"; String bukkitYml = "bukkit.yml";
String levelName = null; String levelName = null;
for (int i = 0; i < args.length - 1; i++) { for (int i = 0; i < args.length; i++) {
switch (args[i]) { String arg = args[i];
case "-c", "--config" -> propertiesPath = args[i + 1]; String next = i < args.length - 1 ? args[i + 1] : null;
case "-b", "--bukkit-settings" -> bukkitYml = args[i + 1];
case "-w", "--level-name", "--world" -> levelName = args[i + 1]; propertiesPath = parse(arg, next, propertiesPath, "-c", "--config");
} bukkitYml = parse(arg, next, bukkitYml, "-b", "--bukkit-settings");
levelName = parse(arg, next, levelName, "-w", "--level-name", "--world");
} }
SERVER_PROPERTIES = new File(propertiesPath); SERVER_PROPERTIES = new File(propertiesPath);
@ -41,4 +45,19 @@ public class ServerProperties {
if (levelName != null) LEVEL_NAME = levelName; if (levelName != null) LEVEL_NAME = levelName;
else LEVEL_NAME = DATA.getProperty("level-name", "world"); else LEVEL_NAME = DATA.getProperty("level-name", "world");
} }
private static String parse(
@NotNull String current,
@Nullable String next,
String fallback,
@NotNull String @NotNull ... keys
) {
for (String k : keys) {
if (current.equals(k) && next != null)
return next;
if (current.startsWith(k + "=") && current.length() > k.length() + 1)
return current.substring(k.length() + 1);
}
return fallback;
}
} }

View File

@ -1,144 +1,69 @@
package com.volmit.iris.util.misc; package com.volmit.iris.util.misc;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.nms.container.Pair;
import io.github.slimjar.app.builder.ApplicationBuilder; import io.github.slimjar.app.builder.ApplicationBuilder;
import io.github.slimjar.exceptions.InjectorException; import io.github.slimjar.app.builder.SpigotApplicationBuilder;
import io.github.slimjar.injector.loader.Injectable; import io.github.slimjar.injector.loader.factory.InjectableFactory;
import io.github.slimjar.injector.loader.InjectableFactory;
import io.github.slimjar.injector.loader.IsolatedInjectableClassLoader;
import io.github.slimjar.logging.ProcessLogger; import io.github.slimjar.logging.ProcessLogger;
import io.github.slimjar.resolver.data.Repository;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiFunction;
import java.util.function.Function; import static com.volmit.iris.Iris.instance;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SlimJar { public class SlimJar {
private static final String NAME = "Iris"; private static final boolean DEBUG = Boolean.getBoolean("iris.debug-slimjar");
private static final Logger LOGGER = Logger.getLogger(NAME);
private static final ReentrantLock lock = new ReentrantLock();
private static final AtomicBoolean loaded = new AtomicBoolean();
private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("iris.disable-remapper"); private static final boolean DISABLE_REMAPPER = Boolean.getBoolean("iris.disable-remapper");
public static void debug(boolean debug) { private static final ReentrantLock lock = new ReentrantLock();
LOGGER.setLevel(debug ? Level.FINE : Level.INFO); private static final AtomicBoolean loaded = new AtomicBoolean();
}
public static void load(@Nullable File localRepository) { public static void load() {
if (loaded.get()) return; if (loaded.get()) return;
lock.lock(); lock.lock();
try { try {
if (loaded.getAndSet(true)) return; if (loaded.getAndSet(true)) return;
if (localRepository == null) { final var downloadPath = instance.getDataFolder("cache", "libraries").toPath();
localRepository = new File(".iris/libraries"); final var logger = instance.getLogger();
logger.info("Loading libraries...");
try {
new SpigotApplicationBuilder(instance)
.downloadDirectoryPath(downloadPath)
.debug(DEBUG)
.remap(!DISABLE_REMAPPER)
.build();
} catch (Throwable e) {
Iris.warn("Failed to inject the library loader, falling back to application builder");
ApplicationBuilder.appending(instance.getName())
.injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE, InjectableFactory.WRAPPED, InjectableFactory.UNSAFE))
.downloadDirectoryPath(downloadPath)
.logger(new ProcessLogger() {
@Override
public void info(@NotNull String message, @Nullable Object... args) {
if (!DEBUG) return;
instance.getLogger().info(message.formatted(args));
}
@Override
public void error(@NotNull String message, @Nullable Object... args) {
instance.getLogger().severe(message.formatted(args));
}
@Override
public void debug(@NotNull String message, @Nullable Object... args) {
if (!DEBUG) return;
instance.getLogger().info(message.formatted(args));
}
})
.build();
} }
logger.info("Libraries loaded successfully!");
LOGGER.info("Loading libraries...");
load(localRepository.toPath(), new ProcessLogger() {
@Override
public void info(@NotNull String message, @Nullable Object... args) {
LOGGER.fine(message.formatted(args));
}
@Override
public void error(@NotNull String message, @Nullable Object... args) {
LOGGER.severe(message.formatted(args));
}
@Override
public void debug(@NotNull String message, @Nullable Object... args) {
LOGGER.fine(message.formatted(args));
}
});
LOGGER.info("Libraries loaded successfully!");
} finally { } finally {
lock.unlock(); lock.unlock();
} }
} }
private static void load(Path downloadPath, ProcessLogger logger) {
try {
loadSpigot(downloadPath, logger);
} catch (Throwable e) {
Iris.warn("Failed to inject the library loader, falling back to application builder");
ApplicationBuilder.appending(NAME)
.downloadDirectoryPath(downloadPath)
.logger(logger)
.build();
}
}
private static void loadSpigot(Path downloadPath, ProcessLogger logger) throws Throwable {
var current = SlimJar.class.getClassLoader();
var libraryLoader = current.getClass().getDeclaredField("libraryLoader");
libraryLoader.setAccessible(true);
if (!ClassLoader.class.isAssignableFrom(libraryLoader.getType())) throw new IllegalStateException("Failed to find library loader");
final var pair = findRemapper();
final var remapper = pair.getA();
final var factory = pair.getB();
final var libraries = factory.apply(new URL[0], current.getParent());
final var injecting = InjectableFactory.create(downloadPath, List.of(Repository.central()), libraries);
ApplicationBuilder.injecting(NAME, new Injectable() {
@Override
public void inject(@NotNull URL url) throws InjectorException {
try {
final List<Path> mapped;
synchronized (remapper) {
mapped = remapper.apply(List.of(Path.of(url.toURI())));
}
for (final Path path : mapped) {
injecting.inject(path.toUri().toURL());
}
} catch (Throwable e) {
throw new InjectorException("Failed to inject " + url, e);
}
}
@Override
public boolean isThreadSafe() {
return injecting.isThreadSafe();
}
})
.downloadDirectoryPath(downloadPath)
.logger(logger)
.build();
libraryLoader.set(current, libraries);
}
private static Pair<Function<List<Path>, List<Path>>, BiFunction<URL[], ClassLoader, URLClassLoader>> findRemapper() {
Function<List<Path>, List<Path>> mapper = null;
BiFunction<URL[], ClassLoader, URLClassLoader> factory = null;
if (!DISABLE_REMAPPER) {
try {
var libraryLoader = Class.forName("org.bukkit.plugin.java.LibraryLoader");
var mapperField = libraryLoader.getDeclaredField("REMAPPER");
var factoryField = libraryLoader.getDeclaredField("LIBRARY_LOADER_FACTORY");
mapperField.setAccessible(true);
factoryField.setAccessible(true);
mapper = (Function<List<Path>, List<Path>>) mapperField.get(null);
factory = (BiFunction<URL[], ClassLoader, URLClassLoader>) factoryField.get(null);
} catch (Throwable ignored) {}
}
if (mapper == null) mapper = Function.identity();
if (factory == null) factory = (urls, parent) -> new IsolatedInjectableClassLoader(urls, List.of(), parent);
return new Pair<>(mapper, factory);
}
} }

View File

@ -0,0 +1,37 @@
package com.volmit.iris.util.reflect;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.volmit.iris.util.data.registry.RegistryTypeAdapter;
import com.volmit.iris.util.data.registry.RegistryUtil;
import org.bukkit.Keyed;
public class KeyedType {
private static final boolean KEYED_ENABLED = Boolean.getBoolean("iris.keyed-types");
private static final boolean KEYED_LENIENT = Boolean.getBoolean("iris.keyed-lenient");
public static String[] values(Class<?> type) {
if (!isKeyed(type)) return new String[0];
if (!KEYED_ENABLED) return OldEnum.values(type);
return RegistryUtil.lookup(type)
.map()
.keySet()
.stream()
.map(Object::toString)
.toArray(String[]::new);
}
public static boolean isKeyed(Class<?> type) {
if (KEYED_ENABLED) {
if (KEYED_LENIENT) return !RegistryUtil.lookup(type).isEmpty();
else return Keyed.class.isAssignableFrom(type);
} else return OldEnum.isOldEnum(type);
}
@SuppressWarnings("unchecked")
public static <T> TypeAdapter<T> createTypeAdapter(Gson gson, TypeToken<T> type) {
if (!isKeyed(type.getRawType())) return null;
return (TypeAdapter<T>) (KEYED_ENABLED ? RegistryTypeAdapter.of(type.getRawType()) : OldEnum.create(type.getRawType()));
}
}

View File

@ -1,20 +1,22 @@
package com.volmit.iris.util.reflect; package com.volmit.iris.util.reflect;
import com.google.gson.TypeAdapter; import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects; import java.util.Objects;
@Deprecated(since = "3.7.1")
public class OldEnum { public class OldEnum {
private static final Class<?> oldEnum; private static final Class<?> oldEnum;
private static final Method name; private static final MethodHandle name;
public static boolean exists() { public static boolean exists() {
return oldEnum != null; return oldEnum != null;
@ -25,11 +27,16 @@ public class OldEnum {
} }
public static <T> T valueOf(Class<? extends T> c, String name) { public static <T> T valueOf(Class<? extends T> c, String name) {
try { return valueOf(c, name, name.replace(".", "_"));
return (T) c.getDeclaredField(name).get(null); }
} catch (Throwable e) {
return null; public static <T> T valueOf(Class<? extends T> c, String... names) {
for (final String name : names) {
try {
return (T) c.getDeclaredField(name).get(null);
} catch (Throwable ignored) {}
} }
return null;
} }
public static String name(Object o) { public static String name(Object o) {
@ -40,20 +47,20 @@ public class OldEnum {
} }
} }
public static Object[] values(Class<?> clazz) { public static String[] values(Class<?> clazz) {
if (!isOldEnum(clazz)) return new Object[0]; if (!isOldEnum(clazz)) return new String[0];
return Arrays.stream(clazz.getDeclaredFields()) return Arrays.stream(clazz.getDeclaredFields())
.filter(f -> Modifier.isStatic(f.getModifiers())) .filter(f -> Modifier.isStatic(f.getModifiers()))
.filter(f -> Modifier.isFinal(f.getModifiers())) .filter(f -> Modifier.isFinal(f.getModifiers()))
.map(f -> { .map(f -> {
try { try {
return f.get(null); return name(f.get(null));
} catch (Throwable ignored) { } catch (Throwable ignored) {
return null; return null;
} }
}) })
.filter(Objects::nonNull) .filter(Objects::nonNull)
.toArray(); .toArray(String[]::new);
} }
public static <T> TypeAdapter<T> create(Class<? extends T> type) { public static <T> TypeAdapter<T> create(Class<? extends T> type) {
@ -76,10 +83,10 @@ public class OldEnum {
static { static {
Class<?> clazz = null; Class<?> clazz = null;
Method method = null; MethodHandle method = null;
try { try {
clazz = Class.forName("org.bukkit.util.OldEnum"); clazz = Class.forName("org.bukkit.util.OldEnum");
method = clazz.getDeclaredMethod("name"); method = MethodHandles.lookup().findVirtual(clazz, "name", MethodType.methodType(String.class));
} catch (Throwable ignored) {} } catch (Throwable ignored) {}
if (clazz == null || method == null) { if (clazz == null || method == null) {

View File

@ -5,7 +5,7 @@
[versions] [versions]
# Plugins # Plugins
shadow = "9.0.0-rc1" # https://plugins.gradle.org/plugin/com.gradleup.shadow shadow = "9.0.0-rc1" # https://plugins.gradle.org/plugin/com.gradleup.shadow
slimjar = "2.0.8" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar slimjar = "2.1.5" # https://plugins.gradle.org/plugin/de.crazydev22.slimjar
download = "5.6.0" # https://plugins.gradle.org/plugin/de.undercouch.download download = "5.6.0" # https://plugins.gradle.org/plugin/de.undercouch.download
runPaper = "2.3.1" # https://plugins.gradle.org/plugin/xyz.jpenilla.run-paper runPaper = "2.3.1" # https://plugins.gradle.org/plugin/xyz.jpenilla.run-paper
sentryPlugin = "5.8.0" # https://github.com/getsentry/sentry-android-gradle-plugin sentryPlugin = "5.8.0" # https://github.com/getsentry/sentry-android-gradle-plugin
@ -15,8 +15,8 @@ grgit = "5.3.2" # https://github.com/ajoberstar/grgit
lombok = "1.18.38" lombok = "1.18.38"
spigot = "1.20.1-R0.1-SNAPSHOT" # https://hub.spigotmc.org/nexus/repository/snapshots/org/spigotmc/spigot-api/maven-metadata.xml spigot = "1.20.1-R0.1-SNAPSHOT" # https://hub.spigotmc.org/nexus/repository/snapshots/org/spigotmc/spigot-api/maven-metadata.xml
log4j = "2.19.0" # https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api log4j = "2.19.0" # https://central.sonatype.com/artifact/org.apache.logging.log4j/log4j-api
adventure-api = "4.23.0" # https://github.com/KyoriPowered/adventure adventure-api = "4.24.0" # https://github.com/KyoriPowered/adventure
adventure-platform = "4.4.0" # https://github.com/KyoriPowered/adventure-platform adventure-platform = "4.4.1" # https://github.com/KyoriPowered/adventure-platform
platform-utils = "e396f93d56" # https://github.com/CrazyDev05/PlatformUtils platform-utils = "e396f93d56" # https://github.com/CrazyDev05/PlatformUtils
annotations = "26.0.2" # https://central.sonatype.com/artifact/org.jetbrains/annotations annotations = "26.0.2" # https://central.sonatype.com/artifact/org.jetbrains/annotations