This commit is contained in:
Brian Neumann-Fopiano
2026-02-18 20:24:25 -05:00
parent 1829fa3eb1
commit d6bd5ec82f
55 changed files with 1063 additions and 800 deletions

View File

@@ -76,7 +76,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Predicate;
@@ -232,7 +232,7 @@ public class Iris extends VolmitPlugin implements Listener {
File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h);
if (!f.exists()) {
try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) {
try (BufferedInputStream in = new BufferedInputStream(URI.create(url).toURL().openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
@@ -251,7 +251,7 @@ public class Iris extends VolmitPlugin implements Listener {
String h = IO.hash(name + "*" + url);
File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h);
try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) {
try (BufferedInputStream in = new BufferedInputStream(URI.create(url).toURL().openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
@@ -274,7 +274,7 @@ public class Iris extends VolmitPlugin implements Listener {
String h = IO.hash(name + "*" + url);
File f = Iris.instance.getDataFile("cache", h.substring(0, 2), h.substring(3, 5), h);
Iris.verbose("Download " + name + " -> " + url);
try (BufferedInputStream in = new BufferedInputStream(new URL(url).openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) {
try (BufferedInputStream in = new BufferedInputStream(URI.create(url).toURL().openStream()); FileOutputStream fileOutputStream = new FileOutputStream(f)) {
byte[] dataBuffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) {
@@ -360,7 +360,6 @@ public class Iris extends VolmitPlugin implements Listener {
}
}
@SuppressWarnings("deprecation")
public static void later(NastyRunnable object) {
try {
J.a(() -> {
@@ -459,7 +458,7 @@ public class Iris extends VolmitPlugin implements Listener {
PrintWriter pw = new PrintWriter(fos);
for (Thread i : f.keySet()) {
pw.println("========================================");
pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name());
pw.println("Thread: '" + i.getName() + "' ID: " + i.threadId() + " STATUS: " + i.getState().name());
for (StackTraceElement j : f.get(i)) {
pw.println(" @ " + j.toString());

View File

@@ -32,7 +32,6 @@ import art.arcane.iris.util.common.misc.ServerProperties;
import art.arcane.iris.util.common.plugin.VolmitSender;
import art.arcane.iris.util.common.scheduling.J;
import lombok.NonNull;
import lombok.SneakyThrows;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
@@ -42,8 +41,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;
@@ -243,8 +241,9 @@ public class ServerConfigurator {
}
public static Stream<IrisData> allPacks() {
return Stream.concat(listFiles(Iris.instance.getDataFolder("packs"))
.filter(File::isDirectory)
File[] packs = Iris.instance.getDataFolder("packs").listFiles(File::isDirectory);
Stream<File> locals = packs == null ? Stream.empty() : Arrays.stream(packs);
return Stream.concat(locals
.filter( base -> {
var content = new File(base, "dimensions").listFiles();
return content != null && content.length > 0;
@@ -263,12 +262,6 @@ public class ServerConfigurator {
return path.substring(worldContainer.length(), path.length() - l);
}
@SneakyThrows
private static Stream<File> listFiles(File parent) {
if (!parent.isDirectory()) return Stream.empty();
return Files.walk(parent.toPath()).map(Path::toFile);
}
public static class DimensionHeight {
private final IDataFixer fixer;
private final AtomicIntegerArray[] dimensions = new AtomicIntegerArray[3];

View File

@@ -350,7 +350,7 @@ public class CommandDeveloper implements DirectorExecutor {
for (Thread i : f.keySet()) {
pw.println("========================================");
pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name());
pw.println("Thread: '" + i.getName() + "' ID: " + i.threadId() + " STATUS: " + i.getState().name());
for (StackTraceElement j : f.get(i)) {
pw.println(" @ " + j.toString());

View File

@@ -289,7 +289,7 @@ public class CommandObject implements DirectorExecutor {
} else {
g[1] = player().getLocation().getBlock().getLocation().clone().add(0, -1, 0);
}
player().setItemInHand(WandSVC.createWand(g[0], g[1]));
player().getInventory().setItemInMainHand(WandSVC.createWand(g[0], g[1]));
}
}
@@ -316,7 +316,7 @@ public class CommandObject implements DirectorExecutor {
} else {
g[0] = player().getLocation().getBlock().getLocation().clone().add(0, -1, 0);
}
player().setItemInHand(WandSVC.createWand(g[0], g[1]));
player().getInventory().setItemInMainHand(WandSVC.createWand(g[0], g[1]));
}
}

View File

@@ -36,9 +36,11 @@ import art.arcane.iris.util.common.scheduling.J;
import org.bukkit.Chunk;
import org.bukkit.FluidCollisionMode;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
@@ -69,13 +71,17 @@ public class CommandWhat implements DirectorExecutor {
public void biome() {
try {
IrisBiome b = engine().getBiome(player().getLocation().getBlockX(), player().getLocation().getBlockY() - player().getWorld().getMinHeight(), player().getLocation().getBlockZ());
sender().sendMessage("IBiome: " + b.getLoadKey() + " (" + b.getDerivative().name() + ")");
Biome derivative = b.getDerivative();
NamespacedKey derivativeKey = resolveBiomeKey(derivative);
sender().sendMessage("IBiome: " + b.getLoadKey() + " (" + (derivativeKey == null ? "unregistered" : derivativeKey.getKey()) + ")");
} catch (Throwable e) {
Iris.reportError(e);
sender().sendMessage("Non-Iris Biome: " + player().getLocation().getBlock().getBiome().name());
Biome biome = player().getLocation().getBlock().getBiome();
NamespacedKey key = resolveBiomeKey(biome);
sender().sendMessage("Non-Iris Biome: " + (key == null ? "unregistered" : key));
if (player().getLocation().getBlock().getBiome().equals(Biome.CUSTOM)) {
if (key == null || key.getKey().equals("custom")) {
try {
sender().sendMessage("Data Pack Biome: " + INMS.get().getTrueBiomeBaseKey(player().getLocation()) + " (ID: " + INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(player().getLocation())) + ")");
} catch (Throwable ee) {
@@ -166,4 +172,36 @@ public class CommandWhat implements DirectorExecutor {
sender().sendMessage(C.IRIS + "Iris worlds only.");
}
}
private NamespacedKey resolveBiomeKey(Biome biome) {
Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull");
if (keyOrNullValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow");
if (keyOrThrowValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
Object keyValue = invokeNoThrow(biome, "getKey");
if (keyValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
return null;
}
private Object invokeNoThrow(Biome biome, String methodName) {
if (biome == null) {
return null;
}
try {
Method method = biome.getClass().getMethod(methodName);
return method.invoke(biome);
} catch (Throwable ignored) {
return null;
}
}
}

View File

@@ -51,10 +51,13 @@ public class BukkitBlockEditor implements BlockEditor {
return M.ms();
}
@SuppressWarnings("deprecation")
@Override
public void setBiome(int x, int z, Biome b) {
world.setBiome(x, z, b);
int minHeight = world.getMinHeight();
int maxHeight = world.getMaxHeight();
for (int y = minHeight; y < maxHeight; y++) {
world.setBiome(x, y, z, b);
}
}
@Override
@@ -67,9 +70,8 @@ public class BukkitBlockEditor implements BlockEditor {
return world.getBiome(x, y, z);
}
@SuppressWarnings("deprecation")
@Override
public Biome getBiome(int x, int z) {
return world.getBiome(x, z);
return world.getBiome(x, world.getMinHeight(), z);
}
}

View File

@@ -1,6 +1,8 @@
package art.arcane.iris.core.link;
import art.arcane.iris.Iris;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.iris.util.common.scheduling.J;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -274,8 +276,8 @@ public class FoliaWorldsLink {
Object primaryLevelData = createPrimaryLevelData(levelStorageAccess, creator.name());
Object runtimeStemKey = createRuntimeLevelStemKey(creator.name());
Object worldLoadingInfo = createWorldLoadingInfo(creator.name(), runtimeStemKey);
Object overworldLevelStem = getOverworldLevelStem();
Object[] createLevelArgs = new Object[]{overworldLevelStem, worldLoadingInfo, levelStorageAccess, primaryLevelData};
Object levelStem = resolveCreateLevelStem(creator);
Object[] createLevelArgs = new Object[]{levelStem, worldLoadingInfo, levelStorageAccess, primaryLevelData};
Method createLevelMethod = minecraftServerCreateLevelMethod;
if (createLevelMethod == null || !matches(createLevelMethod.getParameterTypes(), createLevelArgs)) {
createLevelMethod = resolveMethod(minecraftServer.getClass(), "createLevel", createLevelArgs);
@@ -376,6 +378,44 @@ public class FoliaWorldsLink {
return createMethod.invoke(null, levelStemRegistryKey, identifier);
}
private Object resolveCreateLevelStem(WorldCreator creator) throws ReflectiveOperationException {
Object irisLevelStem = resolveIrisLevelStem(creator);
if (irisLevelStem != null) {
return irisLevelStem;
}
return getOverworldLevelStem();
}
private Object resolveIrisLevelStem(WorldCreator creator) throws ReflectiveOperationException {
ChunkGenerator generator = creator.generator();
if (!(generator instanceof PlatformChunkGenerator)) {
return null;
}
Object registryAccess = invoke(minecraftServer, "registryAccess");
Object binding = INMS.get();
Method levelStemMethod;
try {
levelStemMethod = resolveMethod(binding.getClass(), "levelStem", registryAccess, generator);
} catch (NoSuchMethodException e) {
throw new IllegalStateException("Iris NMS binding does not expose levelStem(RegistryAccess, ChunkGenerator) for runtime world \"" + creator.name() + "\".", e);
}
Object levelStem;
try {
levelStem = levelStemMethod.invoke(binding, registryAccess, generator);
} catch (InvocationTargetException e) {
Throwable cause = unwrap(e);
throw new IllegalStateException("Iris failed to resolve runtime level stem for world \"" + creator.name() + "\".", cause);
}
if (levelStem == null) {
throw new IllegalStateException("Iris resolved a null runtime level stem for world \"" + creator.name() + "\".");
}
return levelStem;
}
private Object getOverworldLevelStem() throws ReflectiveOperationException {
Object levelStemRegistryKey = Class.forName("net.minecraft.core.registries.Registries")
.getField("LEVEL_STEM")

View File

@@ -352,7 +352,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
builder = new GsonBuilder()
.addDeserializationExclusionStrategy(this)
.addSerializationExclusionStrategy(this)
.setLenient()
.setStrictness(Strictness.LENIENT)
.registerTypeAdapterFactory(this)
.registerTypeAdapter(MantleFlag.class, new MantleFlagAdapter())
.setPrettyPrinting();
@@ -409,19 +409,18 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
public Set<Class<?>> resolveSnippets() {
var result = new HashSet<Class<?>>();
var processed = new HashSet<Class<?>>();
var excluder = gson.excluder();
var queue = new LinkedList<Class<?>>(loaders.keySet());
while (!queue.isEmpty()) {
var type = queue.poll();
if (excluder.excludeClass(type, false) || !processed.add(type))
if (shouldSkipClass(type) || !processed.add(type))
continue;
if (type.isAnnotationPresent(Snippet.class))
result.add(type);
try {
for (var field : type.getDeclaredFields()) {
if (excluder.excludeField(field, false))
if (shouldSkipField(new FieldAttributes(field)))
continue;
queue.add(field.getType());
@@ -590,4 +589,4 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
b.complete();
Iris.info("Loaded Prefetch Cache to reduce generation disk use.");
}
}
}

View File

@@ -59,7 +59,12 @@ public class ObjectResourceLoader extends ResourceLoader<IrisObject> {
return t;
} catch (Throwable e) {
Iris.reportError(e);
Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + ": " + e.getMessage());
String message = e.getMessage();
String reason = e.getClass().getSimpleName();
if (message != null && !message.isBlank()) {
reason = reason + ": " + message;
}
Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + " (" + reason + ")");
return null;
}
}

View File

@@ -1,28 +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 art.arcane.iris.core.nms;
@FunctionalInterface
public interface BiomeBaseInjector {
default void setBiome(int x, int z, Object biomeBase) {
setBiome(x, 0, z, biomeBase);
}
void setBiome(int x, int y, int z, Object biomeBase);
}

View File

@@ -39,7 +39,6 @@ import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import java.awt.Color;
@@ -104,6 +103,11 @@ public interface INMSBinding {
default CompletableFuture<World> createWorldAsync(WorldCreator c) {
try {
if (c.generator() instanceof PlatformChunkGenerator gen
&& missingDimensionTypes(gen.getTarget().getDimension().getDimensionTypeKey())) {
return CompletableFuture.failedFuture(new IllegalStateException("Missing dimension types to create world"));
}
FoliaWorldsLink link = FoliaWorldsLink.get();
if (link.isActive()) {
CompletableFuture<World> future = link.createWorld(c);
@@ -119,8 +123,6 @@ public interface INMSBinding {
int countCustomBiomes();
void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk);
default boolean supportsDataPacks() {
return false;
}

View File

@@ -1,5 +1,6 @@
package art.arcane.iris.core.nms.datapack.v1206;
import art.arcane.iris.Iris;
import art.arcane.iris.core.nms.datapack.v1192.DataFixerV1192;
import art.arcane.iris.engine.object.IrisBiomeCustom;
import art.arcane.iris.engine.object.IrisBiomeCustomSpawn;
@@ -7,6 +8,8 @@ import art.arcane.iris.engine.object.IrisBiomeCustomSpawnType;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.json.JSONArray;
import art.arcane.volmlib.util.json.JSONObject;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.EntityType;
import java.util.Locale;
@@ -26,9 +29,23 @@ public class DataFixerV1206 extends DataFixerV1192 {
KMap<IrisBiomeCustomSpawnType, JSONArray> groups = new KMap<>();
for (IrisBiomeCustomSpawn i : spawns) {
JSONArray g = groups.computeIfAbsent(i.getGroup(), (k) -> new JSONArray());
if (i == null) {
continue;
}
EntityType type = i.getType();
if (type == null) {
Iris.warn("Skipping custom biome spawn with null entity type in biome " + biome.getId());
continue;
}
IrisBiomeCustomSpawnType group = i.getGroup() == null ? IrisBiomeCustomSpawnType.MISC : i.getGroup();
JSONArray g = groups.computeIfAbsent(group, (k) -> new JSONArray());
JSONObject o = new JSONObject();
o.put("type", i.getType().getKey());
NamespacedKey key = type.getKey();
if (key == null) {
Iris.warn("Skipping custom biome spawn with unresolved entity key in biome " + biome.getId());
continue;
}
o.put("type", key.toString());
o.put("weight", i.getWeight());
o.put("minCount", i.getMinCount());
o.put("maxCount", i.getMaxCount());

View File

@@ -115,7 +115,7 @@ public class DataFixerV1217 extends DataFixerV1213 {
attributes.put("minecraft:gameplay/fast_lava", true);
attributes.put("minecraft:gameplay/snow_golem_melts", true);
attributes.put("minecraft:visual/default_dripstone_particle", new JSONObject()
.put("value", "minecraft:dripstone_drip_water_lava"));
.put("type", "minecraft:dripping_dripstone_lava"));
}
if ((Boolean) json.remove("bed_works")) {
@@ -132,7 +132,7 @@ public class DataFixerV1217 extends DataFixerV1213 {
}
attributes.put("minecraft:gameplay/respawn_anchor_works", json.remove("respawn_anchor_works"));
attributes.put("minecraft:gameplay/piglins_zombify", json.remove("piglin_safe"));
attributes.put("minecraft:gameplay/piglins_zombify", !(Boolean) json.remove("piglin_safe"));
attributes.put("minecraft:gameplay/can_start_raid", json.remove("has_raids"));
var cloud_height = json.remove("cloud_height");

View File

@@ -40,7 +40,6 @@ import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.structure.Structure;
import org.bukkit.inventory.ItemStack;
@@ -118,8 +117,8 @@ public class NMSBinding1X implements INMSBinding {
@Override
public KList<String> getStructureKeys() {
var list = StreamSupport.stream(Registry.STRUCTURE.spliterator(), false)
.map(Structure::getKey)
List<String> list = StreamSupport.stream(Registry.STRUCTURE.spliterator(), false)
.map(Structure::getKeyOrThrow)
.map(NamespacedKey::toString)
.toList();
return new KList<>(list);
@@ -225,7 +224,11 @@ public class NMSBinding1X implements INMSBinding {
@Override
public KList<Biome> getBiomes() {
return new KList<>(Biome.values()).qdel(Biome.CUSTOM);
KList<Biome> biomes = new KList<>();
for (Biome biome : Registry.BIOME) {
biomes.add(biome);
}
return biomes;
}
@Override
@@ -240,7 +243,9 @@ public class NMSBinding1X implements INMSBinding {
@Override
public int getBiomeId(Biome biome) {
return biome.ordinal();
List<Biome> biomes = StreamSupport.stream(Registry.BIOME.spliterator(), false).toList();
int index = biomes.indexOf(biome);
return Math.max(index, 0);
}
@Override
@@ -262,11 +267,6 @@ public class NMSBinding1X implements INMSBinding {
return 0;
}
@Override
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
}
@Override
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
return null;

View File

@@ -123,13 +123,11 @@ public class LazyPregenerator extends Thread implements Listener {
if (lazyGeneratedChunks.get() >= lazyTotalChunks.get()) {
if (job.isHealing()) {
int pos = (job.getHealingPosition() + 1) % maxPosition;
job.setHealingPosition(pos);
tickRegenerate(getChunk(pos));
} else {
Iris.info("Completed Lazy Gen!");
interrupt();
Iris.warn("LazyGen healing mode is not supported on 1.21.11; ending lazy generation for " + world.getName() + ".");
job.setHealing(false);
}
Iris.info("Completed Lazy Gen!");
interrupt();
} else {
int pos = job.getPosition() + 1;
job.setPosition(pos);
@@ -172,11 +170,6 @@ public class LazyPregenerator extends Thread implements Listener {
});
}
private void tickRegenerate(Position2 chunk) {
J.s(() -> world.regenerateChunk(chunk.getX(), chunk.getZ()));
Iris.verbose("Regenerated " + chunk);
}
public Position2 getChunk(int position) {
int p = -1;
AtomicInteger xx = new AtomicInteger();

View File

@@ -32,6 +32,8 @@ import art.arcane.iris.util.common.data.B;
import art.arcane.volmlib.util.json.JSONArray;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.iris.util.common.reflect.KeyedType;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
@@ -41,6 +43,7 @@ import java.lang.reflect.Field;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
@@ -65,8 +68,11 @@ public class SchemaBuilder {
private static JSONArray getPotionTypes() {
JSONArray a = new JSONArray();
for (PotionEffectType gg : PotionEffectType.values()) {
a.put(gg.getName().toUpperCase().replaceAll("\\Q \\E", "_"));
for (PotionEffectType gg : Registry.EFFECT) {
NamespacedKey key = KeyedType.getKey(gg);
if (key != null) {
a.put(key.getKey().toUpperCase(Locale.ROOT).replaceAll("\\Q \\E", "_"));
}
}
return a;
@@ -74,8 +80,11 @@ public class SchemaBuilder {
private static JSONArray getEnchantTypes() {
JSONArray array = new JSONArray();
for (Enchantment e : Enchantment.values()) {
array.put(e.getKey().getKey());
for (Enchantment e : Registry.ENCHANTMENT) {
NamespacedKey key = KeyedType.getKey(e);
if (key != null) {
array.put(key.getKey());
}
}
return array;
}
@@ -602,7 +611,7 @@ public class SchemaBuilder {
try {
k.setAccessible(true);
Object value = k.get(cl.newInstance());
Object value = k.get(cl.getDeclaredConstructor().newInstance());
if (value != null) {
if (present) d.add(" ");

View File

@@ -31,6 +31,7 @@ import art.arcane.iris.util.common.format.C;
import art.arcane.volmlib.util.format.Form;
import art.arcane.iris.util.common.plugin.IrisService;
import art.arcane.iris.util.common.scheduling.J;
import art.arcane.volmlib.util.matter.MatterCavern;
import lombok.Data;
import org.bukkit.Location;
import org.bukkit.World;
@@ -201,7 +202,7 @@ public class BoardSVC implements IrisService, BoardProvider {
lines.add(C.AQUA + "Mantle" + C.GRAY + ": " + engine.getMantle().getLoadedRegionCount());
if (IrisSettings.get().getGeneral().debug) {
lines.add(C.LIGHT_PURPLE + "Carving" + C.GRAY + ": " + engine.getMantle().isCarved(x,y,z));
lines.add(C.LIGHT_PURPLE + "Carving" + C.GRAY + ": " + (engine.getMantle().getMantle().get(x, y, z, MatterCavern.class) != null));
}
lines.add("&7&m ");

View File

@@ -48,6 +48,9 @@ public class EditSVC implements IrisService {
J.csr(updateTaskId);
updateTaskId = -1;
}
if (editors == null) {
return;
}
flushNow();
}
@@ -77,6 +80,9 @@ public class EditSVC implements IrisService {
@EventHandler
public void on(WorldUnloadEvent e) {
if (editors == null) {
return;
}
if (editors.containsKey(e.getWorld()) && !deletingWorld) {
editors.remove(e.getWorld()).close();
}
@@ -84,6 +90,9 @@ public class EditSVC implements IrisService {
public void update() {
if (editors == null) {
return;
}
for (World i : editors.k()) {
if (M.ms() - editors.get(i).last() > 1000) {
editors.remove(i).close();
@@ -92,12 +101,18 @@ public class EditSVC implements IrisService {
}
public void flushNow() {
if (editors == null) {
return;
}
for (World i : editors.k()) {
editors.remove(i).close();
}
}
public BlockEditor open(World world) {
if (editors == null) {
editors = new KMap<>();
}
if (editors.containsKey(world)) {
return editors.get(world);
}

View File

@@ -61,8 +61,12 @@ public class IrisEngineSVC implements IrisService {
@Override
public void onDisable() {
service.shutdown();
updateTicker.interrupt();
if (service != null) {
service.shutdown();
}
if (updateTicker != null) {
updateTicker.interrupt();
}
worlds.keySet().forEach(this::remove);
worlds.clear();
}

View File

@@ -91,7 +91,9 @@ public class PreservationSVC implements IrisService {
@Override
public void onDisable() {
dereferencer.interrupt();
if (dereferencer != null) {
dereferencer.interrupt();
}
dereference();
postShutdown(() -> {

View File

@@ -40,12 +40,17 @@ import io.papermc.lib.PaperLib;
import lombok.Data;
import lombok.experimental.Accessors;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -231,8 +236,8 @@ public class IrisCreator {
Iris.linkMultiverseCore.removeFromConfig(world);
if (IrisSettings.get().getStudio().isDisableTimeAndWeather()) {
world.setGameRule(GameRule.ADVANCE_WEATHER, false);
world.setGameRule(GameRule.ADVANCE_TIME, false);
setBooleanGameRule(world, false, "ADVANCE_WEATHER", "DO_WEATHER_CYCLE", "WEATHER_CYCLE", "doWeatherCycle", "weatherCycle");
setBooleanGameRule(world, false, "ADVANCE_TIME", "DO_DAYLIGHT_CYCLE", "DAYLIGHT_CYCLE", "doDaylightCycle", "daylightCycle");
world.setTime(6000);
}
};
@@ -278,7 +283,7 @@ public class IrisCreator {
CompletableFuture<Location> locationFuture = J.sfut(() -> {
Location spawnLocation = world.getSpawnLocation();
if (spawnLocation != null) {
return spawnLocation;
return spawnLocation.clone();
}
int x = 0;
@@ -292,7 +297,8 @@ public class IrisCreator {
}
try {
return locationFuture.get(15, TimeUnit.SECONDS);
Location rawLocation = locationFuture.get(15, TimeUnit.SECONDS);
return resolveTopSafeStudioLocation(world, rawLocation);
} catch (Throwable e) {
Iris.warn("Failed to resolve studio entry location for world \"" + world.getName() + "\".");
Iris.reportError(e);
@@ -300,6 +306,105 @@ public class IrisCreator {
}
}
private Location resolveTopSafeStudioLocation(World world, Location rawLocation) {
if (world == null || rawLocation == null) {
return rawLocation;
}
int chunkX = rawLocation.getBlockX() >> 4;
int chunkZ = rawLocation.getBlockZ() >> 4;
try {
CompletableFuture<Chunk> chunkFuture = PaperLib.getChunkAtAsync(world, chunkX, chunkZ, true);
if (chunkFuture != null) {
chunkFuture.get(15, TimeUnit.SECONDS);
}
} catch (Throwable ignored) {
}
CompletableFuture<Location> regionFuture = new CompletableFuture<>();
boolean scheduled = J.runRegion(world, chunkX, chunkZ, () -> {
try {
regionFuture.complete(findTopSafeStudioLocation(world, rawLocation));
} catch (Throwable e) {
regionFuture.completeExceptionally(e);
}
});
if (!scheduled) {
return rawLocation;
}
try {
Location resolved = regionFuture.get(15, TimeUnit.SECONDS);
return resolved == null ? rawLocation : resolved;
} catch (Throwable e) {
Iris.warn("Failed to resolve safe studio entry surface for world \"" + world.getName() + "\".");
Iris.reportError(e);
return rawLocation;
}
}
private Location findTopSafeStudioLocation(World world, Location source) {
int x = source.getBlockX();
int z = source.getBlockZ();
int minY = world.getMinHeight() + 1;
int maxY = world.getMaxHeight() - 2;
int topY = world.getHighestBlockYAt(x, z, HeightMap.MOTION_BLOCKING_NO_LEAVES);
int startY = Math.max(minY, Math.min(maxY, topY + 1));
float yaw = source.getYaw();
float pitch = source.getPitch();
int upperBound = Math.min(maxY, startY + 16);
for (int y = startY; y <= upperBound; y++) {
if (isSafeStandingLocation(world, x, y, z)) {
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
}
}
int lowerBound = Math.max(minY, startY - 24);
for (int y = startY - 1; y >= lowerBound; y--) {
if (isSafeStandingLocation(world, x, y, z)) {
return new Location(world, x + 0.5D, y, z + 0.5D, yaw, pitch);
}
}
int fallbackY = Math.max(minY, Math.min(maxY, source.getBlockY()));
return new Location(world, x + 0.5D, fallbackY, z + 0.5D, yaw, pitch);
}
private boolean isSafeStandingLocation(World world, int x, int y, int z) {
if (y <= world.getMinHeight() || y >= world.getMaxHeight() - 1) {
return false;
}
Block below = world.getBlockAt(x, y - 1, z);
Block feet = world.getBlockAt(x, y, z);
Block head = world.getBlockAt(x, y + 1, z);
Material belowType = below.getType();
if (!belowType.isSolid()) {
return false;
}
if (Tag.LEAVES.isTagged(belowType)) {
return false;
}
if (belowType == Material.LAVA
|| belowType == Material.MAGMA_BLOCK
|| belowType == Material.FIRE
|| belowType == Material.SOUL_FIRE
|| belowType == Material.CAMPFIRE
|| belowType == Material.SOUL_CAMPFIRE) {
return false;
}
if (feet.getType().isSolid() || head.getType().isSolid()) {
return false;
}
if (feet.isLiquid() || head.isLiquid()) {
return false;
}
return true;
}
private static boolean containsCreateWorldUnsupportedOperation(Throwable throwable) {
Throwable cursor = throwable;
while (cursor != null) {
@@ -316,6 +421,138 @@ public class IrisCreator {
return false;
}
@SuppressWarnings("unchecked")
private static void setBooleanGameRule(World world, boolean value, String... names) {
GameRule<Boolean> gameRule = resolveBooleanGameRule(world, names);
if (gameRule != null) {
world.setGameRule(gameRule, value);
}
}
@SuppressWarnings("unchecked")
private static GameRule<Boolean> resolveBooleanGameRule(World world, String... names) {
if (world == null || names == null || names.length == 0) {
return null;
}
Set<String> candidates = buildRuleNameCandidates(names);
for (String name : candidates) {
if (name == null || name.isBlank()) {
continue;
}
try {
Field field = GameRule.class.getField(name);
Object value = field.get(null);
if (value instanceof GameRule<?> gameRule && Boolean.class.equals(gameRule.getType())) {
return (GameRule<Boolean>) gameRule;
}
} catch (Throwable ignored) {
}
try {
GameRule<?> byName = GameRule.getByName(name);
if (byName != null && Boolean.class.equals(byName.getType())) {
return (GameRule<Boolean>) byName;
}
} catch (Throwable ignored) {
}
}
String[] availableRules = world.getGameRules();
if (availableRules == null || availableRules.length == 0) {
return null;
}
Set<String> normalizedCandidates = new LinkedHashSet<>();
for (String candidate : candidates) {
if (candidate != null && !candidate.isBlank()) {
normalizedCandidates.add(normalizeRuleName(candidate));
}
}
for (String availableRule : availableRules) {
String normalizedAvailable = normalizeRuleName(availableRule);
if (!normalizedCandidates.contains(normalizedAvailable)) {
continue;
}
try {
GameRule<?> byName = GameRule.getByName(availableRule);
if (byName != null && Boolean.class.equals(byName.getType())) {
return (GameRule<Boolean>) byName;
}
} catch (Throwable ignored) {
}
}
return null;
}
private static Set<String> buildRuleNameCandidates(String... names) {
Set<String> candidates = new LinkedHashSet<>();
for (String name : names) {
if (name == null || name.isBlank()) {
continue;
}
candidates.add(name);
candidates.add(name.toLowerCase(Locale.ROOT));
String lowerCamel = toLowerCamel(name);
if (!lowerCamel.isEmpty()) {
candidates.add(lowerCamel);
}
}
return candidates;
}
private static String toLowerCamel(String name) {
if (name == null) {
return "";
}
String raw = name.trim();
if (raw.isEmpty()) {
return "";
}
String[] parts = raw.split("_+");
if (parts.length == 0) {
return raw;
}
StringBuilder builder = new StringBuilder();
builder.append(parts[0].toLowerCase(Locale.ROOT));
for (int i = 1; i < parts.length; i++) {
String part = parts[i].toLowerCase(Locale.ROOT);
if (part.isEmpty()) {
continue;
}
builder.append(Character.toUpperCase(part.charAt(0)));
if (part.length() > 1) {
builder.append(part.substring(1));
}
}
return builder.toString();
}
private static String normalizeRuleName(String name) {
if (name == null || name.isBlank()) {
return "";
}
StringBuilder builder = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
char c = name.charAt(i);
if (Character.isLetterOrDigit(c)) {
builder.append(Character.toLowerCase(c));
}
}
return builder.toString();
}
private void addToBukkitYml() {
YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML);
String gen = "Iris:" + dimension;

View File

@@ -18,183 +18,82 @@
package art.arcane.iris.engine.data.chunk;
import art.arcane.iris.core.nms.BiomeBaseInjector;
import art.arcane.iris.core.nms.INMS;
import art.arcane.volmlib.util.data.IrisBiomeStorage;
import art.arcane.iris.util.common.data.IrisCustomData;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
import org.bukkit.generator.ChunkGenerator.ChunkData;
import org.bukkit.material.MaterialData;
@SuppressWarnings("deprecation")
public class LinkedTerrainChunk implements TerrainChunk {
private final IrisBiomeStorage biome3D;
private final BiomeGrid storage;
private ChunkData rawChunkData;
@Setter
private boolean unsafe = true;
private static final int CHUNK_SIZE = 16;
private final ChunkData rawChunkData;
private final int minHeight;
private final int maxHeight;
private final int biomeHeight;
private final Biome[] biomes;
public LinkedTerrainChunk(World world) {
this(null, Bukkit.createChunkData(world));
this(Bukkit.createChunkData(world));
}
public LinkedTerrainChunk(World world, BiomeGrid storage) {
this(storage, Bukkit.createChunkData(world));
}
public LinkedTerrainChunk(BiomeGrid storage, ChunkData data) {
this.storage = storage;
public LinkedTerrainChunk(ChunkData data) {
rawChunkData = data;
biome3D = storage != null ? null : new IrisBiomeStorage();
minHeight = data.getMinHeight();
maxHeight = data.getMaxHeight();
biomeHeight = Math.max(1, maxHeight - minHeight);
biomes = new Biome[CHUNK_SIZE * biomeHeight * CHUNK_SIZE];
}
@Override
public BiomeBaseInjector getBiomeBaseInjector() {
if (unsafe) {
return (a, b, c, d) -> {
};
}
return (x, y, z, bb) -> INMS.get().forceBiomeInto(x, y, z, bb, storage);
}
@Override
public Biome getBiome(int x, int z) {
if (storage != null) {
return storage.getBiome(x, z);
}
return biome3D.getBiome(x, 0, z);
}
@Override
public Biome getBiome(int x, int y, int z) {
if (storage != null) {
return storage.getBiome(x, y, z);
}
return biome3D.getBiome(x, y, z);
}
@Override
public void setBiome(int x, int z, Biome bio) {
if (storage != null) {
storage.setBiome(x, z, bio);
return;
}
biome3D.setBiome(x, 0, z, bio);
}
public BiomeGrid getRawBiome() {
return storage;
int index = biomeIndex(x, y, z);
Biome biome = biomes[index];
return biome == null ? Biome.PLAINS : biome;
}
@Override
public void setBiome(int x, int y, int z, Biome bio) {
if (storage != null) {
storage.setBiome(x, y, z, bio);
return;
}
biome3D.setBiome(x, y, z, bio);
biomes[biomeIndex(x, y, z)] = bio;
}
@Override
public int getMinHeight() {
return rawChunkData.getMinHeight();
return minHeight;
}
@Override
public int getMaxHeight() {
return rawChunkData.getMaxHeight();
return maxHeight;
}
@Override
public synchronized void setBlock(int x, int y, int z, BlockData blockData) {
if (blockData instanceof IrisCustomData d)
blockData = d.getBase();
if (blockData instanceof IrisCustomData data) {
blockData = data.getBase();
}
rawChunkData.setBlock(x, y, z, blockData);
}
@Override
public BlockData getBlockData(int x, int y, int z) {
return rawChunkData.getBlockData(x, y, z);
}
@Deprecated
@Override
public synchronized void setBlock(int x, int y, int z, Material material) {
rawChunkData.setBlock(x, y, z, material);
}
@Deprecated
@Override
public synchronized void setBlock(int x, int y, int z, MaterialData material) {
rawChunkData.setBlock(x, y, z, material);
}
@Deprecated
@Override
public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
rawChunkData.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material);
}
@Deprecated
@Override
public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
rawChunkData.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, material);
}
@Override
public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
rawChunkData.setRegion(xMin, yMin, zMin, xMax, yMax, zMax, blockData);
}
@Deprecated
@Override
public synchronized Material getType(int x, int y, int z) {
return rawChunkData.getType(x, y, z);
}
@Deprecated
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return rawChunkData.getTypeAndData(x, y, z);
}
@Deprecated
@Override
public byte getData(int x, int y, int z) {
return rawChunkData.getData(x, y, z);
public BlockData getBlockData(int x, int y, int z) {
return rawChunkData.getBlockData(x, y, z);
}
@Override
public ChunkData getRaw() {
public ChunkData getChunkData() {
return rawChunkData;
}
@Override
public void setRaw(ChunkData data) {
rawChunkData = data;
}
@Override
public void inject(BiomeGrid biome) {
if (biome3D != null) {
biome3D.inject(biome);
}
private int biomeIndex(int x, int y, int z) {
int clampedX = x & (CHUNK_SIZE - 1);
int clampedZ = z & (CHUNK_SIZE - 1);
int clampedY = Math.max(minHeight, Math.min(maxHeight - 1, y)) - minHeight;
return (clampedY * CHUNK_SIZE + clampedZ) * CHUNK_SIZE + clampedX;
}
}

View File

@@ -1,167 +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 art.arcane.iris.engine.data.chunk;
import art.arcane.iris.Iris;
import art.arcane.iris.core.nms.BiomeBaseInjector;
import art.arcane.iris.util.common.data.IrisCustomData;
import art.arcane.iris.util.nbt.common.mca.Chunk;
import art.arcane.iris.util.nbt.common.mca.NBTWorld;
import lombok.AllArgsConstructor;
import lombok.Builder;
import org.bukkit.Material;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.material.MaterialData;
@Builder
@AllArgsConstructor
public class MCATerrainChunk implements TerrainChunk {
private final NBTWorld writer;
private final BiomeBaseInjector injector;
private final int ox;
private final int oz;
private final int minHeight;
private final int maxHeight;
private final Chunk mcaChunk;
@Override
public BiomeBaseInjector getBiomeBaseInjector() {
return injector;
}
@Override
public Biome getBiome(int x, int z) {
return Biome.THE_VOID;
}
@Override
public Biome getBiome(int x, int y, int z) {
return Biome.THE_VOID;
}
@Override
public void setBiome(int x, int z, Biome bio) {
setBiome(ox + x, 0, oz + z, bio);
}
@Override
public void setBiome(int x, int y, int z, Biome bio) {
mcaChunk.setBiomeAt((ox + x) & 15, y, (oz + z) & 15, writer.getBiomeId(bio));
}
@Override
public int getMinHeight() {
return minHeight;
}
@Override
public int getMaxHeight() {
return maxHeight;
}
@Override
public void setBlock(int x, int y, int z, BlockData blockData) {
int xx = (x + ox) & 15;
int zz = (z + oz) & 15;
if (y > getMaxHeight() || y < getMinHeight()) {
return;
}
if (blockData == null) {
Iris.error("NULL BD");
}
if (blockData instanceof IrisCustomData data)
blockData = data.getBase();
mcaChunk.setBlockStateAt(xx, y, zz, NBTWorld.getCompound(blockData), false);
}
@Override
public org.bukkit.block.data.BlockData getBlockData(int x, int y, int z) {
if (y > getMaxHeight()) {
y = getMaxHeight();
}
if (y < getMinHeight()) {
y = getMinHeight();
}
return NBTWorld.getBlockData(mcaChunk.getBlockStateAt((x + ox) & 15, y, (z + oz) & 15));
}
@Override
public ChunkGenerator.ChunkData getRaw() {
return null;
}
@Override
public void setRaw(ChunkGenerator.ChunkData data) {
}
@Override
public void inject(ChunkGenerator.BiomeGrid biome) {
}
@Override
public void setBlock(int x, int y, int z, Material material) {
}
@Override
public void setBlock(int x, int y, int z, MaterialData material) {
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, Material material) {
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, MaterialData material) {
}
@Override
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData) {
}
@Override
public Material getType(int x, int y, int z) {
return null;
}
@Override
public MaterialData getTypeAndData(int x, int y, int z) {
return null;
}
@Override
public byte getData(int x, int y, int z) {
return 0;
}
}

View File

@@ -18,116 +18,27 @@
package art.arcane.iris.engine.data.chunk;
import art.arcane.iris.core.nms.BiomeBaseInjector;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
import org.bukkit.generator.ChunkGenerator.ChunkData;
public interface TerrainChunk extends BiomeGrid, ChunkData {
public interface TerrainChunk {
static TerrainChunk create(World world) {
return new LinkedTerrainChunk(world);
}
static TerrainChunk create(World world, BiomeGrid grid) {
return new LinkedTerrainChunk(world, grid);
static TerrainChunk create(ChunkData raw) {
return new LinkedTerrainChunk(raw);
}
static TerrainChunk createUnsafe(World world, BiomeGrid grid) {
LinkedTerrainChunk ltc = new LinkedTerrainChunk(world, grid);
ltc.setUnsafe(true);
return ltc;
}
static TerrainChunk create(ChunkData raw, BiomeGrid grid) {
return new LinkedTerrainChunk(grid, raw);
}
BiomeBaseInjector getBiomeBaseInjector();
/**
* Get biome at x, z within chunk being generated
*
* @param x - 0-15
* @param z - 0-15
* @return Biome value
* @deprecated biomes are now 3-dimensional
*/
@Deprecated
Biome getBiome(int x, int z);
/**
* Get biome at x, z within chunk being generated
*
* @param x - 0-15
* @param y - 0-255
* @param z - 0-15
* @return Biome value
*/
Biome getBiome(int x, int y, int z);
/**
* Set biome at x, z within chunk being generated
*
* @param x - 0-15
* @param z - 0-15
* @param bio - Biome value
* @deprecated biomes are now 3-dimensional
*/
@Deprecated
void setBiome(int x, int z, Biome bio);
/**
* Set biome at x, z within chunk being generated
*
* @param x - 0-15
* @param y - 0-255
* @param z - 0-15
* @param bio - Biome value
*/
void setBiome(int x, int y, int z, Biome bio);
/**
* Get the maximum height for the chunk.
* <p>
* Setting blocks at or above this height will do nothing.
*
* @return the maximum height
*/
int getMinHeight();
int getMaxHeight();
/**
* Set the block at x,y,z in the chunk data to material.
* <p>
* Setting blocks outside the chunk's bounds does nothing.
*
* @param x the x location in the chunk from 0-15 inclusive
* @param y the y location in the chunk from 0 (inclusive) - maxHeight
* (exclusive)
* @param z the z location in the chunk from 0-15 inclusive
* @param blockData the type to set the block to
*/
void setBlock(int x, int y, int z, BlockData blockData);
/**
* Get the type and data of the block at x, y, z.
* <p>
* Getting blocks outside the chunk's bounds returns air.
*
* @param x the x location in the chunk from 0-15 inclusive
* @param y the y location in the chunk from 0 (inclusive) - maxHeight
* (exclusive)
* @param z the z location in the chunk from 0-15 inclusive
* @return the data of the block or the BlockData for air if x, y or z are
* outside the chunk's bounds
*/
void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, BlockData blockData);
BlockData getBlockData(int x, int y, int z);
ChunkData getRaw();
void setRaw(ChunkData data);
void inject(BiomeGrid biome);
ChunkData getChunkData();
}

View File

@@ -22,6 +22,7 @@ import art.arcane.iris.Iris;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineAssignedComponent;
import art.arcane.iris.engine.framework.EngineDecorator;
import art.arcane.iris.engine.mantle.EngineMantle;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisDecorationPart;
import art.arcane.iris.engine.object.IrisDecorator;
@@ -88,7 +89,10 @@ public abstract class IrisEngineDecorator extends EngineAssignedComponent implem
continue;
int yy = y + f.getModY();
BlockData r = getEngine().getMantle().get(x + f.getModX(), yy, z + f.getModZ());
BlockData r = getEngine().getMantle().getMantle().get(x + f.getModX(), yy, z + f.getModZ(), BlockData.class);
if (r == null) {
r = EngineMantle.AIR;
}
if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) {
found = true;
data.setFace(f, true);

View File

@@ -62,6 +62,7 @@ import art.arcane.volmlib.util.matter.MatterUpdate;
import art.arcane.volmlib.util.matter.slices.container.JigsawPieceContainer;
import art.arcane.iris.util.common.parallel.BurstExecutor;
import art.arcane.iris.util.common.parallel.MultiBurst;
import art.arcane.iris.util.common.reflect.KeyedType;
import art.arcane.iris.util.common.reflect.W;
import art.arcane.volmlib.util.scheduling.ChronoLatch;
import art.arcane.iris.util.common.scheduling.J;
@@ -149,7 +150,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
@BlockCoordinates
default void generate(int x, int z, TerrainChunk tc, boolean multicore) throws WrongEngineBroException {
generate(x, z, Hunk.view(tc), Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()), multicore);
generate(x, z, Hunk.view(tc), Hunk.viewBiomes(tc), multicore);
}
@BlockCoordinates
@@ -302,8 +303,13 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, c, () -> {
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
if (!TileData.setTileState(block, v.getData()))
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey());
if (!TileData.setTileState(block, v.getData())) {
NamespacedKey blockTypeKey = KeyedType.getKey(block.getType());
NamespacedKey tileTypeKey = KeyedType.getKey(v.getData().getMaterial());
String blockType = blockTypeKey == null ? block.getType().name() : blockTypeKey.toString();
String tileType = tileTypeKey == null ? v.getData().getMaterial().name() : tileTypeKey.toString();
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), blockType, tileType);
}
});
}, 0));
chunk.raiseFlagUnchecked(MantleFlag.CUSTOM, run(semaphore, c, () -> {

View File

@@ -15,6 +15,7 @@ import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.data.IrisCustomData;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.MatterCavern;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.bukkit.Bukkit;
@@ -97,7 +98,7 @@ public class WorldObjectPlacer implements IObjectPlacer {
@Override
public boolean isCarved(int x, int y, int z) {
return mantle.isCarved(x, y, z);
return mantle.getMantle().get(x, y, z, MatterCavern.class) != null;
}
@Override

View File

@@ -65,14 +65,17 @@ public class PlannedPiece {
public PlannedPiece(PlannedStructure structure, IrisPosition position, IrisJigsawPiece piece, IrisObjectRotation rot) {
this.structure = structure;
this.position = position;
this.data = piece.getLoader();
this.setRotation(rot);
this.ogObject = data.getObjectLoader().load(piece.getObject());
this.object = structure.rotated(piece, rotation);
this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset()));
this.piece.setLoadKey(piece.getLoadKey());
this.object.setLoadKey(piece.getObject());
this.ogObject.setLoadKey(piece.getObject());
this.data = piece.getLoader();
this.setRotation(rot);
this.ogObject = data.getObjectLoader().load(piece.getObject());
this.object = structure.rotated(piece, rotation);
if (this.ogObject == null || this.object == null) {
throw new IllegalStateException("Unable to create planned piece for object \"" + piece.getObject() + "\"");
}
this.piece = rotation.rotateCopy(piece, new IrisPosition(object.getShrinkOffset()));
this.piece.setLoadKey(piece.getLoadKey());
this.object.setLoadKey(piece.getObject());
this.ogObject.setLoadKey(piece.getObject());
this.connected = new KList<>();
this.realPositions = new KMap<>();

View File

@@ -357,6 +357,16 @@ public class PlannedStructure {
public IrisObject rotated(IrisJigsawPiece piece, IrisObjectRotation rotation) {
String key = piece.getObject() + "-" + rotation.hashCode();
return objectRotationCache.computeIfAbsent(key, (k) -> rotation.rotateCopy(data.getObjectLoader().load(piece.getObject())));
return objectRotationCache.computeIfAbsent(key, (k) -> {
IrisObject loaded = data.getObjectLoader().load(piece.getObject());
if (loaded == null) {
throw new IllegalStateException("Unable to load jigsaw object \"" + piece.getObject() + "\" for piece \"" + piece.getLoadKey() + "\"");
}
IrisObject rotatedObject = rotation.rotateCopy(loaded);
if (rotatedObject == null) {
throw new IllegalStateException("Unable to rotate jigsaw object \"" + piece.getObject() + "\" for piece \"" + piece.getLoadKey() + "\"");
}
return rotatedObject;
});
}
}

View File

@@ -24,12 +24,17 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.NamespacedKey;
import org.bukkit.attribute.Attributable;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.attribute.AttributeModifier.Operation;
import org.bukkit.inventory.EquipmentSlotGroup;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.Locale;
import java.util.UUID;
@Snippet("attribute-modifier")
@Accessors(chain = true)
@AllArgsConstructor
@@ -62,17 +67,35 @@ public class IrisAttributeModifier {
public void apply(RNG rng, ItemMeta meta) {
if (rng.nextDouble() < getChance()) {
meta.addAttributeModifier(getAttribute(), new AttributeModifier(getName(), getAmount(rng), getOperation()));
meta.addAttributeModifier(getAttribute(), createModifier(rng));
}
}
public void apply(RNG rng, Attributable meta) {
if (rng.nextDouble() < getChance()) {
meta.getAttribute(getAttribute()).addModifier(new AttributeModifier(getName(), getAmount(rng), getOperation()));
meta.getAttribute(getAttribute()).addModifier(createModifier(rng));
}
}
public double getAmount(RNG rng) {
return rng.d(getMinAmount(), getMaxAmount());
}
private AttributeModifier createModifier(RNG rng) {
NamespacedKey key = NamespacedKey.minecraft(generateModifierKey());
return new AttributeModifier(key, getAmount(rng), getOperation(), EquipmentSlotGroup.ANY);
}
private String generateModifierKey() {
String source = getName() == null ? "modifier" : getName();
String normalized = source.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9/._-]", "_");
if (normalized.isBlank()) {
normalized = "modifier";
}
if (normalized.length() > 32) {
normalized = normalized.substring(0, 32);
}
String random = UUID.randomUUID().toString().replace("-", "");
return normalized + "_" + random;
}
}

View File

@@ -29,6 +29,8 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.EntityType;
import java.awt.*;
import java.util.Locale;
@@ -142,9 +144,23 @@ public class IrisBiomeCustom {
KMap<IrisBiomeCustomSpawnType, JSONArray> groups = new KMap<>();
for (IrisBiomeCustomSpawn i : getSpawns()) {
JSONArray g = groups.computeIfAbsent(i.getGroup(), (k) -> new JSONArray());
if (i == null) {
continue;
}
EntityType type = i.getType();
if (type == null) {
Iris.warn("Skipping custom biome spawn with null entity type in biome " + getId());
continue;
}
IrisBiomeCustomSpawnType group = i.getGroup() == null ? IrisBiomeCustomSpawnType.MISC : i.getGroup();
JSONArray g = groups.computeIfAbsent(group, (k) -> new JSONArray());
JSONObject o = new JSONObject();
o.put("type", "minecraft:" + i.getType().name().toLowerCase());
NamespacedKey key = type.getKey();
if (key == null) {
Iris.warn("Skipping custom biome spawn with unresolved entity key in biome " + getId());
continue;
}
o.put("type", key.toString());
o.put("weight", i.getWeight());
o.put("minCount", i.getMinCount());
o.put("maxCount", i.getMaxCount());

View File

@@ -24,13 +24,16 @@ import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.annotations.*;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.scheduling.ChronoLatch;
import art.arcane.iris.util.common.reflect.KeyedType;
import art.arcane.iris.util.common.scheduling.J;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -38,6 +41,8 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import java.util.Locale;
@Snippet("effect")
@Accessors(chain = true)
@NoArgsConstructor
@@ -164,8 +169,9 @@ public class IrisEffect {
}
try {
for (PotionEffectType i : PotionEffectType.values()) {
if (i.getName().toUpperCase().replaceAll("\\Q \\E", "_").equals(getPotionEffect())) {
for (PotionEffectType i : Registry.EFFECT) {
NamespacedKey key = KeyedType.getKey(i);
if (key != null && key.getKey().toUpperCase(Locale.ROOT).replaceAll("\\Q \\E", "_").equals(getPotionEffect())) {
t = i;
return t;

View File

@@ -26,6 +26,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.meta.EnchantmentStorageMeta;
import org.bukkit.inventory.meta.ItemMeta;
@@ -58,7 +59,7 @@ public class IrisEnchantment {
public void apply(RNG rng, ItemMeta meta) {
try {
Enchantment enchant = Enchantment.getByKey(NamespacedKey.minecraft(getEnchantment()));
Enchantment enchant = Registry.ENCHANTMENT.get(NamespacedKey.minecraft(getEnchantment()));
if (enchant == null) {
Iris.warn("Unknown Enchantment: " + getEnchantment());
return;

View File

@@ -423,7 +423,11 @@ public class IrisEntity extends IrisRegistrant {
return null;
}
if (type.equals(EntityType.UNKNOWN) && !isSpecialType()) {
if (type == null) {
return null;
}
if (EntityType.UNKNOWN.equals(type) && !isSpecialType()) {
return null;
}

View File

@@ -37,15 +37,17 @@ import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.material.Colorable;
import java.awt.*;
import java.util.Optional;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.inventory.meta.components.CustomModelDataComponent;
import org.bukkit.material.Colorable;
import java.awt.*;
import java.util.List;
import java.util.Optional;
@Snippet("loot")
@Accessors(chain = true)
@@ -176,9 +178,11 @@ public class IrisLoot {
m.addItemFlags(i);
}
if (getCustomModel() != null) {
m.setCustomModelData(getCustomModel());
}
if (getCustomModel() != null) {
CustomModelDataComponent customModelData = m.getCustomModelDataComponent();
customModelData.setFloats(List.of(getCustomModel().floatValue()));
m.setCustomModelDataComponent(customModelData);
}
if (is.getType().getMaxDurability() > 0 && m instanceof Damageable d) {
int max = is.getType().getMaxDurability();
@@ -194,10 +198,9 @@ public class IrisLoot {
colorable.setColor(getDyeColor());
}
if (displayName != null) {
m.setLocalizedName(C.translateAlternateColorCodes('&', displayName));
m.setDisplayName(C.translateAlternateColorCodes('&', displayName));
}
if (displayName != null) {
m.setDisplayName(C.translateAlternateColorCodes('&', displayName));
}
KList<String> lore = new KList<>();

View File

@@ -21,14 +21,19 @@ package art.arcane.iris.engine.object;
import art.arcane.iris.Iris;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.iris.engine.object.annotations.*;
import art.arcane.iris.util.common.reflect.KeyedType;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.LivingEntity;
import org.bukkit.Registry;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.util.Locale;
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@@ -65,8 +70,9 @@ public class IrisPotionEffect {
}
try {
for (PotionEffectType i : PotionEffectType.values()) {
if (i.getName().toUpperCase().replaceAll("\\Q \\E", "_").equals(getPotionEffect())) {
for (PotionEffectType i : Registry.EFFECT) {
NamespacedKey key = KeyedType.getKey(i);
if (key != null && key.getKey().toUpperCase(Locale.ROOT).replaceAll("\\Q \\E", "_").equals(getPotionEffect())) {
t = i;
return t;

View File

@@ -4,6 +4,7 @@ import art.arcane.iris.core.nms.container.Pair;
import art.arcane.iris.engine.data.cache.AtomicCache;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.iris.util.common.reflect.KeyedType;
import art.arcane.iris.util.common.scheduling.J;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
@@ -14,6 +15,8 @@ import org.bukkit.*;
import org.bukkit.block.*;
import org.bukkit.block.banner.Pattern;
import org.bukkit.block.banner.PatternType;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
@@ -22,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -126,11 +130,11 @@ public class LegacyTileData extends TileData {
dyeColor = DyeColor.values()[in.readByte()];
}
@SuppressWarnings("deprecation")
private static SignHandler fromBukkit(BlockState blockState, Material type) {
if (!signsTag().isTagged(type) || !(blockState instanceof Sign sign))
return null;
return new SignHandler(sign.getLine(0), sign.getLine(1), sign.getLine(2), sign.getLine(3), sign.getColor());
SignSide front = sign.getSide(Side.FRONT);
return new SignHandler(front.getLine(0), front.getLine(1), front.getLine(2), front.getLine(3), front.getColor());
}
@Override
@@ -155,11 +159,18 @@ public class LegacyTileData extends TileData {
@Override
public void toBukkit(Block block) {
Sign sign = (Sign) block.getState();
sign.setLine(0, line1);
sign.setLine(1, line2);
sign.setLine(2, line3);
sign.setLine(3, line4);
sign.setColor(dyeColor);
SignSide front = sign.getSide(Side.FRONT);
SignSide back = sign.getSide(Side.BACK);
front.setLine(0, line1);
front.setLine(1, line2);
front.setLine(2, line3);
front.setLine(3, line4);
front.setColor(dyeColor);
back.setLine(0, line1);
back.setLine(1, line2);
back.setLine(2, line3);
back.setLine(3, line4);
back.setColor(dyeColor);
sign.update();
}
}
@@ -170,7 +181,33 @@ public class LegacyTileData extends TileData {
private final EntityType type;
private SpawnerHandler(DataInputStream in) throws IOException {
type = EntityType.values()[in.readShort()];
EntityType resolved = null;
if (in.markSupported()) {
in.mark(Integer.MAX_VALUE);
}
try {
String keyString = in.readUTF();
NamespacedKey key = NamespacedKey.fromString(keyString);
resolved = key == null ? null : Registry.ENTITY_TYPE.get(key);
if (resolved == null && in.markSupported()) {
in.reset();
}
} catch (Throwable ignored) {
if (in.markSupported()) {
in.reset();
}
}
if (resolved == null) {
short legacyOrdinal = in.readShort();
EntityType[] legacyValues = EntityType.values();
if (legacyOrdinal >= 0 && legacyOrdinal < legacyValues.length) {
resolved = legacyValues[legacyOrdinal];
}
}
type = resolved == null ? EntityType.PIG : resolved;
}
private static SpawnerHandler fromBukkit(BlockState blockState, Material material) {
@@ -191,7 +228,8 @@ public class LegacyTileData extends TileData {
@Override
public void toBinary(DataOutputStream out) throws IOException {
out.writeShort(type.ordinal());
NamespacedKey key = KeyedType.getKey(type);
out.writeUTF(key == null ? type.name() : key.toString());
}
@Override
@@ -209,13 +247,55 @@ public class LegacyTileData extends TileData {
private final DyeColor baseColor;
private BannerHandler(DataInputStream in) throws IOException {
baseColor = DyeColor.values()[in.readByte()];
DyeColor[] dyeColors = DyeColor.values();
int baseColorIndex = in.readUnsignedByte();
baseColor = baseColorIndex >= 0 && baseColorIndex < dyeColors.length ? dyeColors[baseColorIndex] : DyeColor.WHITE;
patterns = new KList<>();
int listSize = in.readByte();
int listSize = in.readUnsignedByte();
if (in.markSupported()) {
in.mark(Integer.MAX_VALUE);
}
boolean parsedKeyed = false;
try {
KList<Pattern> keyedPatterns = new KList<>();
for (int i = 0; i < listSize; i++) {
int colorIndex = in.readUnsignedByte();
DyeColor color = colorIndex >= 0 && colorIndex < dyeColors.length ? dyeColors[colorIndex] : DyeColor.WHITE;
NamespacedKey patternKey = NamespacedKey.fromString(in.readUTF());
PatternType pattern = patternKey == null ? null : Registry.BANNER_PATTERN.get(patternKey);
if (pattern == null) {
throw new IOException("Unknown banner pattern key");
}
keyedPatterns.add(new Pattern(color, pattern));
}
patterns.addAll(keyedPatterns);
parsedKeyed = true;
} catch (Throwable ignored) {
if (in.markSupported()) {
in.reset();
}
}
if (parsedKeyed) {
return;
}
PatternType[] legacyPatternTypes = PatternType.values();
PatternType fallbackPattern = Registry.BANNER_PATTERN.get(NamespacedKey.minecraft("base"));
if (fallbackPattern == null && legacyPatternTypes.length > 0) {
fallbackPattern = legacyPatternTypes[0];
}
for (int i = 0; i < listSize; i++) {
DyeColor color = DyeColor.values()[in.readByte()];
PatternType pattern = PatternType.values()[in.readByte()];
patterns.add(new Pattern(color, pattern));
int colorIndex = in.readUnsignedByte();
DyeColor color = colorIndex >= 0 && colorIndex < dyeColors.length ? dyeColors[colorIndex] : DyeColor.WHITE;
int legacyPatternIndex = in.readUnsignedByte();
PatternType pattern = legacyPatternIndex >= 0 && legacyPatternIndex < legacyPatternTypes.length ? legacyPatternTypes[legacyPatternIndex] : fallbackPattern;
if (pattern != null) {
patterns.add(new Pattern(color, pattern));
}
}
}
@@ -241,7 +321,11 @@ public class LegacyTileData extends TileData {
out.writeByte(patterns.size());
for (Pattern i : patterns) {
out.writeByte(i.getColor().ordinal());
out.writeByte(i.getPattern().ordinal());
NamespacedKey key = KeyedType.getKey(i.getPattern());
if (key == null) {
key = NamespacedKey.minecraft("base");
}
out.writeUTF(key.toString());
}
}
@@ -262,7 +346,11 @@ public class LegacyTileData extends TileData {
return new Tag<>() {
@Override
public boolean isTagged(@NotNull Material item) {
return item.getKey().getKey().endsWith("_sign");
NamespacedKey key = KeyedType.getKey(item);
if (key != null) {
return key.getKey().endsWith("_sign");
}
return item.name().toLowerCase(Locale.ROOT).endsWith("_sign");
}
@NotNull

View File

@@ -20,11 +20,14 @@ package art.arcane.iris.engine.object;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.Strictness;
import art.arcane.iris.Iris;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.util.common.reflect.KeyedType;
import art.arcane.volmlib.util.collection.KMap;
import lombok.*;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
@@ -39,7 +42,7 @@ import java.io.IOException;
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TileData implements Cloneable {
private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setLenient().create();
private static final Gson gson = new GsonBuilder().disableHtmlEscaping().setStrictness(Strictness.LENIENT).create();
@NonNull
private Material material;
@@ -125,7 +128,13 @@ public class TileData implements Cloneable {
}
public void toBinary(DataOutputStream out) throws IOException {
out.writeUTF(material == null ? "" : material.getKey().toString());
if (material == null) {
out.writeUTF("");
} else {
NamespacedKey key = KeyedType.getKey(material);
String value = key == null ? material.name() : key.toString();
out.writeUTF(value);
}
out.writeUTF(gson.toJson(properties));
}
@@ -139,6 +148,8 @@ public class TileData implements Cloneable {
@Override
public String toString() {
return material.getKey() + gson.toJson(properties);
NamespacedKey key = KeyedType.getKey(material);
String value = key == null ? String.valueOf(material) : key.toString();
return value + gson.toJson(properties);
}
}

View File

@@ -4,12 +4,11 @@ import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.framework.ListFunction;
import art.arcane.volmlib.util.collection.KList;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.loot.LootTable;
import org.bukkit.loot.LootTables;
import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class LootTableKeyFunction implements ListFunction<KList<String>> {
@Override
@@ -24,7 +23,7 @@ public class LootTableKeyFunction implements ListFunction<KList<String>> {
@Override
public KList<String> apply(IrisData data) {
return StreamSupport.stream(Registry.LOOT_TABLES.spliterator(), false)
return Arrays.stream(LootTables.values())
.map(LootTables::getLootTable)
.map(LootTable::getKey)
.map(NamespacedKey::toString)

View File

@@ -35,8 +35,7 @@ import art.arcane.iris.engine.object.IrisWorld;
import art.arcane.iris.engine.object.StudioMode;
import art.arcane.iris.engine.platform.studio.StudioGenerator;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.data.IrisBiomeStorage;
import art.arcane.iris.util.project.hunk.view.BiomeGridHunkHolder;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.iris.util.project.hunk.view.ChunkDataHunkHolder;
import art.arcane.volmlib.util.io.ReactiveFolder;
import art.arcane.volmlib.util.scheduling.ChronoLatch;
@@ -47,6 +46,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.block.Biome;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -220,8 +220,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
if (acquireWait >= 5000L) {
Iris.warn("Chunk replacement waited " + acquireWait + "ms for load lock at " + x + "," + z + ".");
}
IrisBiomeStorage st = new IrisBiomeStorage();
TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
TerrainChunk tc = TerrainChunk.create(world);
this.world.bind(world);
phase = "engine-generate";
@@ -481,16 +480,15 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
return;
}
computeStudioGenerator();
TerrainChunk tc = TerrainChunk.create(d, new IrisBiomeStorage());
TerrainChunk tc = TerrainChunk.create(d);
this.world.bind(world);
if (studioGenerator != null) {
studioGenerator.generateChunk(engine, tc, x, z);
} else {
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc);
BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight());
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(d);
Hunk<Biome> biomes = Hunk.viewBiomes(tc);
engine.generate(x << 4, z << 4, blocks, biomes, IrisSettings.get().getGenerator().useMulticore);
blocks.apply();
biomes.apply();
}
Iris.debug("Generated " + x + " " + z);
@@ -526,11 +524,6 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
return populators;
}
@Override
public boolean isParallelCapable() {
return true;
}
@Override
public boolean shouldGenerateCaves() {
return false;
@@ -562,8 +555,8 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
}
@Override
public boolean shouldGenerateBedrock() {
return false;
public void generateBedrock(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull ChunkData chunkData) {
}
@Nullable

View File

@@ -1,29 +0,0 @@
package art.arcane.iris.engine.platform;
import org.bukkit.block.Biome;
import org.bukkit.generator.ChunkGenerator;
import org.jetbrains.annotations.NotNull;
public class DummyBiomeGrid implements ChunkGenerator.BiomeGrid {
@NotNull
@Override
public Biome getBiome(int x, int z) {
return null;
}
@NotNull
@Override
public Biome getBiome(int x, int y, int z) {
return null;
}
@Override
public void setBiome(int x, int z, @NotNull Biome bio) {
}
@Override
public void setBiome(int x, int y, int z, @NotNull Biome bio) {
}
}

View File

@@ -11,11 +11,14 @@ import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.core.nms.container.BlockProperty;
import art.arcane.iris.core.service.ExternalDataSVC;
import art.arcane.iris.util.common.data.registry.Materials;
import art.arcane.iris.util.common.reflect.KeyedType;
import it.unimi.dsi.fastutil.ints.IntSet;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.data.BlockData;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
public class B {
@@ -103,7 +106,9 @@ public class B {
protected KMap<List<String>, List<BlockProperty>> loadExternalBlockStates() {
KMap<List<BlockProperty>, List<String>> flipped = new KMap<>();
INMS.get().getBlockProperties().forEach((k, v) -> {
flipped.computeIfAbsent(v, $ -> new KList<>()).add(k.getKey().toString());
NamespacedKey key = KeyedType.getKey(k);
String serialized = key == null ? k.name().toLowerCase(Locale.ROOT) : key.toString();
flipped.computeIfAbsent(v, $ -> new KList<>()).add(serialized);
});
var emptyStates = flipped.computeIfAbsent(new KList<>(0), $ -> new KList<>());

View File

@@ -443,7 +443,7 @@ public enum C {
}
public static String compress(String c) {
return BaseComponent.toLegacyText(TextComponent.fromLegacyText(c));
return BaseComponent.toLegacyText(TextComponent.fromLegacy(c));
}
/**
@@ -768,4 +768,4 @@ public enum C {
default -> (byte) 15;
};
}
}
}

View File

@@ -26,15 +26,18 @@ import art.arcane.volmlib.util.nbt.mca.MCAWorldStoreSupport;
import art.arcane.volmlib.util.nbt.mca.MCAWorldRuntimeSupport;
import art.arcane.volmlib.util.nbt.mca.NBTWorldSupport;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.reflect.KeyedType;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
import art.arcane.iris.util.common.parallel.HyperLock;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import java.io.File;
import java.util.Map;
import java.util.Locale;
public class NBTWorld {
private static final BlockData AIR = B.get("AIR");
@@ -43,7 +46,10 @@ public class NBTWorld {
B::getAir,
blockData -> blockData.getAsString(true),
blockData -> {
NamespacedKey key = blockData.getMaterial().getKey();
NamespacedKey key = KeyedType.getKey(blockData.getMaterial());
if (key == null) {
return "minecraft:" + blockData.getMaterial().name().toLowerCase(Locale.ROOT);
}
return key.getNamespace() + ":" + key.getKey();
}
);
@@ -127,8 +133,9 @@ public class NBTWorld {
private static Map<Biome, Integer> computeBiomeIDs() {
Map<Biome, Integer> biomeIds = new KMap<>();
for (Biome biome : Biome.values()) {
if (!biome.name().equals("CUSTOM")) {
for (Biome biome : Registry.BIOME) {
NamespacedKey key = biome.getKeyOrNull();
if (key != null && !key.getKey().equals("custom")) {
biomeIds.put(biome, INMS.get().getBiomeId(biome));
}
}

View File

@@ -6,14 +6,16 @@ import com.google.gson.reflect.TypeToken;
import art.arcane.iris.util.common.data.registry.RegistryTypeAdapter;
import art.arcane.iris.util.common.data.registry.RegistryUtil;
import org.bukkit.Keyed;
import org.bukkit.NamespacedKey;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
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()
@@ -23,15 +25,48 @@ public class KeyedType {
}
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);
if (KEYED_LENIENT) return !RegistryUtil.lookup(type).isEmpty();
return Keyed.class.isAssignableFrom(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()));
return (TypeAdapter<T>) RegistryTypeAdapter.of(type.getRawType());
}
@Nullable
public static NamespacedKey getKey(Object value) {
if (value == null) {
return null;
}
if (value instanceof Keyed keyed) {
NamespacedKey key = keyed.getKey();
if (key != null) {
return key;
}
}
NamespacedKey keyOrThrow = invokeKey(value, "getKeyOrThrow");
if (keyOrThrow != null) {
return keyOrThrow;
}
return invokeKey(value, "getKey");
}
@Nullable
private static NamespacedKey invokeKey(Object value, String methodName) {
try {
Method method = value.getClass().getMethod(methodName);
Object result = method.invoke(value);
if (result instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
return null;
} catch (Throwable ignored) {
return null;
}
}
}

View File

@@ -24,7 +24,7 @@ import art.arcane.volmlib.util.network.DownloadMonitor;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
public class DownloadJob implements Job {
private final DL.Download download;
@@ -34,7 +34,7 @@ public class DownloadJob implements Job {
public DownloadJob(String url, File destination) throws MalformedURLException {
tw = 1;
cw = 0;
download = new DL.Download(new URL(url), destination, DL.DownloadFlag.CALCULATE_SIZE);
download = new DL.Download(URI.create(url).toURL(), destination, DL.DownloadFlag.CALCULATE_SIZE);
download.monitor(new DownloadMonitor() {
@Override
public void onUpdate(DL.DownloadState state, double progress, long elapsed, long estimated, long bps, long iobps, long size, long downloaded, long buffer, double bufferuse) {

View File

@@ -19,6 +19,7 @@
package art.arcane.iris.util.project.hunk;
import art.arcane.iris.Iris;
import art.arcane.iris.engine.data.chunk.TerrainChunk;
import art.arcane.iris.engine.object.IrisPosition;
import art.arcane.volmlib.util.function.*;
import art.arcane.volmlib.util.hunk.HunkComputeSupport;
@@ -38,7 +39,6 @@ import art.arcane.iris.util.project.stream.interpolation.Interpolated;
import org.bukkit.Chunk;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
import org.bukkit.generator.ChunkGenerator.ChunkData;
import java.io.IOException;
@@ -94,14 +94,18 @@ public interface Hunk<T> extends HunkLike<T> {
return adapt(new art.arcane.volmlib.util.hunk.view.FunctionalHunkView<>(unwrap(src), reader, writer));
}
static Hunk<Biome> view(BiomeGrid biome, int minHeight, int maxHeight) {
return new BiomeGridHunkView(biome, minHeight, maxHeight);
}
static <T> Hunk<T> fringe(Hunk<T> i, Hunk<T> o) {
return adapt(new art.arcane.volmlib.util.hunk.view.FringedHunkView<>(unwrap(i), unwrap(o)));
}
static Hunk<BlockData> view(TerrainChunk src) {
return new ChunkDataHunkView(src.getChunkData());
}
static Hunk<Biome> viewBiomes(TerrainChunk src) {
return new TerrainChunkBiomeHunkView(src);
}
static Hunk<BlockData> view(ChunkData src) {
return new ChunkDataHunkView(src);
}

View File

@@ -1,45 +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 art.arcane.iris.util.project.hunk.view;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.engine.data.chunk.LinkedTerrainChunk;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.volmlib.util.hunk.view.BiomeGridForceSupport;
import org.bukkit.block.Biome;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
@SuppressWarnings("ClassCanBeRecord")
public class BiomeGridHunkHolder extends art.arcane.volmlib.util.hunk.view.BiomeGridHunkHolder implements Hunk<Biome> {
public BiomeGridHunkHolder(BiomeGrid chunk, int minHeight, int maxHeight) {
super(chunk, minHeight, maxHeight);
}
public void forceBiomeBaseInto(int x, int y, int z, Object somethingVeryDirty) {
BiomeGridForceSupport.forceBiomeBaseInto(
getChunk(),
getMinHeight(),
x,
y,
z,
somethingVeryDirty,
chunk -> chunk instanceof LinkedTerrainChunk ? ((LinkedTerrainChunk) chunk).getRawBiome() : chunk,
(wx, wy, wz, dirty, target) -> INMS.get().forceBiomeInto(wx, wy, wz, dirty, target));
}
}

View File

@@ -1,45 +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 art.arcane.iris.util.project.hunk.view;
import art.arcane.iris.core.nms.INMS;
import art.arcane.iris.engine.data.chunk.LinkedTerrainChunk;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.volmlib.util.hunk.view.BiomeGridForceSupport;
import org.bukkit.block.Biome;
import org.bukkit.generator.ChunkGenerator.BiomeGrid;
@SuppressWarnings("ClassCanBeRecord")
public class BiomeGridHunkView extends art.arcane.volmlib.util.hunk.view.BiomeGridHunkView implements Hunk<Biome> {
public BiomeGridHunkView(BiomeGrid chunk, int minHeight, int maxHeight) {
super(chunk, minHeight, maxHeight);
}
public void forceBiomeBaseInto(int x, int y, int z, Object somethingVeryDirty) {
BiomeGridForceSupport.forceBiomeBaseInto(
getChunk(),
getMinHeight(),
x,
y,
z,
somethingVeryDirty,
chunk -> chunk instanceof LinkedTerrainChunk ? ((LinkedTerrainChunk) chunk).getRawBiome() : chunk,
(wx, wy, wz, dirty, target) -> INMS.get().forceBiomeInto(wx, wy, wz, dirty, target));
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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 art.arcane.iris.util.project.hunk.view;
import art.arcane.iris.engine.data.chunk.TerrainChunk;
import art.arcane.iris.util.project.hunk.Hunk;
import art.arcane.iris.util.project.hunk.storage.StorageHunk;
import org.bukkit.block.Biome;
public class TerrainChunkBiomeHunkView extends StorageHunk<Biome> implements Hunk<Biome> {
private final TerrainChunk chunk;
public TerrainChunkBiomeHunkView(TerrainChunk chunk) {
super(16, chunk.getMaxHeight() - chunk.getMinHeight(), 16);
this.chunk = chunk;
}
@Override
public void setRaw(int x, int y, int z, Biome biome) {
chunk.setBiome(x, y + chunk.getMinHeight(), z, biome);
}
@Override
public Biome getRaw(int x, int y, int z) {
return chunk.getBiome(x, y + chunk.getMinHeight(), z);
}
}

View File

@@ -61,7 +61,7 @@ enum class Mode(private val color: C) {
"",
padd2 + color + " Iris, " + C.AQUA + "Iris, Dimension Engine " + C.RED + "[" + releaseTrain + " RELEASE]",
padd2 + C.GRAY + " Version: " + color + version,
padd2 + C.GRAY + " By: " + color + "Arcane Arts (Volmit Software)",
padd2 + C.GRAY + " By: " + color + "Volmit Software (Arcane Arts)",
padd2 + C.GRAY + " Server: " + color + serverVersion,
padd2 + C.GRAY + " Java: " + color + javaVersion + C.GRAY + " | Date: " + color + startupDate,
padd2 + C.GRAY + " Commit: " + color + BuildConstants.COMMIT + C.GRAY + "/" + color + BuildConstants.ENVIRONMENT,

View File

@@ -25,6 +25,7 @@ import org.bukkit.craftbukkit.v1_21_R7.CraftWorld;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
@@ -38,6 +39,7 @@ public class CustomBiomeSource extends BiomeSource {
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final RNG rng;
private final KMap<String, Holder<Biome>> customBiomes;
private final Holder<Biome> fallbackBiome;
public CustomBiomeSource(long seed, Engine engine, World world) {
this.engine = engine;
@@ -45,24 +47,37 @@ public class CustomBiomeSource extends BiomeSource {
this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null);
this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null);
this.rng = new RNG(engine.getSeedManager().getBiome());
this.customBiomes = fillCustomBiomes(biomeCustomRegistry, engine);
this.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry);
this.customBiomes = fillCustomBiomes(this.biomeCustomRegistry, engine, this.fallbackBiome);
}
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine) {
List<Holder<Biome>> b = new ArrayList<>();
private static List<Holder<Biome>> getAllBiomes(Registry<Biome> customRegistry, Registry<Biome> registry, Engine engine, Holder<Biome> fallback) {
LinkedHashSet<Holder<Biome>> biomes = new LinkedHashSet<>();
if (fallback != null) {
biomes.add(fallback);
}
for (IrisBiome i : engine.getAllBiomes()) {
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
b.add(customRegistry.get(customRegistry.getResourceKey(customRegistry
.getValue(Identifier.fromNamespaceAndPath(engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT), j.getId().toLowerCase(java.util.Locale.ROOT)))).get()).get());
Holder<Biome> customHolder = resolveCustomBiomeHolder(customRegistry, engine, j.getId());
if (customHolder != null) {
biomes.add(customHolder);
} else if (fallback != null) {
biomes.add(fallback);
}
}
} else {
b.add(NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative()));
Holder<Biome> vanillaHolder = NMSBinding.biomeToBiomeBase(registry, i.getVanillaDerivative());
if (vanillaHolder != null) {
biomes.add(vanillaHolder);
} else if (fallback != null) {
biomes.add(fallback);
}
}
}
return b;
return new ArrayList<>(biomes);
}
private static Object getFor(Class<?> type, Object source) {
@@ -117,28 +132,28 @@ public class CustomBiomeSource extends BiomeSource {
((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer()))
.lookup(Registries.BIOME).orElse(null),
((CraftWorld) engine.getWorld().realWorld()).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null),
engine).stream();
engine,
fallbackBiome).stream();
}
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine) {
private KMap<String, Holder<Biome>> fillCustomBiomes(Registry<Biome> customRegistry, Engine engine, Holder<Biome> fallback) {
KMap<String, Holder<Biome>> m = new KMap<>();
if (customRegistry == null) {
return m;
}
for (IrisBiome i : engine.getAllBiomes()) {
if (i.isCustom()) {
for (IrisBiomeCustom j : i.getCustomDerivitives()) {
Identifier resourceLocation = Identifier.fromNamespaceAndPath(engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT), j.getId().toLowerCase(java.util.Locale.ROOT));
Biome biome = customRegistry.getValue(resourceLocation);
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
if (optionalBiomeKey.isEmpty()) {
Holder<Biome> holder = resolveCustomBiomeHolder(customRegistry, engine, j.getId());
if (holder == null) {
if (fallback != null) {
m.put(j.getId(), fallback);
}
Iris.error("Cannot find biome for IrisBiomeCustom " + j.getId() + " from engine " + engine.getName());
continue;
}
ResourceKey<Biome> biomeKey = optionalBiomeKey.get();
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(biomeKey);
if (optionalReferenceHolder.isEmpty()) {
Iris.error("Cannot find reference to biome " + biomeKey + " for engine " + engine.getName());
continue;
}
m.put(j.getId(), optionalReferenceHolder.get());
m.put(j.getId(), holder);
}
}
}
@@ -159,11 +174,89 @@ public class CustomBiomeSource extends BiomeSource {
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
int m = (y - engine.getMinHeight()) << 2;
IrisBiome ib = engine.getComplex().getTrueBiomeStream().get(x << 2, z << 2);
if (ib.isCustom()) {
return customBiomes.get(ib.getCustomBiome(rng, x << 2, m, z << 2).getId());
} else {
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
return NMSBinding.biomeToBiomeBase(biomeRegistry, v);
if (ib == null) {
return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
}
if (ib.isCustom()) {
IrisBiomeCustom custom = ib.getCustomBiome(rng, x << 2, m, z << 2);
if (custom != null) {
Holder<Biome> holder = customBiomes.get(custom.getId());
if (holder != null) {
return holder;
}
}
return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
}
org.bukkit.block.Biome v = ib.getSkyBiome(rng, x << 2, m, z << 2);
Holder<Biome> holder = NMSBinding.biomeToBiomeBase(biomeRegistry, v);
if (holder != null) {
return holder;
}
return resolveFallbackBiome(biomeRegistry, biomeCustomRegistry);
}
}
private static Holder<Biome> resolveCustomBiomeHolder(Registry<Biome> customRegistry, Engine engine, String customBiomeId) {
if (customRegistry == null || engine == null || customBiomeId == null || customBiomeId.isBlank()) {
return null;
}
Identifier resourceLocation = Identifier.fromNamespaceAndPath(
engine.getDimension().getLoadKey().toLowerCase(java.util.Locale.ROOT),
customBiomeId.toLowerCase(java.util.Locale.ROOT)
);
Biome biome = customRegistry.getValue(resourceLocation);
if (biome == null) {
return null;
}
Optional<ResourceKey<Biome>> optionalBiomeKey = customRegistry.getResourceKey(biome);
if (optionalBiomeKey.isEmpty()) {
return null;
}
Optional<Holder.Reference<Biome>> optionalReferenceHolder = customRegistry.get(optionalBiomeKey.get());
if (optionalReferenceHolder.isEmpty()) {
return null;
}
return optionalReferenceHolder.get();
}
private static Holder<Biome> resolveFallbackBiome(Registry<Biome> registry, Registry<Biome> customRegistry) {
Holder<Biome> plains = NMSBinding.biomeToBiomeBase(registry, org.bukkit.block.Biome.PLAINS);
if (plains != null) {
return plains;
}
Holder<Biome> vanilla = firstHolder(registry);
if (vanilla != null) {
return vanilla;
}
return firstHolder(customRegistry);
}
private static Holder<Biome> firstHolder(Registry<Biome> registry) {
if (registry == null) {
return null;
}
for (Biome biome : registry) {
Optional<ResourceKey<Biome>> optionalBiomeKey = registry.getResourceKey(biome);
if (optionalBiomeKey.isEmpty()) {
continue;
}
Optional<Holder.Reference<Biome>> optionalHolder = registry.get(optionalBiomeKey.get());
if (optionalHolder.isPresent()) {
return optionalHolder.get();
}
}
return null;
}
}

View File

@@ -402,12 +402,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
delegate.validate();
}
@Override
@SuppressWarnings("deprecation")
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> holder) {
return delegate.getBiomeGenerationSettings(holder);
}
static {
Field biomeSource = null;
for (Field field : ChunkGenerator.class.getDeclaredFields()) {

View File

@@ -108,7 +108,6 @@ public class NMSBinding implements INMSBinding {
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
private Field biomeStorageCache = null;
private static Object getFor(Class<?> type, Object source) {
Object o = fieldFor(type, source);
@@ -390,7 +389,11 @@ public class NMSBinding implements INMSBinding {
@Override
public KList<Biome> getBiomes() {
return new KList<>(Biome.values()).qadd(Biome.CHERRY_GROVE).qdel(Biome.CUSTOM);
KList<Biome> biomes = new KList<>();
for (Biome biome : org.bukkit.Registry.BIOME) {
biomes.add(biome);
}
return biomes;
}
@Override
@@ -407,7 +410,12 @@ public class NMSBinding implements INMSBinding {
}
}
return biome.ordinal();
List<Biome> biomes = new ArrayList<>();
for (Biome entry : org.bukkit.Registry.BIOME) {
biomes.add(entry);
}
int index = biomes.indexOf(biome);
return Math.max(index, 0);
}
private MCAIdMap<net.minecraft.world.level.biome.Biome> getBiomeMapping() {
@@ -488,38 +496,6 @@ public class NMSBinding implements INMSBinding {
c.markUnsaved();
}
@Override
public void forceBiomeInto(int x, int y, int z, Object somethingVeryDirty, ChunkGenerator.BiomeGrid chunk) {
try {
ChunkAccess s = (ChunkAccess) getFieldForBiomeStorage(chunk).get(chunk);
Holder<net.minecraft.world.level.biome.Biome> biome = (Holder<net.minecraft.world.level.biome.Biome>) somethingVeryDirty;
s.setBiome(x, y, z, biome);
} catch (IllegalAccessException e) {
Iris.reportError(e);
e.printStackTrace();
}
}
private Field getFieldForBiomeStorage(Object storage) {
Field f = biomeStorageCache;
if (f != null) {
return f;
}
try {
f = storage.getClass().getDeclaredField("biome");
f.setAccessible(true);
return f;
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
Iris.error(storage.getClass().getCanonicalName());
}
biomeStorageCache = f;
return null;
}
@Override
public MCAPaletteAccess createPalette() {
MCAIdMapper<BlockState> registry = registryCache.aquireNasty(() -> {
@@ -598,31 +574,41 @@ public class NMSBinding implements INMSBinding {
}
public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) {
Field[] fields = EntityType.class.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(EntityType.class)) {
try {
EntityType entityType = (EntityType) field.get(null);
if (entityType.getDescriptionId().equals("entity.minecraft." + entity.name().toLowerCase())) {
Vector<Float> v1 = new Vector<>();
v1.add(entityType.getHeight());
entityType.getDimensions();
Vector3d box = new Vector3d( entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
//System.out.println("Entity Type: " + entityType.getDescriptionId() + ", " + "Height: " + height + ", Width: " + width);
return box;
}
} catch (IllegalAccessException e) {
Iris.error("Unable to get entity dimensions!");
e.printStackTrace();
if (entity == null) {
return null;
}
try {
String descriptionId = "entity.minecraft." + entity.name().toLowerCase(Locale.ROOT);
Field[] fields = EntityType.class.getDeclaredFields();
for (Field field : fields) {
if (!Modifier.isStatic(field.getModifiers()) || !field.getType().equals(EntityType.class)) {
continue;
}
EntityType entityType = (EntityType) field.get(null);
if (entityType == null) {
continue;
}
if (descriptionId.equals(entityType.getDescriptionId())) {
return new Vector3d(entityType.getWidth(), entityType.getHeight(), entityType.getWidth());
}
}
return null;
} catch (Throwable e) {
Iris.error("Unable to get entity dimensions for " + entity + "!");
Iris.reportError(e);
return null;
}
return null;
}
@Override
public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) {
if (location == null || location.getWorld() == null || type == null || type.getEntityClass() == null) {
return null;
}
return ((CraftWorld) location.getWorld()).spawn(location, type.getEntityClass(), null, reason);
}
@@ -670,7 +656,49 @@ public class NMSBinding implements INMSBinding {
}
public static Holder<net.minecraft.world.level.biome.Biome> biomeToBiomeBase(Registry<net.minecraft.world.level.biome.Biome> registry, Biome biome) {
return registry.getOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
if (registry == null || biome == null) {
return null;
}
NamespacedKey biomeKey = resolveBiomeKey(biome);
if (biomeKey == null) {
return null;
}
ResourceKey<net.minecraft.world.level.biome.Biome> key = ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biomeKey));
return registry.get(key).orElse(null);
}
private static NamespacedKey resolveBiomeKey(Biome biome) {
Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull", new Class<?>[0]);
if (keyOrNullValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow", new Class<?>[0]);
if (keyOrThrowValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
Object keyValue = invokeNoThrow(biome, "getKey", new Class<?>[0]);
if (keyValue instanceof NamespacedKey namespacedKey) {
return namespacedKey;
}
return null;
}
private static Object invokeNoThrow(Object target, String methodName, Class<?>[] parameterTypes, Object... args) {
if (target == null) {
return null;
}
try {
Method method = target.getClass().getMethod(methodName, parameterTypes);
return method.invoke(target, args);
} catch (Throwable ignored) {
return null;
}
}
@Override

View File

@@ -23,13 +23,41 @@ plugins {
rootProject.name = "Iris"
fun hasVolmLibSettings(directory: File): Boolean {
return directory.resolve("settings.gradle.kts").exists() || directory.resolve("settings.gradle").exists()
}
fun resolveLocalVolmLibDirectory(): File? {
val configuredPath: String? = providers.gradleProperty("localVolmLibDirectory")
.orElse(providers.environmentVariable("VOLMLIB_DIR"))
.orNull
if (!configuredPath.isNullOrBlank()) {
val configuredDirectory: File = file(configuredPath)
if (hasVolmLibSettings(configuredDirectory)) {
return configuredDirectory
}
}
var currentDirectory: File? = settingsDir
while (currentDirectory != null) {
val candidate: File = currentDirectory.resolve("VolmLib")
if (hasVolmLibSettings(candidate)) {
return candidate
}
currentDirectory = currentDirectory.parentFile
}
return null
}
val useLocalVolmLib: Boolean = providers.gradleProperty("useLocalVolmLib")
.orElse("true")
.map { value: String -> value.equals("true", ignoreCase = true) }
.get()
val localVolmLibDirectory: File = file("../VolmLib")
val localVolmLibDirectory: File? = resolveLocalVolmLibDirectory()
if (useLocalVolmLib && localVolmLibDirectory.resolve("settings.gradle.kts").exists()) {
if (useLocalVolmLib && localVolmLibDirectory != null) {
includeBuild(localVolmLibDirectory) {
dependencySubstitution {
substitute(module("com.github.VolmitSoftware:VolmLib")).using(project(":shared"))