mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-01 23:47:21 +00:00
fix levelstem injection not working for main worlds
This commit is contained in:
parent
1c5eb8b910
commit
7fa1484b21
@ -35,6 +35,7 @@ import com.volmit.iris.core.tools.IrisToolbelt;
|
|||||||
import com.volmit.iris.core.tools.IrisWorldCreator;
|
import com.volmit.iris.core.tools.IrisWorldCreator;
|
||||||
import com.volmit.iris.engine.EnginePanic;
|
import com.volmit.iris.engine.EnginePanic;
|
||||||
import com.volmit.iris.engine.object.IrisCompat;
|
import com.volmit.iris.engine.object.IrisCompat;
|
||||||
|
import com.volmit.iris.engine.object.IrisContextInjector;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
import com.volmit.iris.engine.object.IrisWorld;
|
import com.volmit.iris.engine.object.IrisWorld;
|
||||||
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
|
import com.volmit.iris.engine.platform.BukkitChunkGenerator;
|
||||||
@ -466,6 +467,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
||||||
services.values().forEach(IrisService::onEnable);
|
services.values().forEach(IrisService::onEnable);
|
||||||
services.values().forEach(this::registerListener);
|
services.values().forEach(this::registerListener);
|
||||||
|
registerListener(new IrisContextInjector());
|
||||||
J.s(() -> {
|
J.s(() -> {
|
||||||
J.a(() -> PaperLib.suggestPaper(this));
|
J.a(() -> PaperLib.suggestPaper(this));
|
||||||
J.a(() -> IO.delete(getTemp()));
|
J.a(() -> IO.delete(getTemp()));
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
|
|
||||||
package com.volmit.iris.core.nms;
|
package com.volmit.iris.core.nms;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@ -91,7 +93,9 @@ public interface INMSBinding {
|
|||||||
MCABiomeContainer newBiomeContainer(int min, int max);
|
MCABiomeContainer newBiomeContainer(int min, int max);
|
||||||
|
|
||||||
default World createWorld(WorldCreator c) {
|
default World createWorld(WorldCreator c) {
|
||||||
return c.createWorld();
|
try (var ignored = injectLevelStems()) {
|
||||||
|
return c.createWorld();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int countCustomBiomes();
|
int countCustomBiomes();
|
||||||
@ -125,4 +129,12 @@ public interface INMSBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KList<String> getStructureKeys();
|
KList<String> getStructureKeys();
|
||||||
|
|
||||||
|
default AutoClosing injectLevelStems() {
|
||||||
|
return new AutoClosing(() -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
return new Pair<>(0, injectLevelStems());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.volmit.iris.core.nms.container;
|
||||||
|
|
||||||
|
import com.volmit.iris.util.function.NastyRunnable;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AutoClosing implements AutoCloseable {
|
||||||
|
private final AtomicBoolean closed = new AtomicBoolean();
|
||||||
|
private final NastyRunnable action;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (closed.getAndSet(true)) return;
|
||||||
|
try {
|
||||||
|
action.run();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
|
import com.volmit.iris.core.nms.INMS;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
|
import com.volmit.iris.util.misc.ServerProperties;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.world.WorldInitEvent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.volmit.iris.Iris.instance;
|
||||||
|
|
||||||
|
public class IrisContextInjector implements Listener {
|
||||||
|
private AutoClosing autoClosing = null;
|
||||||
|
private final int totalWorlds;
|
||||||
|
private int worldCounter = 0;
|
||||||
|
|
||||||
|
public IrisContextInjector() {
|
||||||
|
if (!Bukkit.getWorlds().isEmpty()) {
|
||||||
|
totalWorlds = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String levelName = ServerProperties.LEVEL_NAME;
|
||||||
|
List<String> irisWorlds = irisWorlds();
|
||||||
|
boolean overworld = irisWorlds.contains(levelName);
|
||||||
|
boolean nether = irisWorlds.contains(levelName + "_nether");
|
||||||
|
boolean end = irisWorlds.contains(levelName + "_end");
|
||||||
|
|
||||||
|
int i = 1;
|
||||||
|
if (Bukkit.getAllowNether()) i++;
|
||||||
|
if (Bukkit.getAllowEnd()) i++;
|
||||||
|
|
||||||
|
if (overworld || nether || end) {
|
||||||
|
var pair = INMS.get().injectUncached(overworld, nether, end);
|
||||||
|
i += pair.getA() - 3;
|
||||||
|
autoClosing = pair.getB();
|
||||||
|
}
|
||||||
|
|
||||||
|
totalWorlds = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void on(WorldInitEvent event) {
|
||||||
|
if (++worldCounter < totalWorlds) return;
|
||||||
|
if (autoClosing != null) {
|
||||||
|
autoClosing.close();
|
||||||
|
autoClosing = null;
|
||||||
|
}
|
||||||
|
instance.unregisterListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> irisWorlds() {
|
||||||
|
var config = YamlConfiguration.loadConfiguration(ServerProperties.BUKKIT_YML);
|
||||||
|
ConfigurationSection section = config.getConfigurationSection("worlds");
|
||||||
|
if (section == null) return List.of();
|
||||||
|
|
||||||
|
return section.getKeys(false)
|
||||||
|
.stream()
|
||||||
|
.filter(k -> section.getString(k + ".generator", "").startsWith("Iris"))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.volmit.iris.util.misc;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class ServerProperties {
|
||||||
|
public static final Properties DATA = new Properties();
|
||||||
|
public static final File SERVER_PROPERTIES;
|
||||||
|
public static final File BUKKIT_YML;
|
||||||
|
|
||||||
|
public static final String LEVEL_NAME = DATA.getProperty("level-name", "world");
|
||||||
|
|
||||||
|
static {
|
||||||
|
String[] args = ProcessHandle.current()
|
||||||
|
.info()
|
||||||
|
.arguments()
|
||||||
|
.orElse(new String[0]);
|
||||||
|
|
||||||
|
String propertiesPath = "server.properties";
|
||||||
|
String bukkitYml = "bukkit.yml";
|
||||||
|
|
||||||
|
for (int i = 0; i < args.length - 1; i++) {
|
||||||
|
switch (args[i]) {
|
||||||
|
case "-c", "--config" -> propertiesPath = args[i + 1];
|
||||||
|
case "-b", "--bukkit-settings" -> bukkitYml = args[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVER_PROPERTIES = new File(propertiesPath);
|
||||||
|
BUKKIT_YML = new File(bukkitYml);
|
||||||
|
try (FileInputStream in = new FileInputStream(SERVER_PROPERTIES)){
|
||||||
|
DATA.load(in);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import com.mojang.datafixers.util.Pair;
|
|||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
@ -78,11 +79,10 @@ import java.io.DataOutputStream;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Iterator;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
|
|
||||||
@ -94,6 +94,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -574,8 +575,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
public void inject(long seed, Engine engine, World world) {
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,38 +639,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -679,4 +722,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getOrThrow(key).generator()
|
levelStems.getOrThrow(key).generator()
|
||||||
), Lifecycle.stable());
|
), Lifecycle.stable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.registryKeySet().forEach(key -> {
|
||||||
|
var value = source.get(key);
|
||||||
|
var info = source.lifecycle(value);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@ import java.io.DataOutputStream;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Iterator;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@ -91,6 +91,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -543,8 +544,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
public void inject(long seed, Engine engine, World world) {
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -638,38 +640,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -680,4 +723,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getOrThrow(key).generator()
|
levelStems.getOrThrow(key).generator()
|
||||||
), Lifecycle.stable());
|
), Lifecycle.stable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.registryKeySet().forEach(key -> {
|
||||||
|
var value = source.get(key);
|
||||||
|
var info = source.lifecycle(value);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,14 @@ import java.io.DataOutputStream;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Iterator;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Vector;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@ -91,6 +91,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -543,8 +544,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
public void inject(long seed, Engine engine, World world) {
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -639,38 +641,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
return registry.getHolderOrThrow(ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biome.getKey())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -681,4 +724,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getOrThrow(key).generator()
|
levelStems.getOrThrow(key).generator()
|
||||||
), Lifecycle.stable());
|
), Lifecycle.stable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.registryKeySet().forEach(key -> {
|
||||||
|
var value = source.get(key);
|
||||||
|
var info = source.lifecycle(value);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,12 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
||||||
@ -96,6 +99,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -551,8 +555,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
|
|
||||||
public void inject(long seed, Engine engine, World world) {
|
public void inject(long seed, Engine engine, World world) {
|
||||||
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
var chunkMap = ((CraftWorld)world).getHandle().getChunkSource().chunkMap;
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
|
|
||||||
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
chunkMap.generator = new IrisChunkGenerator(chunkMap.generator, seed, engine, world);
|
||||||
}
|
}
|
||||||
@ -661,38 +666,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -703,4 +749,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getOrThrow(key).generator()
|
levelStems.getOrThrow(key).generator()
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.registryKeySet().forEach(key -> {
|
||||||
|
var value = source.get(key);
|
||||||
|
var info = source.registrationInfo(key).orElse(null);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,12 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.mojang.datafixers.util.Pair;
|
import com.mojang.datafixers.util.Pair;
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
@ -93,6 +96,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -551,8 +555,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||||
worldGenContextField.setAccessible(true);
|
worldGenContextField.setAccessible(true);
|
||||||
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
|
|
||||||
var newContext = new WorldGenContext(
|
var newContext = new WorldGenContext(
|
||||||
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||||
@ -665,38 +670,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public com.volmit.iris.core.nms.container.Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new com.volmit.iris.core.nms.container.Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.registryOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.registryOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.registry(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -707,4 +753,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getOrThrow(key).generator()
|
levelStems.getOrThrow(key).generator()
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.registryKeySet().forEach(key -> {
|
||||||
|
var value = source.get(key);
|
||||||
|
var info = source.registrationInfo(key).orElse(null);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,13 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
@ -82,6 +86,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -540,8 +545,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||||
worldGenContextField.setAccessible(true);
|
worldGenContextField.setAccessible(true);
|
||||||
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
|
|
||||||
var newContext = new WorldGenContext(
|
var newContext = new WorldGenContext(
|
||||||
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||||
@ -654,38 +660,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.lookupOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -696,4 +743,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getValueOrThrow(key).generator()
|
levelStems.getValueOrThrow(key).generator()
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.listElementIds().forEach(key -> {
|
||||||
|
var value = source.getValue(key);
|
||||||
|
var info = source.registrationInfo(key).orElse(null);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|||||||
import com.mojang.serialization.Lifecycle;
|
import com.mojang.serialization.Lifecycle;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.nms.INMSBinding;
|
import com.volmit.iris.core.nms.INMSBinding;
|
||||||
|
import com.volmit.iris.core.nms.container.AutoClosing;
|
||||||
import com.volmit.iris.core.nms.container.BiomeColor;
|
import com.volmit.iris.core.nms.container.BiomeColor;
|
||||||
|
import com.volmit.iris.core.nms.container.Pair;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.engine.data.cache.AtomicCache;
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
@ -72,6 +74,8 @@ import java.lang.reflect.Modifier;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class NMSBinding implements INMSBinding {
|
public class NMSBinding implements INMSBinding {
|
||||||
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
private final KMap<Biome, Object> baseBiomeCache = new KMap<>();
|
||||||
@ -81,6 +85,7 @@ public class NMSBinding implements INMSBinding {
|
|||||||
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
private final AtomicCache<MCAIdMapper<BlockState>> registryCache = new AtomicCache<>();
|
||||||
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
private final AtomicCache<MCAPalette<BlockState>> globalCache = new AtomicCache<>();
|
||||||
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
private final AtomicCache<RegistryAccess> registryAccess = new AtomicCache<>();
|
||||||
|
private final ReentrantLock dataContextLock = new ReentrantLock(true);
|
||||||
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
private final AtomicCache<Method> byIdRef = new AtomicCache<>();
|
||||||
private Field biomeStorageCache = null;
|
private Field biomeStorageCache = null;
|
||||||
|
|
||||||
@ -539,8 +544,9 @@ public class NMSBinding implements INMSBinding {
|
|||||||
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
var worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class);
|
||||||
worldGenContextField.setAccessible(true);
|
worldGenContextField.setAccessible(true);
|
||||||
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
var worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
|
||||||
if (!chunkMap.level.dimension().location().getPath().startsWith("iris"))
|
var dimensionType = chunkMap.level.dimensionTypeRegistration().unwrapKey().orElse(null);
|
||||||
Iris.error("Loaded world %s with invalid dimension type!", world.getName());
|
if (dimensionType != null && !dimensionType.location().getNamespace().equals("iris"))
|
||||||
|
Iris.error("Loaded world %s with invalid dimension type! (%s)", world.getName(), dimensionType.location().toString());
|
||||||
|
|
||||||
var newContext = new WorldGenContext(
|
var newContext = new WorldGenContext(
|
||||||
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
worldGenContext.level(), new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world),
|
||||||
@ -653,38 +659,79 @@ public class NMSBinding implements INMSBinding {
|
|||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AutoClosing injectLevelStems() {
|
||||||
|
return inject(this::supplier);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public World createWorld(WorldCreator creator) {
|
public Pair<Integer, AutoClosing> injectUncached(boolean overworld, boolean nether, boolean end) {
|
||||||
|
var reg = registry();
|
||||||
|
var field = getField(RegistryAccess.ImmutableRegistryAccess.class, Map.class);
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
AutoClosing closing = inject(old -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), true, overworld, nether, end)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
var injected = ((CraftServer) Bukkit.getServer()).getServer().worldLoader.datapackDimensions().lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
var old = (Map<ResourceKey<? extends Registry<?>>, Registry<?>>) field.get(reg);
|
||||||
|
var fake = new HashMap<>(old);
|
||||||
|
fake.put(Registries.LEVEL_STEM, injected);
|
||||||
|
field.set(reg, fake);
|
||||||
|
|
||||||
|
return new Pair<>(
|
||||||
|
injected.size(),
|
||||||
|
new AutoClosing(() -> {
|
||||||
|
closing.close();
|
||||||
|
field.set(reg, old);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WorldLoader.DataLoadContext supplier(WorldLoader.DataLoadContext old) {
|
||||||
|
return dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
||||||
|
old.resources(),
|
||||||
|
old.dataConfiguration(),
|
||||||
|
old.datapackWorldgen(),
|
||||||
|
createRegistryAccess(old.datapackDimensions(), false, true, true, true)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private AutoClosing inject(Function<WorldLoader.DataLoadContext, WorldLoader.DataLoadContext> transformer) {
|
||||||
|
if (!dataContextLock.tryLock()) throw new IllegalStateException("Failed to inject data context!");
|
||||||
|
|
||||||
var server = ((CraftServer) Bukkit.getServer());
|
var server = ((CraftServer) Bukkit.getServer());
|
||||||
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
var field = getField(MinecraftServer.class, WorldLoader.DataLoadContext.class);
|
||||||
var nmsServer = server.getServer();
|
var nmsServer = server.getServer();
|
||||||
var old = nmsServer.worldLoader;
|
var old = nmsServer.worldLoader;
|
||||||
|
|
||||||
field.setAccessible(true);
|
field.setAccessible(true);
|
||||||
field.set(nmsServer, dataLoadContext.aquire(() -> new WorldLoader.DataLoadContext(
|
field.set(nmsServer, transformer.apply(old));
|
||||||
old.resources(),
|
|
||||||
old.dataConfiguration(),
|
|
||||||
old.datapackWorldgen(),
|
|
||||||
createRegistryAccess()
|
|
||||||
)));
|
|
||||||
|
|
||||||
try {
|
return new AutoClosing(() -> {
|
||||||
return server.createWorld(creator);
|
|
||||||
} finally {
|
|
||||||
field.set(nmsServer, old);
|
field.set(nmsServer, old);
|
||||||
}
|
dataContextLock.unlock();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private RegistryAccess.Frozen createRegistryAccess() {
|
private RegistryAccess.Frozen createRegistryAccess(RegistryAccess.Frozen datapack, boolean copy, boolean overworld, boolean nether, boolean end) {
|
||||||
var access = registry();
|
var access = registry();
|
||||||
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
var dimensions = access.lookupOrThrow(Registries.DIMENSION_TYPE);
|
||||||
var levelStems = access.lookupOrThrow(Registries.LEVEL_STEM);
|
var levelStems = access.lookupOrThrow(Registries.LEVEL_STEM);
|
||||||
|
|
||||||
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
var fake = new MappedRegistry<>(Registries.LEVEL_STEM, Lifecycle.experimental());
|
||||||
register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
if (overworld) register(fake, levelStems, dimensions, LevelStem.OVERWORLD);
|
||||||
register(fake, levelStems, dimensions, LevelStem.NETHER);
|
if (nether) register(fake, levelStems, dimensions, LevelStem.NETHER);
|
||||||
register(fake, levelStems, dimensions, LevelStem.END);
|
if (end) register(fake, levelStems, dimensions, LevelStem.END);
|
||||||
|
copy(fake, datapack.lookup(Registries.LEVEL_STEM).orElse(null));
|
||||||
|
|
||||||
|
if (copy) copy(fake, levelStems);
|
||||||
|
|
||||||
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
return new RegistryAccess.Frozen.ImmutableRegistryAccess(List.of(fake.freeze())).freeze();
|
||||||
}
|
}
|
||||||
@ -695,4 +742,14 @@ public class NMSBinding implements INMSBinding {
|
|||||||
levelStems.getValueOrThrow(key).generator()
|
levelStems.getValueOrThrow(key).generator()
|
||||||
), RegistrationInfo.BUILT_IN);
|
), RegistrationInfo.BUILT_IN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copy(MappedRegistry<LevelStem> target, Registry<LevelStem> source) {
|
||||||
|
if (source == null) return;
|
||||||
|
source.listElementIds().forEach(key -> {
|
||||||
|
var value = source.getValue(key);
|
||||||
|
var info = source.registrationInfo(key).orElse(null);
|
||||||
|
if (value != null && info != null && !target.containsKey(key))
|
||||||
|
target.register(key, value, info);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user