fix setup being set before engine setup

This commit is contained in:
CrazyDev22
2024-02-12 11:42:47 +01:00
parent d658ec2099
commit 6a012cf400
@@ -1,413 +1,415 @@
/* /*
* Iris is a World Generator for Minecraft Bukkit Servers * Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software) * Copyright (c) 2022 Arcane Arts (Volmit Software)
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.volmit.iris.engine.platform; package com.volmit.iris.engine.platform;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.INMS;
import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.IrisEngine; import com.volmit.iris.engine.IrisEngine;
import com.volmit.iris.engine.data.chunk.TerrainChunk; import com.volmit.iris.engine.data.chunk.TerrainChunk;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.framework.EngineTarget; import com.volmit.iris.engine.framework.EngineTarget;
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.object.StudioMode; import com.volmit.iris.engine.object.StudioMode;
import com.volmit.iris.engine.platform.studio.StudioGenerator; import com.volmit.iris.engine.platform.studio.StudioGenerator;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.data.IrisBiomeStorage; import com.volmit.iris.util.data.IrisBiomeStorage;
import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder; import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder; import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
import com.volmit.iris.util.io.ReactiveFolder; import com.volmit.iris.util.io.ReactiveFolder;
import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper; import com.volmit.iris.util.scheduling.Looper;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Setter; import lombok.Setter;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldInitEvent; import org.bukkit.event.world.WorldInitEvent;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo; import org.bukkit.generator.WorldInfo;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.File; import java.io.File;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Data @Data
public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator, Listener { public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator, Listener {
private static final int LOAD_LOCKS = Runtime.getRuntime().availableProcessors() * 4; private static final int LOAD_LOCKS = Runtime.getRuntime().availableProcessors() * 4;
private final Semaphore loadLock; private final Semaphore loadLock;
private final IrisWorld world; private final IrisWorld world;
private final File dataLocation; private final File dataLocation;
private final String dimensionKey; private final String dimensionKey;
private final ReactiveFolder folder; private final ReactiveFolder folder;
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
private final KList<BlockPopulator> populators; private final KList<BlockPopulator> populators;
private final ChronoLatch hotloadChecker; private final ChronoLatch hotloadChecker;
private final AtomicBoolean setup; private final AtomicBoolean setup;
private final boolean studio; private final boolean studio;
private final AtomicInteger a = new AtomicInteger(0); private final AtomicInteger a = new AtomicInteger(0);
private Engine engine; private Engine engine;
private Looper hotloader; private Looper hotloader;
private StudioMode lastMode; private StudioMode lastMode;
private DummyBiomeProvider dummyBiomeProvider; private DummyBiomeProvider dummyBiomeProvider;
@Setter @Setter
private StudioGenerator studioGenerator; private StudioGenerator studioGenerator;
private boolean initialized = false; private boolean initialized = false;
public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) { public BukkitChunkGenerator(IrisWorld world, boolean studio, File dataLocation, String dimensionKey) {
setup = new AtomicBoolean(false); setup = new AtomicBoolean(false);
studioGenerator = null; studioGenerator = null;
dummyBiomeProvider = new DummyBiomeProvider(); dummyBiomeProvider = new DummyBiomeProvider();
populators = new KList<>(); populators = new KList<>();
loadLock = new Semaphore(LOAD_LOCKS); loadLock = new Semaphore(LOAD_LOCKS);
this.world = world; this.world = world;
this.hotloadChecker = new ChronoLatch(1000, false); this.hotloadChecker = new ChronoLatch(1000, false);
this.studio = studio; this.studio = studio;
this.dataLocation = dataLocation; this.dataLocation = dataLocation;
this.dimensionKey = dimensionKey; this.dimensionKey = dimensionKey;
this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> hotload()); this.folder = new ReactiveFolder(dataLocation, (_a, _b, _c) -> hotload());
Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance); Bukkit.getServer().getPluginManager().registerEvents(this, Iris.instance);
} }
private static Field getField(Class clazz, String fieldName) private static Field getField(Class clazz, String fieldName)
throws NoSuchFieldException { throws NoSuchFieldException {
try { try {
return clazz.getDeclaredField(fieldName); return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
Class superClass = clazz.getSuperclass(); Class superClass = clazz.getSuperclass();
if (superClass == null) { if (superClass == null) {
throw e; throw e;
} else { } else {
return getField(superClass, fieldName); return getField(superClass, fieldName);
} }
} }
} }
@EventHandler @EventHandler
public void onWorldInit(WorldInitEvent event) { public void onWorldInit(WorldInitEvent event) {
try { try {
if (!initialized) { if (!initialized) {
world.setRawWorldSeed(event.getWorld().getSeed()); world.setRawWorldSeed(event.getWorld().getSeed());
if (world.name().equals(event.getWorld().getName())) { if (world.name().equals(event.getWorld().getName())) {
INMS.get().inject(event.getWorld().getSeed(), getEngine(event.getWorld()), event.getWorld()); INMS.get().inject(event.getWorld().getSeed(), getEngine(event.getWorld()), event.getWorld());
Iris.info("Injected Iris Biome Source into " + event.getWorld().getName()); Iris.info("Injected Iris Biome Source into " + event.getWorld().getName());
initialized = true; initialized = true;
} }
} }
} catch (Throwable e) { } catch (Throwable e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private void setupEngine() { private void setupEngine() {
IrisData data = IrisData.get(dataLocation); IrisData data = IrisData.get(dataLocation);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey); IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) { if (dimension == null) {
Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey); Iris.error("Oh No! There's no pack in " + data.getDataFolder().getPath() + " or... there's no dimension for the key " + dimensionKey);
IrisDimension test = IrisData.loadAnyDimension(dimensionKey); IrisDimension test = IrisData.loadAnyDimension(dimensionKey);
if (test != null) { if (test != null) {
Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " "); Iris.warn("Looks like " + dimensionKey + " exists in " + test.getLoadFile().getPath() + " ");
Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile()); Iris.service(StudioSVC.class).installIntoWorld(Iris.getSender(), dimensionKey, dataLocation.getParentFile().getParentFile());
Iris.warn("Attempted to install into " + data.getDataFolder().getPath()); Iris.warn("Attempted to install into " + data.getDataFolder().getPath());
data.dump(); data.dump();
data.clearLists(); data.clearLists();
test = data.getDimensionLoader().load(dimensionKey); test = data.getDimensionLoader().load(dimensionKey);
if (test != null) { if (test != null) {
Iris.success("Woo! Patched the Engine!"); Iris.success("Woo! Patched the Engine!");
dimension = test; dimension = test;
} else { } else {
Iris.error("Failed to patch dimension!"); Iris.error("Failed to patch dimension!");
throw new RuntimeException("Missing Dimension: " + dimensionKey); throw new RuntimeException("Missing Dimension: " + dimensionKey);
} }
} else { } else {
Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?"); Iris.error("Nope, you don't have an installation containing " + dimensionKey + " try downloading it?");
throw new RuntimeException("Missing Dimension: " + dimensionKey); throw new RuntimeException("Missing Dimension: " + dimensionKey);
} }
} }
lastMode = StudioMode.NORMAL; lastMode = StudioMode.NORMAL;
engine = new IrisEngine(new EngineTarget(world, dimension, data), studio); engine = new IrisEngine(new EngineTarget(world, dimension, data), studio);
populators.clear(); populators.clear();
} }
@Override @Override
public void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs) { public void injectChunkReplacement(World world, int x, int z, Consumer<Runnable> jobs) {
try { try {
loadLock.acquire(); loadLock.acquire();
IrisBiomeStorage st = new IrisBiomeStorage(); IrisBiomeStorage st = new IrisBiomeStorage();
TerrainChunk tc = TerrainChunk.createUnsafe(world, st); TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
Hunk<BlockData> blocks = Hunk.view(tc); Hunk<BlockData> blocks = Hunk.view(tc);
Hunk<Biome> biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight()); Hunk<Biome> biomes = Hunk.view(tc, tc.getMinHeight(), tc.getMaxHeight());
this.world.bind(world); this.world.bind(world);
getEngine().generate(x << 4, z << 4, blocks, biomes, true); getEngine().generate(x << 4, z << 4, blocks, biomes, true);
Iris.debug("Regenerated " + x + " " + z); Iris.debug("Regenerated " + x + " " + z);
int t = 0; int t = 0;
for (int i = getEngine().getHeight() >> 4; i >= 0; i--) { for (int i = getEngine().getHeight() >> 4; i >= 0; i--) {
if (!world.isChunkLoaded(x, z)) { if (!world.isChunkLoaded(x, z)) {
continue; continue;
} }
Chunk c = world.getChunkAt(x, z); Chunk c = world.getChunkAt(x, z);
for (Entity ee : c.getEntities()) { for (Entity ee : c.getEntities()) {
if (ee instanceof Player) { if (ee instanceof Player) {
continue; continue;
} }
J.s(ee::remove); J.s(ee::remove);
} }
J.s(() -> engine.getWorldManager().onChunkLoad(c, false)); J.s(() -> engine.getWorldManager().onChunkLoad(c, false));
int finalI = i; int finalI = i;
jobs.accept(() -> { jobs.accept(() -> {
for (int xx = 0; xx < 16; xx++) { for (int xx = 0; xx < 16; xx++) {
for (int yy = 0; yy < 16; yy++) { for (int yy = 0; yy < 16; yy++) {
for (int zz = 0; zz < 16; zz++) { for (int zz = 0; zz < 16; zz++) {
if (yy + (finalI << 4) >= engine.getHeight() || yy + (finalI << 4) < 0) { if (yy + (finalI << 4) >= engine.getHeight() || yy + (finalI << 4) < 0) {
continue; continue;
} }
c.getBlock(xx, yy + (finalI << 4) + world.getMinHeight(), zz) c.getBlock(xx, yy + (finalI << 4) + world.getMinHeight(), zz)
.setBlockData(tc.getBlockData(xx, yy + (finalI << 4) + world.getMinHeight(), zz), false); .setBlockData(tc.getBlockData(xx, yy + (finalI << 4) + world.getMinHeight(), zz), false);
} }
} }
} }
}); });
} }
loadLock.release(); loadLock.release();
} catch (Throwable e) { } catch (Throwable e) {
loadLock.release(); loadLock.release();
Iris.error("======================================"); Iris.error("======================================");
e.printStackTrace(); e.printStackTrace();
Iris.reportErrorChunk(x, z, e, "CHUNK"); Iris.reportErrorChunk(x, z, e, "CHUNK");
Iris.error("======================================"); Iris.error("======================================");
ChunkData d = Bukkit.createChunkData(world); ChunkData d = Bukkit.createChunkData(world);
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) { for (int j = 0; j < 16; j++) {
d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData()); d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData());
} }
} }
} }
} }
private Engine getEngine(WorldInfo world) { private Engine getEngine(WorldInfo world) {
if (setup.get()) { if (setup.get()) {
return getEngine(); return getEngine();
} }
lock.lock(); lock.lock();
if (setup.get()) { try {
return getEngine(); if (setup.get()) {
} return getEngine();
}
setup.set(true);
getWorld().setRawWorldSeed(world.getSeed()); getWorld().setRawWorldSeed(world.getSeed());
setupEngine(); setupEngine();
this.hotloader = studio ? new Looper() { setup.set(true);
@Override this.hotloader = studio ? new Looper() {
protected long loop() { @Override
if (hotloadChecker.flip()) { protected long loop() {
folder.check(); if (hotloadChecker.flip()) {
} folder.check();
}
return 250;
} return 250;
} : null; }
} : null;
if (studio) {
hotloader.setPriority(Thread.MIN_PRIORITY); if (studio) {
hotloader.start(); hotloader.setPriority(Thread.MIN_PRIORITY);
hotloader.setName(getTarget().getWorld().name() + " Hotloader"); hotloader.start();
} hotloader.setName(getTarget().getWorld().name() + " Hotloader");
}
lock.unlock();
return engine;
return engine; } finally {
} lock.unlock();
}
@Override }
public void close() {
withExclusiveControl(() -> { @Override
if (isStudio()) { public void close() {
hotloader.interrupt(); withExclusiveControl(() -> {
} if (isStudio()) {
hotloader.interrupt();
getEngine().close(); }
folder.clear();
populators.clear(); getEngine().close();
folder.clear();
}); populators.clear();
}
});
@Override }
public boolean isStudio() {
return studio; @Override
} public boolean isStudio() {
return studio;
@Override }
public void hotload() {
if (!isStudio()) { @Override
return; public void hotload() {
} if (!isStudio()) {
return;
withExclusiveControl(() -> getEngine().hotload()); }
}
withExclusiveControl(() -> getEngine().hotload());
public void withExclusiveControl(Runnable r) { }
J.a(() -> {
try { public void withExclusiveControl(Runnable r) {
loadLock.acquire(LOAD_LOCKS); J.a(() -> {
r.run(); try {
loadLock.release(LOAD_LOCKS); loadLock.acquire(LOAD_LOCKS);
} catch (Throwable e) { r.run();
Iris.reportError(e); loadLock.release(LOAD_LOCKS);
} } catch (Throwable e) {
}); Iris.reportError(e);
} }
});
@Override }
public void touch(World world) {
getEngine(world); @Override
} public void touch(World world) {
getEngine(world);
@Override }
public void generateNoise(@NotNull WorldInfo world, @NotNull Random random, int x, int z, @NotNull ChunkGenerator.ChunkData d) {
try { @Override
getEngine(world); public void generateNoise(@NotNull WorldInfo world, @NotNull Random random, int x, int z, @NotNull ChunkGenerator.ChunkData d) {
computeStudioGenerator(); try {
TerrainChunk tc = TerrainChunk.create(d, new IrisBiomeStorage()); getEngine(world);
this.world.bind(world); computeStudioGenerator();
if (studioGenerator != null) { TerrainChunk tc = TerrainChunk.create(d, new IrisBiomeStorage());
studioGenerator.generateChunk(getEngine(), tc, x, z); this.world.bind(world);
} else { if (studioGenerator != null) {
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); studioGenerator.generateChunk(getEngine(), tc, x, z);
BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight()); } else {
getEngine().generate(x << 4, z << 4, blocks, biomes, false); ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc);
blocks.apply(); BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight());
biomes.apply(); getEngine().generate(x << 4, z << 4, blocks, biomes, false);
} blocks.apply();
biomes.apply();
Iris.debug("Generated " + x + " " + z); }
} catch (Throwable e) {
Iris.error("======================================"); Iris.debug("Generated " + x + " " + z);
e.printStackTrace(); } catch (Throwable e) {
Iris.reportErrorChunk(x, z, e, "CHUNK"); Iris.error("======================================");
Iris.error("======================================"); e.printStackTrace();
Iris.reportErrorChunk(x, z, e, "CHUNK");
for (int i = 0; i < 16; i++) { Iris.error("======================================");
for (int j = 0; j < 16; j++) {
d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData()); for (int i = 0; i < 16; i++) {
} for (int j = 0; j < 16; j++) {
} d.setBlock(i, 0, j, Material.RED_GLAZED_TERRACOTTA.createBlockData());
} }
} }
}
@Override }
public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) {
return 4; @Override
} public int getBaseHeight(@NotNull WorldInfo worldInfo, @NotNull Random random, int x, int z, @NotNull HeightMap heightMap) {
return 4;
private void computeStudioGenerator() { }
if (!getEngine().getDimension().getStudioMode().equals(lastMode)) {
lastMode = getEngine().getDimension().getStudioMode(); private void computeStudioGenerator() {
getEngine().getDimension().getStudioMode().inject(this); if (!getEngine().getDimension().getStudioMode().equals(lastMode)) {
} lastMode = getEngine().getDimension().getStudioMode();
} getEngine().getDimension().getStudioMode().inject(this);
}
@NotNull }
@Override
public List<BlockPopulator> getDefaultPopulators(@NotNull World world) { @NotNull
return populators; @Override
} public List<BlockPopulator> getDefaultPopulators(@NotNull World world) {
return populators;
@Override }
public boolean isParallelCapable() {
return true; @Override
} public boolean isParallelCapable() {
return true;
@Override }
public boolean shouldGenerateCaves() {
return false; @Override
} public boolean shouldGenerateCaves() {
return false;
@Override }
public boolean shouldGenerateDecorations() {
return false; @Override
} public boolean shouldGenerateDecorations() {
return false;
@Override }
public boolean shouldGenerateMobs() {
return false; @Override
} public boolean shouldGenerateMobs() {
return false;
@Override }
public boolean shouldGenerateStructures() {
return false; @Override
} public boolean shouldGenerateStructures() {
return false;
@Override }
public boolean shouldGenerateNoise() {
return false; @Override
} public boolean shouldGenerateNoise() {
return false;
@Override }
public boolean shouldGenerateSurface() {
return false; @Override
} public boolean shouldGenerateSurface() {
return false;
@Override }
public boolean shouldGenerateBedrock() {
return false; @Override
} public boolean shouldGenerateBedrock() {
return false;
@Nullable }
@Override
public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) { @Nullable
return dummyBiomeProvider; @Override
} public BiomeProvider getDefaultBiomeProvider(@NotNull WorldInfo worldInfo) {
} return dummyBiomeProvider;
}
}