mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-07-18 18:23:06 +00:00
Can you smell the rot?
This commit is contained in:
parent
2ffd1e6e47
commit
a8a87e7f79
@ -1,91 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.core.tools;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.engine.framework.IrisAccess;
|
|
||||||
import com.volmit.iris.engine.framework.IrisAccessProvider;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
@SuppressWarnings("ALL")
|
|
||||||
public class IrisWorlds {
|
|
||||||
private static final KMap<String, IrisAccess> provisioned = new KMap<>();
|
|
||||||
|
|
||||||
public static void register(World w, IrisAccess p) {
|
|
||||||
provisioned.put(w.getUID().toString(), p);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isIrisWorld(World world) {
|
|
||||||
if (world == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (provisioned.containsKey(world.getUID().toString())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return world.getGenerator() instanceof IrisAccess || world.getGenerator() instanceof IrisAccessProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IrisAccess access(World world) {
|
|
||||||
if (isIrisWorld(world)) {
|
|
||||||
if (provisioned.containsKey(world.getUID().toString())) {
|
|
||||||
return provisioned.get(world.getUID().toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return world.getGenerator() instanceof IrisAccessProvider ? (((IrisAccessProvider) world.getGenerator()).getAccess()) : ((IrisAccess) world.getGenerator());
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean evacuate(World world) {
|
|
||||||
for (World i : Bukkit.getWorlds()) {
|
|
||||||
if (!i.getName().equals(world.getName())) {
|
|
||||||
for (Player j : world.getPlayers()) {
|
|
||||||
new VolmitSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world.");
|
|
||||||
j.teleport(i.getSpawnLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean evacuate(World world, String m) {
|
|
||||||
for (World i : Bukkit.getWorlds()) {
|
|
||||||
if (!i.getName().equals(world.getName())) {
|
|
||||||
for (Player j : world.getPlayers()) {
|
|
||||||
new VolmitSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world. " + m);
|
|
||||||
j.teleport(i.getSpawnLocation());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,288 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.engine;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.IrisSettings;
|
|
||||||
import com.volmit.iris.core.project.loader.IrisData;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.framework.EngineCompound;
|
|
||||||
import com.volmit.iris.engine.framework.EngineData;
|
|
||||||
import com.volmit.iris.engine.framework.EngineTarget;
|
|
||||||
import com.volmit.iris.engine.object.basic.IrisPosition;
|
|
||||||
import com.volmit.iris.engine.object.common.IrisWorld;
|
|
||||||
import com.volmit.iris.engine.object.dimensional.IrisDimension;
|
|
||||||
import com.volmit.iris.engine.object.dimensional.IrisDimensionIndex;
|
|
||||||
import com.volmit.iris.util.atomics.AtomicRollingSequence;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.documentation.BlockCoordinates;
|
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.world.WorldSaveEvent;
|
|
||||||
import org.bukkit.generator.BlockPopulator;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class IrisEngineCompound implements EngineCompound {
|
|
||||||
@Getter
|
|
||||||
private final IrisWorld world;
|
|
||||||
|
|
||||||
private final AtomicRollingSequence wallClock;
|
|
||||||
|
|
||||||
private Engine defaultEngine;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final EngineData engineMetadata;
|
|
||||||
|
|
||||||
private final Engine[] engines;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final MultiBurst burster;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final KList<BlockPopulator> populators;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final IrisDimension rootDimension;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final int threadCount = -1;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private boolean studio;
|
|
||||||
|
|
||||||
public IrisEngineCompound(IrisWorld world, IrisDimension rootDimension, IrisData data, int maximumThreads) {
|
|
||||||
wallClock = new AtomicRollingSequence(32);
|
|
||||||
this.rootDimension = rootDimension;
|
|
||||||
Iris.info("Initializing Engine Composite for " + world.name());
|
|
||||||
this.world = world;
|
|
||||||
engineMetadata = EngineData.load(getEngineMetadataFile());
|
|
||||||
engineMetadata.setDimension(rootDimension.getLoadKey());
|
|
||||||
engineMetadata.setLastVersion(Iris.instance.getDescription().getVersion());
|
|
||||||
|
|
||||||
saveEngineMetadata();
|
|
||||||
populators = new KList<>();
|
|
||||||
|
|
||||||
if (rootDimension.getDimensionalComposite().isEmpty()) {
|
|
||||||
burster = null;
|
|
||||||
// TODO: WARNING HEIGHT
|
|
||||||
engines = new Engine[]{new IrisEngine(new EngineTarget(world, rootDimension, data, 256, maximumThreads), this, 0)};
|
|
||||||
defaultEngine = engines[0];
|
|
||||||
} else {
|
|
||||||
double totalWeight = 0D;
|
|
||||||
engines = new Engine[rootDimension.getDimensionalComposite().size()];
|
|
||||||
burster = engines.length > 1 ? new MultiBurst("Iris Compound " + rootDimension.getName(), IrisSettings.get().getConcurrency().getEngineThreadPriority(), engines.length) : null;
|
|
||||||
int threadDist = (Math.max(2, maximumThreads - engines.length)) / engines.length;
|
|
||||||
|
|
||||||
if ((threadDist * engines.length) + engines.length > maximumThreads) {
|
|
||||||
Iris.warn("Using " + ((threadDist * engines.length) + engines.length) + " threads instead of the configured " + maximumThreads + " maximum thread count due to the requirements of this dimension!");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (IrisDimensionIndex i : rootDimension.getDimensionalComposite()) {
|
|
||||||
totalWeight += i.getWeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
int buf = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < engines.length; i++) {
|
|
||||||
IrisDimensionIndex index = rootDimension.getDimensionalComposite().get(i);
|
|
||||||
IrisDimension dimension = data.getDimensionLoader().load(index.getDimension());
|
|
||||||
// TODO: WARNING HEIGHT
|
|
||||||
engines[i] = new IrisEngine(new EngineTarget(world, dimension, data.copy(), (int) Math.floor(256D * (index.getWeight() / totalWeight)), index.isInverted(), threadDist), this, i);
|
|
||||||
engines[i].setMinHeight(buf);
|
|
||||||
buf += engines[i].getHeight();
|
|
||||||
|
|
||||||
if (index.isPrimary()) {
|
|
||||||
defaultEngine = engines[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultEngine == null) {
|
|
||||||
defaultEngine = engines[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Engine i : engines) {
|
|
||||||
if (i instanceof BlockPopulator) {
|
|
||||||
populators.add((BlockPopulator) i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.instance.registerListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<IrisPosition> getStrongholdPositions() {
|
|
||||||
return engineMetadata.getStrongholdPositions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void on(WorldSaveEvent e) {
|
|
||||||
if (world != null && e.getWorld().equals(world)) {
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void printMetrics(CommandSender sender) {
|
|
||||||
KMap<String, Double> totals = new KMap<>();
|
|
||||||
KMap<String, Double> weights = new KMap<>();
|
|
||||||
double masterWallClock = wallClock.getAverage();
|
|
||||||
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
Engine e = getEngine(i);
|
|
||||||
KMap<String, Double> timings = e.getMetrics().pull();
|
|
||||||
double totalWeight = 0;
|
|
||||||
double wallClock = e.getMetrics().getTotal().getAverage();
|
|
||||||
|
|
||||||
for (double j : timings.values()) {
|
|
||||||
totalWeight += j;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String j : timings.k()) {
|
|
||||||
weights.put(e.getName() + "[" + e.getIndex() + "]." + j, (wallClock / totalWeight) * timings.get(j));
|
|
||||||
}
|
|
||||||
|
|
||||||
totals.put(e.getName() + "[" + e.getIndex() + "]", wallClock);
|
|
||||||
}
|
|
||||||
|
|
||||||
double mtotals = 0;
|
|
||||||
|
|
||||||
for (double i : totals.values()) {
|
|
||||||
mtotals += i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String i : totals.k()) {
|
|
||||||
totals.put(i, (masterWallClock / mtotals) * totals.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
double v = 0;
|
|
||||||
|
|
||||||
for (double i : weights.values()) {
|
|
||||||
v += i;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String i : weights.k()) {
|
|
||||||
weights.put(i, weights.get(i) / v);
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.sendMessage("Total: " + C.BOLD + C.WHITE + Form.duration(masterWallClock, 0));
|
|
||||||
|
|
||||||
for (String i : totals.k()) {
|
|
||||||
sender.sendMessage(" Engine " + C.UNDERLINE + C.GREEN + i + C.RESET + ": " + C.BOLD + C.WHITE + Form.duration(totals.get(i), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
sender.sendMessage("Details: ");
|
|
||||||
|
|
||||||
for (String i : weights.sortKNumber().reverse()) {
|
|
||||||
String befb = C.UNDERLINE + "" + C.GREEN + "" + i.split("\\Q[\\E")[0] + C.RESET + C.GRAY + "[";
|
|
||||||
String num = C.GOLD + i.split("\\Q[\\E")[1].split("]")[0] + C.RESET + C.GRAY + "].";
|
|
||||||
String afb = C.ITALIC + "" + C.AQUA + i.split("\\Q]\\E")[1].substring(1) + C.RESET + C.GRAY;
|
|
||||||
|
|
||||||
sender.sendMessage(" " + befb + num + afb + ": " + C.BOLD + C.WHITE + Form.pc(weights.get(i), 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getEngineMetadataFile() {
|
|
||||||
return new File(world.worldFolder(), "iris/engine-metadata.json");
|
|
||||||
}
|
|
||||||
|
|
||||||
@ChunkCoordinates
|
|
||||||
@Override
|
|
||||||
public void generate(int x, int z, Hunk<BlockData> blocks, Hunk<BlockData> postblocks, Hunk<Biome> biomes, boolean multicore) {
|
|
||||||
recycle();
|
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
|
||||||
if (engines.length == 1 && !getEngine(0).getTarget().isInverted()) {
|
|
||||||
engines[0].generate(x, z, blocks, biomes, multicore);
|
|
||||||
} else {
|
|
||||||
int i;
|
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < engines.length; i++) {
|
|
||||||
Engine engine = engines[i];
|
|
||||||
int doffset = offset;
|
|
||||||
int height = engine.getTarget().getHeight();
|
|
||||||
Hunk<BlockData> cblock = Hunk.newArrayHunk(16, height, 16);
|
|
||||||
Hunk<Biome> cbiome = Hunk.newArrayHunk(16, height, 16);
|
|
||||||
|
|
||||||
if (engine.getTarget().isInverted()) {
|
|
||||||
cblock = cblock.invertY();
|
|
||||||
cbiome = cbiome.invertY();
|
|
||||||
}
|
|
||||||
|
|
||||||
engine.generate(x, z, cblock, cbiome, multicore);
|
|
||||||
blocks.insert(0, doffset, 0, cblock);
|
|
||||||
biomes.insert(0, doffset, 0, cbiome);
|
|
||||||
offset += height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wallClock.put(p.getMilliseconds());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return engines.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Engine getEngine(int index) {
|
|
||||||
return engines[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveEngineMetadata() {
|
|
||||||
engineMetadata.save(getEngineMetadataFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
@BlockCoordinates
|
|
||||||
@Override
|
|
||||||
public IrisData getData(int height) {
|
|
||||||
return getEngineForHeight(height).getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: FAIL
|
|
||||||
@Override
|
|
||||||
public boolean isFailing() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Engine getDefaultEngine() {
|
|
||||||
return defaultEngine;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hotload() {
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
getEngine(i).hotload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,810 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.engine.framework;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.IrisSettings;
|
|
||||||
import com.volmit.iris.core.nms.INMS;
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenTask;
|
|
||||||
import com.volmit.iris.core.project.loader.IrisData;
|
|
||||||
import com.volmit.iris.engine.IrisEngineCompound;
|
|
||||||
import com.volmit.iris.engine.data.chunk.MCATerrainChunk;
|
|
||||||
import com.volmit.iris.engine.data.chunk.TerrainChunk;
|
|
||||||
import com.volmit.iris.engine.framework.headless.HeadlessGenerator;
|
|
||||||
import com.volmit.iris.engine.object.basic.IrisPosition;
|
|
||||||
import com.volmit.iris.engine.object.biome.IrisBiome;
|
|
||||||
import com.volmit.iris.engine.object.common.IrisWorld;
|
|
||||||
import com.volmit.iris.engine.object.dimensional.IrisDimension;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.data.B;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.io.ReactiveFolder;
|
|
||||||
import com.volmit.iris.util.math.M;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.nbt.tag.CompoundTag;
|
|
||||||
import com.volmit.iris.util.parallel.BurstExecutor;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
|
||||||
import com.volmit.iris.util.reflect.V;
|
|
||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
|
||||||
import com.volmit.iris.util.scheduling.J;
|
|
||||||
import com.volmit.iris.util.scheduling.Looper;
|
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
|
||||||
import io.netty.util.internal.ConcurrentSet;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.bukkit.*;
|
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.generator.BlockPopulator;
|
|
||||||
import org.bukkit.generator.ChunkGenerator;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public class EngineCompositeGenerator extends ChunkGenerator implements IrisAccess {
|
|
||||||
private static final BlockData ERROR_BLOCK = Material.RED_GLAZED_TERRACOTTA.createBlockData();
|
|
||||||
private final AtomicReference<EngineCompound> compound = new AtomicReference<>();
|
|
||||||
private final AtomicBoolean initialized;
|
|
||||||
private final String dimensionQuery;
|
|
||||||
private final boolean production;
|
|
||||||
private final KList<BlockPopulator> populators;
|
|
||||||
private long mst = 0;
|
|
||||||
private HeadlessGenerator headlessGenerator;
|
|
||||||
private NBTWorld nbtWorld;
|
|
||||||
private int generated = 0;
|
|
||||||
private int lgenerated = 0;
|
|
||||||
private final ChronoLatch hotloadcd;
|
|
||||||
@Getter
|
|
||||||
private double generatedPerSecond = 0;
|
|
||||||
private ReactiveFolder hotloader = null;
|
|
||||||
private IrisWorld cworld = null;
|
|
||||||
private final Looper ticker;
|
|
||||||
private final Looper cleaner;
|
|
||||||
private int hotloaderMisses = 0;
|
|
||||||
private long lastHotloadTime = 100;
|
|
||||||
|
|
||||||
public EngineCompositeGenerator() {
|
|
||||||
this(null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EngineCompositeGenerator(String query, boolean production) {
|
|
||||||
super();
|
|
||||||
ticker = new Looper() {
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
|
||||||
if (!tickHotloader()) {
|
|
||||||
hotloaderMisses++;
|
|
||||||
} else {
|
|
||||||
hotloaderMisses = 0;
|
|
||||||
}
|
|
||||||
lastHotloadTime += p.getMilliseconds();
|
|
||||||
lastHotloadTime /= 2;
|
|
||||||
|
|
||||||
return 120 + (lastHotloadTime / 2) + Math.min(hotloaderMisses * 125, 1375);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
ticker.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
ticker.setName("Iris Project Manager");
|
|
||||||
|
|
||||||
cleaner = new Looper() {
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
if (getComposite() != null) {
|
|
||||||
getComposite().clean();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 10000;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cleaner.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
cleaner.setName("Iris Parallax Manager");
|
|
||||||
cleaner.start();
|
|
||||||
|
|
||||||
if (isStudio()) {
|
|
||||||
ticker.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
hotloadcd = new ChronoLatch(3500);
|
|
||||||
mst = M.ms();
|
|
||||||
this.production = production;
|
|
||||||
this.dimensionQuery = query;
|
|
||||||
initialized = new AtomicBoolean(false);
|
|
||||||
populators = new KList<BlockPopulator>().qadd(new BlockPopulator() {
|
|
||||||
@Override
|
|
||||||
public void populate(World world, Random random, Chunk chunk) {
|
|
||||||
if (compound.get() != null) {
|
|
||||||
for (BlockPopulator i : compound.get().getPopulators()) {
|
|
||||||
i.populate(world, random, chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hotload() {
|
|
||||||
if (isStudio()) {
|
|
||||||
Iris.proj.updateWorkspace();
|
|
||||||
getData().dump();
|
|
||||||
J.s(() -> {
|
|
||||||
try {
|
|
||||||
for (Player i : getTarget().getWorld().getPlayers()) {
|
|
||||||
new VolmitSender(i, Iris.instance.getTag()).sendMessage("Dimension Hotloaded");
|
|
||||||
i.playSound(i.getLocation(), Sound.BLOCK_COPPER_PLACE, 1f, 1.25f);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getComposite().close();
|
|
||||||
initialized.lazySet(false);
|
|
||||||
|
|
||||||
if (cworld != null) {
|
|
||||||
initialize(cworld);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean tickHotloader() {
|
|
||||||
if (getComposite() == null || isClosed()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!initialized.get()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (hotloader != null) {
|
|
||||||
return hotloader.check();
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized IrisDimension getDimension(IrisWorld world) {
|
|
||||||
String query = dimensionQuery;
|
|
||||||
query = Iris.linkMultiverseCore.getWorldNameType(world.name(), query);
|
|
||||||
|
|
||||||
IrisDimension dim = null;
|
|
||||||
|
|
||||||
if (query == null) {
|
|
||||||
File iris = new File(world.worldFolder(), "iris");
|
|
||||||
|
|
||||||
if (iris.exists() && iris.isDirectory()) {
|
|
||||||
for (File i : iris.listFiles()) {
|
|
||||||
// Look for v1 location
|
|
||||||
if (i.isDirectory() && i.getName().equals("dimensions")) {
|
|
||||||
for (File j : i.listFiles()) {
|
|
||||||
if (j.isFile() && j.getName().endsWith(".json")) {
|
|
||||||
query = j.getName().replaceAll("\\Q.json\\E", "");
|
|
||||||
Iris.error("Found v1 install. Please create a new world, this will cause chunks to change in your existing iris worlds!");
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for v2 location
|
|
||||||
else if (i.isFile() && i.getName().equals("engine-metadata.json")) {
|
|
||||||
EngineData metadata = EngineData.load(i);
|
|
||||||
query = metadata.getDimension();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query == null) {
|
|
||||||
Iris.error("Cannot find iris dimension data for world: " + world.name() + "! Assuming " + IrisSettings.get().getGenerator().getDefaultWorldType() + "!");
|
|
||||||
query = IrisSettings.get().getGenerator().getDefaultWorldType();
|
|
||||||
}
|
|
||||||
|
|
||||||
dim = IrisData.loadAnyDimension(query);
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
Iris.proj.downloadSearch(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), query, false);
|
|
||||||
dim = IrisData.loadAnyDimension(query);
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
throw new RuntimeException("Cannot find dimension: " + query);
|
|
||||||
} else {
|
|
||||||
Iris.info("Download pack: " + query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (production) {
|
|
||||||
IrisDimension od = dim;
|
|
||||||
dim = new IrisData(getDataFolder(world)).getDimensionLoader().load(od.getLoadKey());
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
Iris.info("Installing Iris pack " + od.getName() + " into world " + world.name() + "...");
|
|
||||||
Iris.proj.installIntoWorld(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), od.getLoadKey(), world.worldFolder());
|
|
||||||
dim = new IrisData(getDataFolder(world)).getDimensionLoader().load(od.getLoadKey());
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
throw new RuntimeException("Cannot find dimension: " + query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized IrisDimension getDimension(String world) {
|
|
||||||
String query = dimensionQuery;
|
|
||||||
IrisDimension dim = null;
|
|
||||||
|
|
||||||
if (query == null) {
|
|
||||||
File iris = new File(world + "/iris");
|
|
||||||
|
|
||||||
if (iris.exists() && iris.isDirectory()) {
|
|
||||||
for (File i : Objects.requireNonNull(iris.listFiles())) {
|
|
||||||
// Look for v1 location
|
|
||||||
if (i.isDirectory() && i.getName().equals("dimensions")) {
|
|
||||||
for (File j : Objects.requireNonNull(i.listFiles())) {
|
|
||||||
if (j.isFile() && j.getName().endsWith(".json")) {
|
|
||||||
query = j.getName().replaceAll("\\Q.json\\E", "");
|
|
||||||
Iris.error("Found v1 install. Please create a new world, this will cause chunks to change in your existing iris worlds!");
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for v2 location
|
|
||||||
else if (i.isFile() && i.getName().equals("engine-metadata.json")) {
|
|
||||||
EngineData metadata = EngineData.load(i);
|
|
||||||
query = metadata.getDimension();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query == null) {
|
|
||||||
Iris.error("Cannot find iris dimension data for world: " + world + "! Assuming " + IrisSettings.get().getGenerator().getDefaultWorldType() + "!");
|
|
||||||
query = IrisSettings.get().getGenerator().getDefaultWorldType();
|
|
||||||
}
|
|
||||||
|
|
||||||
dim = IrisData.loadAnyDimension(query);
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
Iris.proj.downloadSearch(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), query, false);
|
|
||||||
dim = IrisData.loadAnyDimension(query);
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
throw new RuntimeException("Cannot find dimension: " + query);
|
|
||||||
} else {
|
|
||||||
Iris.info("Download pack: " + query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (production) {
|
|
||||||
IrisDimension od = dim;
|
|
||||||
dim = new IrisData(getDataFolder(world)).getDimensionLoader().load(od.getLoadKey());
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
Iris.info("Installing Iris pack " + od.getName() + " into world " + world + "...");
|
|
||||||
Iris.proj.installIntoWorld(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), od.getLoadKey(), new File(world));
|
|
||||||
dim = new IrisData(getDataFolder(world)).getDimensionLoader().load(od.getLoadKey());
|
|
||||||
|
|
||||||
if (dim == null) {
|
|
||||||
throw new RuntimeException("Cannot find dimension: " + query);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.info(world + " is configured to generate " + dim.getName() + "!");
|
|
||||||
|
|
||||||
return dim;
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void initialize(IrisWorld world) {
|
|
||||||
if (initialized.get()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
initialized.set(true);
|
|
||||||
IrisDimension dim = getDimension(world);
|
|
||||||
IrisData data = production ? new IrisData(getDataFolder(world)) : dim.getLoader().copy();
|
|
||||||
compound.set(new IrisEngineCompound(world, dim, data, IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getEngineThreadCount())));
|
|
||||||
compound.get().setStudio(!production);
|
|
||||||
populators.clear();
|
|
||||||
populators.addAll(compound.get().getPopulators());
|
|
||||||
hotloader = new ReactiveFolder(data.getDataFolder(), (a, c, d) -> hotload());
|
|
||||||
cworld = world;
|
|
||||||
|
|
||||||
if (isStudio()) {
|
|
||||||
dim.installDataPack(() -> data, Iris.instance.getDatapacksFolder());
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.error("FAILED TO INITIALIZE DIMENSION FROM " + world.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Place strongholds in the world
|
|
||||||
*/
|
|
||||||
public void placeStrongholds(World world) {
|
|
||||||
EngineData metadata = getComposite().getEngineMetadata();
|
|
||||||
// TODO: In nms class, not here. Also it doesnt work
|
|
||||||
if (metadata.getStrongholdPositions() == null || metadata.getStrongholdPositions().size() == 0) {
|
|
||||||
|
|
||||||
List<IrisPosition> strongholds = new ArrayList<>();
|
|
||||||
Object nmsWorld = new V(world).invoke("getHandle");
|
|
||||||
Object chunkProvider = new V(nmsWorld).invoke("getChunkProvider");
|
|
||||||
Object chunkGenerator = new V(chunkProvider).invoke("getChunkGenerator");
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName("net.minecraft.world.level.chunk.ChunkGenerator");
|
|
||||||
Class<?> clazzSG = Class.forName("net.minecraft.world.level.levelgen.feature.StructureGenerator");
|
|
||||||
Class<?> clazzBP = Class.forName("net.minecraft.core.BlockPosition");
|
|
||||||
@SuppressWarnings("rawtypes") Constructor bpCon = clazzBP.getConstructor(int.class, int.class, int.class);
|
|
||||||
|
|
||||||
//By default, we place 9 strongholds. One near 0,0 and 8 all around it at about 10_000 blocks out
|
|
||||||
int[][] coords = {{0, 0}, {7000, -7000}, {10000, 0}, {7000, 7000}, {0, 10000}, {-7000, 7000}, {-10000, 0}, {-7000, -7000}, {0, -10000}};
|
|
||||||
|
|
||||||
//Set of stronghold locations so we don't place 2 strongholds at the same location
|
|
||||||
Set<Long> existing = new ConcurrentSet<>();
|
|
||||||
Set<CompletableFuture<Object>> futures = new HashSet<>();
|
|
||||||
for (int[] currCoords : coords) {
|
|
||||||
//Create a NMS BlockPosition
|
|
||||||
Object blockPosToTest = bpCon.newInstance(currCoords[0], 0, currCoords[1]);
|
|
||||||
//Create a CompletableFuture so we can track once the sync code is complete
|
|
||||||
|
|
||||||
CompletableFuture<Object> future = new CompletableFuture<>();
|
|
||||||
futures.add(future);
|
|
||||||
|
|
||||||
//We have to run this code synchronously because it uses NMS
|
|
||||||
J.s(() -> {
|
|
||||||
try {
|
|
||||||
Object o = getBP(clazz, clazzSG, clazzBP, nmsWorld, blockPosToTest, chunkGenerator);
|
|
||||||
future.complete(o);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
future.complete(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
CompletableFuture<Void> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
|
||||||
all.thenAccept((_void) -> { //Once all futures for all 9 strongholds have completed
|
|
||||||
for (CompletableFuture<Object> future : futures) {
|
|
||||||
try {
|
|
||||||
Object pos = future.getNow(null);
|
|
||||||
if (pos != null) {
|
|
||||||
IrisPosition ipos = new IrisPosition((int) new V(pos, false).invoke("getX"), (int) new V(pos,
|
|
||||||
false).invoke("getY"), (int) new V(pos, false).invoke("getZ"));
|
|
||||||
long xz = (((long) ipos.getX()) << 32) | (ipos.getZ() & 0xffffffffL);
|
|
||||||
if (existing.contains(xz)) return; //Make sure we don't double up on stronghold locs
|
|
||||||
existing.add(xz);
|
|
||||||
strongholds.add(ipos);
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder positions = new StringBuilder();
|
|
||||||
for (IrisPosition pos : strongholds) {
|
|
||||||
positions.append("(").append(pos.getX()).append(",").append(pos.getY()).append(",").append(pos.getZ()).append(") ");
|
|
||||||
}
|
|
||||||
Iris.info("Strongholds (" + strongholds.size() + ") found at [" + positions + "]");
|
|
||||||
|
|
||||||
metadata.setStrongholdPositions(strongholds);
|
|
||||||
getComposite().saveEngineMetadata();
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
strongholds.add(new IrisPosition(1337, 32, -1337));
|
|
||||||
metadata.setStrongholdPositions(strongholds);
|
|
||||||
Iris.warn("Couldn't properly find the stronghold position for this world. Is this headless mode? Are you not using 1.16 or higher?");
|
|
||||||
Iris.warn(" -> Setting default stronghold position");
|
|
||||||
e.printStackTrace();
|
|
||||||
StringBuilder positions = new StringBuilder();
|
|
||||||
for (IrisPosition pos : strongholds) {
|
|
||||||
positions.append("(").append(pos.getX()).append(",").append(pos.getY()).append(",").append(pos.getZ()).append(") ");
|
|
||||||
}
|
|
||||||
Iris.info("Strongholds (" + metadata.getStrongholdPositions().size() + ") found at [" + positions + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get BlockPosition for nearest stronghold from the provided position
|
|
||||||
*/
|
|
||||||
private Object getBP(Class<?> clazz, Class<?> clazzSG, Class<?> clazzBP, Object nmsWorld, Object pos, Object chunkGenerator) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
|
||||||
final String stronghold = "k"; //1.17_01 mapping
|
|
||||||
|
|
||||||
Object structureGeneratorStronghold = clazzSG.getDeclaredField(stronghold).get(null);
|
|
||||||
Method getNearestGeneratedFeature = clazz.getDeclaredMethod("findNearestMapFeature",
|
|
||||||
nmsWorld.getClass(),
|
|
||||||
clazzSG,
|
|
||||||
clazzBP,
|
|
||||||
int.class,
|
|
||||||
boolean.class
|
|
||||||
);
|
|
||||||
return getNearestGeneratedFeature.invoke(chunkGenerator,
|
|
||||||
nmsWorld,
|
|
||||||
structureGeneratorStronghold,
|
|
||||||
pos,
|
|
||||||
100,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getDataFolder(IrisWorld world) {
|
|
||||||
return new File(world.worldFolder(), "iris/pack");
|
|
||||||
}
|
|
||||||
|
|
||||||
private File getDataFolder(String world) {
|
|
||||||
return new File(world + "/iris/pack");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkData generateChunkData(World world, Random ignored, int x, int z, BiomeGrid biome) {
|
|
||||||
try {
|
|
||||||
PrecisionStopwatch ps = PrecisionStopwatch.start();
|
|
||||||
TerrainChunk tc = TerrainChunk.create(world, biome);
|
|
||||||
IrisWorld ww = (getComposite() == null || getComposite().getWorld() == null) ? IrisWorld.fromWorld(world) : getComposite().getWorld();
|
|
||||||
generateChunkRawData(ww, x, z, tc, true).run();
|
|
||||||
|
|
||||||
if (!getComposite().getWorld().hasRealWorld()) {
|
|
||||||
getComposite().getWorld().bind(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
generated++;
|
|
||||||
ps.end();
|
|
||||||
|
|
||||||
if (IrisSettings.get().getGeneral().isDebug()) {
|
|
||||||
Iris.debug("Chunk " + C.GREEN + x + "," + z + C.LIGHT_PURPLE + " in " + C.YELLOW + Form.duration(ps.getMillis(), 2) + C.LIGHT_PURPLE + " Rate: " + C.BLUE + Form.f(getGeneratedPerSecond(), 0) + "/s");
|
|
||||||
}
|
|
||||||
|
|
||||||
return tc.getRaw();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.error("======================================");
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.reportErrorChunk(x, z, e, "CHUNK");
|
|
||||||
Iris.error("======================================");
|
|
||||||
|
|
||||||
ChunkData d = Bukkit.createChunkData(world);
|
|
||||||
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
for (int j = 0; j < 16; j++) {
|
|
||||||
d.setBlock(i, 0, j, ERROR_BLOCK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assignHeadlessGenerator(HeadlessGenerator headlessGenerator) {
|
|
||||||
this.headlessGenerator = headlessGenerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeadlessGenerator getHeadlessGenerator() {
|
|
||||||
return headlessGenerator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void assignHeadlessNBTWriter(NBTWorld writer) {
|
|
||||||
this.nbtWorld = writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public NBTWorld getHeadlessNBTWriter() {
|
|
||||||
return nbtWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst) {
|
|
||||||
directWriteMCA(w, x, z, writer, burst, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst, PregenListener l) {
|
|
||||||
BurstExecutor e = burst.burst(1024);
|
|
||||||
|
|
||||||
PregenTask.iterateRegion(x, z, (ii, jj) -> e.queue(() -> {
|
|
||||||
if (l != null) {
|
|
||||||
l.onChunkGenerating(ii, jj);
|
|
||||||
}
|
|
||||||
directWriteChunk(w, ii, jj, writer);
|
|
||||||
if (l != null) {
|
|
||||||
l.onChunkGenerated(ii, jj);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
e.complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void directWriteChunk(IrisWorld w, int x, int z, NBTWorld writer) {
|
|
||||||
try {
|
|
||||||
int ox = x << 4;
|
|
||||||
int oz = z << 4;
|
|
||||||
com.volmit.iris.util.nbt.mca.Chunk chunk = writer.getChunk(x, z);
|
|
||||||
generateChunkRawData(w, x, z, MCATerrainChunk.builder()
|
|
||||||
.writer(writer).ox(ox).oz(oz).mcaChunk(chunk)
|
|
||||||
.minHeight(w.minHeight()).maxHeight(w.maxHeight())
|
|
||||||
.injector((xx, yy, zz, biomeBase) -> chunk.setBiomeAt(ox + xx, yy, oz + zz,
|
|
||||||
INMS.get().getTrueBiomeBaseId(biomeBase)))
|
|
||||||
.build(), false).run();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.error("======================================");
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.reportErrorChunk(x, z, e, "MCA");
|
|
||||||
Iris.error("======================================");
|
|
||||||
com.volmit.iris.util.nbt.mca.Chunk chunk = writer.getChunk(x, z);
|
|
||||||
CompoundTag c = NBTWorld.getCompound(ERROR_BLOCK);
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
for (int j = 0; j < 16; j++) {
|
|
||||||
chunk.setBlockStateAt(i, 0, j, c, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Runnable generateChunkRawData(IrisWorld world, int x, int z, TerrainChunk tc, boolean multicore) {
|
|
||||||
initialize(world);
|
|
||||||
tickMetrics();
|
|
||||||
|
|
||||||
Hunk<BlockData> blocks = Hunk.view((ChunkData) tc);
|
|
||||||
Hunk<Biome> biomes = Hunk.view((BiomeGrid) tc);
|
|
||||||
Hunk<BlockData> post = Hunk.newAtomicHunk(biomes.getWidth(), biomes.getHeight(), biomes.getDepth());
|
|
||||||
compound.get().generate(x * 16, z * 16, blocks, post, biomes, multicore);
|
|
||||||
|
|
||||||
return () -> blocks.insertSoftly(0, 0, 0, post, (b) -> b == null || B.isAirOrFluid(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tickMetrics() {
|
|
||||||
if (M.ms() - mst > 1000) {
|
|
||||||
generatedPerSecond = (double) (generated - lgenerated) / ((double) (M.ms() - mst) / 1000D);
|
|
||||||
mst = M.ms();
|
|
||||||
lgenerated = generated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canSpawn(World world, int x, int z) {
|
|
||||||
return super.canSpawn(world, x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<BlockPopulator> getDefaultPopulators(World world) {
|
|
||||||
return populators;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Location getFixedSpawnLocation(World world, Random random) {
|
|
||||||
return super.getFixedSpawnLocation(world, random);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isParallelCapable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateCaves() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateDecorations() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateMobs() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateStructures() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EngineCompositeGenerator newStudioWorld(String dimension) {
|
|
||||||
return new EngineCompositeGenerator(dimension, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EngineCompositeGenerator newProductionWorld(String dimension) {
|
|
||||||
return new EngineCompositeGenerator(dimension, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EngineCompositeGenerator newProductionWorld() {
|
|
||||||
return new EngineCompositeGenerator(null, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public EngineCompound getComposite() {
|
|
||||||
return compound.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IrisBiome getBiome(int x, int z) {
|
|
||||||
return getBiome(x, 0, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IrisBiome getCaveBiome(int x, int z) {
|
|
||||||
return getCaveBiome(x, 0, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getGenerated() {
|
|
||||||
return generated;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printMetrics(CommandSender sender) {
|
|
||||||
getComposite().printMetrics(sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IrisBiome getBiome(int x, int y, int z) {
|
|
||||||
// TODO: REMOVE GET ABS BIOME OR THIS ONE
|
|
||||||
return getEngineAccess(y).getBiome(x, y - getComposite().getEngineForHeight(y).getMinHeight(), z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IrisBiome getCaveBiome(int x, int y, int z) {
|
|
||||||
return getEngineAccess(y).getCaveBiome(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GeneratorAccess getEngineAccess(int y) {
|
|
||||||
return getComposite().getEngineForHeight(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IrisData getData() {
|
|
||||||
if (getCompound() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getComposite().getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight(int x, int y, int z) {
|
|
||||||
return getEngineAccess(y).getHeight(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getThreadCount() {
|
|
||||||
return getComposite().getThreadCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changeThreadCount(int m) {
|
|
||||||
// TODO: DO IT
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
if (isStudio()) {
|
|
||||||
ticker.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
cleaner.interrupt();
|
|
||||||
|
|
||||||
if (getComposite() != null) {
|
|
||||||
getComposite().close();
|
|
||||||
|
|
||||||
if (isStudio() && getComposite().getWorld().hasRealWorld()) {
|
|
||||||
getComposite().getWorld().evacuate();
|
|
||||||
Bukkit.unloadWorld(getComposite().getWorld().realWorld(), !isStudio());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isClosed() {
|
|
||||||
try {
|
|
||||||
return getComposite().getEngine(0).isClosed();
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EngineTarget getTarget() {
|
|
||||||
try {
|
|
||||||
return getComposite().getEngine(0).getTarget();
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
Iris.info("Failed to get composite engine. Please re-create the world in case you notice issues");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EngineCompound getCompound() {
|
|
||||||
return getComposite();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFailing() {
|
|
||||||
if (getComposite() == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getComposite().isFailing();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStudio() {
|
|
||||||
return !production;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVanillaCaves() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public KList<IrisBiome> getAllBiomes(String worldName) {
|
|
||||||
if (getComposite() != null) {
|
|
||||||
return getComposite().getAllBiomes();
|
|
||||||
} else {
|
|
||||||
KMap<String, IrisBiome> v = new KMap<>();
|
|
||||||
IrisDimension dim = getDimension(worldName);
|
|
||||||
dim.getAllAnyBiomes().forEach((i) -> v.put(i.getLoadKey(), i));
|
|
||||||
|
|
||||||
try {
|
|
||||||
dim.getDimensionalComposite().forEach((m) -> IrisData.loadAnyDimension(m.getDimension()).getAllAnyBiomes().forEach((i) -> v.put(i.getLoadKey(), i)));
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
Iris.reportError(ignored);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Iris.info("Injecting " + v.size() + " biomes into the NMS World Chunk Provider (Iris)");
|
|
||||||
|
|
||||||
return v.v();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.engine.framework;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.project.loader.IrisData;
|
|
||||||
import com.volmit.iris.engine.actuator.IrisTerrainNormalActuator;
|
|
||||||
import com.volmit.iris.engine.object.basic.IrisPosition;
|
|
||||||
import com.volmit.iris.engine.object.biome.IrisBiome;
|
|
||||||
import com.volmit.iris.engine.object.common.IrisWorld;
|
|
||||||
import com.volmit.iris.engine.object.dimensional.IrisDimension;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.data.DataProvider;
|
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.generator.BlockPopulator;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface EngineCompound extends Listener, Hotloadable, DataProvider {
|
|
||||||
IrisDimension getRootDimension();
|
|
||||||
|
|
||||||
void generate(int x, int z, Hunk<BlockData> blocks, Hunk<BlockData> postblocks, Hunk<Biome> biomes, boolean multicore);
|
|
||||||
|
|
||||||
IrisWorld getWorld();
|
|
||||||
|
|
||||||
List<IrisPosition> getStrongholdPositions();
|
|
||||||
|
|
||||||
void printMetrics(CommandSender sender);
|
|
||||||
|
|
||||||
int getSize();
|
|
||||||
|
|
||||||
default int getHeight() {
|
|
||||||
// TODO: WARNING HEIGHT
|
|
||||||
return 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine getEngine(int index);
|
|
||||||
|
|
||||||
MultiBurst getBurster();
|
|
||||||
|
|
||||||
EngineData getEngineMetadata();
|
|
||||||
|
|
||||||
void saveEngineMetadata();
|
|
||||||
|
|
||||||
KList<BlockPopulator> getPopulators();
|
|
||||||
|
|
||||||
default Engine getEngineForHeight(int height) {
|
|
||||||
if (getSize() == 1) {
|
|
||||||
return getEngine(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int buf = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
Engine e = getEngine(i);
|
|
||||||
buf += e.getHeight();
|
|
||||||
|
|
||||||
if (buf >= height) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getEngine(getSize() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void recycle() {
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
getEngine(i).recycle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default void save() {
|
|
||||||
saveEngineMetadata();
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
getEngine(i).save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default void saveNOW() {
|
|
||||||
saveEngineMetadata();
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
getEngine(i).saveNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IrisData getData(int height);
|
|
||||||
|
|
||||||
default IrisData getData() {
|
|
||||||
return getData(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void close() {
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
getEngine(i).close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isFailing();
|
|
||||||
|
|
||||||
int getThreadCount();
|
|
||||||
|
|
||||||
boolean isStudio();
|
|
||||||
|
|
||||||
void setStudio(boolean std);
|
|
||||||
|
|
||||||
default void clean() {
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
getEngine(i).clean();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Engine getDefaultEngine();
|
|
||||||
|
|
||||||
default KList<IrisBiome> getAllBiomes() {
|
|
||||||
KMap<String, IrisBiome> v = new KMap<>();
|
|
||||||
|
|
||||||
IrisDimension dim = getRootDimension();
|
|
||||||
dim.getAllBiomes(this).forEach((i) -> v.put(i.getLoadKey(), i));
|
|
||||||
|
|
||||||
try {
|
|
||||||
dim.getDimensionalComposite().forEach((m) -> getData().getDimensionLoader().load(m.getDimension()).getAllBiomes(this).forEach((i) -> v.put(i.getLoadKey(), i)));
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
Iris.reportError(ignored);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.v();
|
|
||||||
}
|
|
||||||
|
|
||||||
default int getLowestBedrock() {
|
|
||||||
int f = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
for (int i = 0; i < getSize(); i++) {
|
|
||||||
Engine e = getEngine(i);
|
|
||||||
|
|
||||||
if (e.getDimension().isBedrock()) {
|
|
||||||
int m = ((IrisTerrainNormalActuator) e.getTerrainActuator()).getLastBedrock();
|
|
||||||
|
|
||||||
if (f > m) {
|
|
||||||
f = m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,287 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.engine.framework;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
|
||||||
import com.volmit.iris.core.project.loader.IrisData;
|
|
||||||
import com.volmit.iris.engine.framework.headless.HeadlessGenerator;
|
|
||||||
import com.volmit.iris.engine.object.biome.IrisBiome;
|
|
||||||
import com.volmit.iris.engine.object.common.IrisWorld;
|
|
||||||
import com.volmit.iris.engine.object.regional.IrisRegion;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.data.DataProvider;
|
|
||||||
import com.volmit.iris.util.math.M;
|
|
||||||
import com.volmit.iris.util.math.RNG;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
|
||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
|
||||||
import com.volmit.iris.util.scheduling.J;
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
@SuppressWarnings("EmptyMethod")
|
|
||||||
public interface IrisAccess extends Hotloadable, DataProvider {
|
|
||||||
|
|
||||||
HeadlessGenerator getHeadlessGenerator();
|
|
||||||
|
|
||||||
default boolean isHeadless() {
|
|
||||||
return getHeadlessGenerator() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
NBTWorld getHeadlessNBTWriter();
|
|
||||||
|
|
||||||
void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst);
|
|
||||||
|
|
||||||
void directWriteMCA(IrisWorld w, int x, int z, NBTWorld writer, MultiBurst burst, PregenListener listener);
|
|
||||||
|
|
||||||
void directWriteChunk(IrisWorld w, int x, int z, NBTWorld writer);
|
|
||||||
|
|
||||||
int getGenerated();
|
|
||||||
|
|
||||||
double getGeneratedPerSecond();
|
|
||||||
|
|
||||||
void printMetrics(CommandSender sender);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ignores the world, just uses the position
|
|
||||||
*
|
|
||||||
* @param l the location
|
|
||||||
* @return the biome
|
|
||||||
*/
|
|
||||||
default IrisBiome getBiome(Location l) {
|
|
||||||
return getBiome(l.toVector());
|
|
||||||
}
|
|
||||||
|
|
||||||
default IrisRegion getRegion(int x, int y, int z) {
|
|
||||||
return getEngineAccess(y).getRegion(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
default IrisRegion getRegion(Location l) {
|
|
||||||
return getRegion(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
default IrisBiome getBiome(Vector l) {
|
|
||||||
return getBiome(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
IrisBiome getBiome(int x, int y, int z);
|
|
||||||
|
|
||||||
IrisBiome getCaveBiome(int x, int y, int z);
|
|
||||||
|
|
||||||
IrisBiome getBiome(int x, int z);
|
|
||||||
|
|
||||||
IrisBiome getCaveBiome(int x, int z);
|
|
||||||
|
|
||||||
GeneratorAccess getEngineAccess(int y);
|
|
||||||
|
|
||||||
IrisData getData();
|
|
||||||
|
|
||||||
int getHeight(int x, int y, int z);
|
|
||||||
|
|
||||||
int getThreadCount();
|
|
||||||
|
|
||||||
void changeThreadCount(int m);
|
|
||||||
|
|
||||||
void close();
|
|
||||||
|
|
||||||
boolean isClosed();
|
|
||||||
|
|
||||||
EngineTarget getTarget();
|
|
||||||
|
|
||||||
EngineCompound getCompound();
|
|
||||||
|
|
||||||
boolean isFailing();
|
|
||||||
|
|
||||||
boolean isStudio();
|
|
||||||
|
|
||||||
default Location lookForBiome(IrisBiome biome, long timeout, Consumer<Integer> triesc) {
|
|
||||||
if (!getCompound().getWorld().hasRealWorld()) {
|
|
||||||
Iris.error("Cannot GOTO without a bound world (headless mode)");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChronoLatch cl = new ChronoLatch(250, false);
|
|
||||||
long s = M.ms();
|
|
||||||
int cpus = (Runtime.getRuntime().availableProcessors());
|
|
||||||
KList<Engine> engines = new KList<>();
|
|
||||||
for (int i = 0; i < getCompound().getSize(); i++) {
|
|
||||||
Engine e = getCompound().getEngine(i);
|
|
||||||
if (e.getDimension().getAllBiomes(e).contains(biome)) {
|
|
||||||
engines.add(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engines.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
AtomicInteger tries = new AtomicInteger(0);
|
|
||||||
AtomicBoolean found = new AtomicBoolean(false);
|
|
||||||
AtomicBoolean running = new AtomicBoolean(true);
|
|
||||||
AtomicReference<Location> location = new AtomicReference<>();
|
|
||||||
for (int i = 0; i < cpus; i++) {
|
|
||||||
J.a(() -> {
|
|
||||||
try {
|
|
||||||
Engine e;
|
|
||||||
IrisBiome b;
|
|
||||||
int x, z;
|
|
||||||
|
|
||||||
while (!found.get() && running.get()) {
|
|
||||||
try {
|
|
||||||
synchronized (engines) {
|
|
||||||
e = engines.getRandom();
|
|
||||||
x = RNG.r.i(-29999970, 29999970);
|
|
||||||
z = RNG.r.i(-29999970, 29999970);
|
|
||||||
b = e.getSurfaceBiome(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b != null && b.getLoadKey() == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b != null && b.getLoadKey().equals(biome.getLoadKey())) {
|
|
||||||
found.lazySet(true);
|
|
||||||
location.lazySet(new Location(e.getWorld().realWorld(), x, e.getHeight(x, z), z));
|
|
||||||
}
|
|
||||||
|
|
||||||
tries.getAndIncrement();
|
|
||||||
} catch (Throwable ex) {
|
|
||||||
Iris.reportError(ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!found.get() || location.get() == null) {
|
|
||||||
J.sleep(50);
|
|
||||||
|
|
||||||
if (cl.flip()) {
|
|
||||||
triesc.accept(tries.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (M.ms() - s > timeout) {
|
|
||||||
running.set(false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
running.set(false);
|
|
||||||
return location.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
default Location lookForRegion(IrisRegion reg, long timeout, Consumer<Integer> triesc) {
|
|
||||||
if (!getCompound().getWorld().hasRealWorld()) {
|
|
||||||
Iris.error("Cannot GOTO without a bound world (headless mode)");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChronoLatch cl = new ChronoLatch(3000, false);
|
|
||||||
long s = M.ms();
|
|
||||||
int cpus = (Runtime.getRuntime().availableProcessors());
|
|
||||||
KList<Engine> engines = new KList<>();
|
|
||||||
for (int i = 0; i < getCompound().getSize(); i++) {
|
|
||||||
Engine e = getCompound().getEngine(i);
|
|
||||||
if (e.getDimension().getRegions().contains(reg.getLoadKey())) {
|
|
||||||
engines.add(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engines.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
AtomicInteger tries = new AtomicInteger(0);
|
|
||||||
AtomicBoolean found = new AtomicBoolean(false);
|
|
||||||
AtomicBoolean running = new AtomicBoolean(true);
|
|
||||||
AtomicReference<Location> location = new AtomicReference<>();
|
|
||||||
|
|
||||||
for (int i = 0; i < cpus; i++) {
|
|
||||||
J.a(() -> {
|
|
||||||
Engine e;
|
|
||||||
IrisRegion b;
|
|
||||||
int x, z;
|
|
||||||
|
|
||||||
while (!found.get() && running.get()) {
|
|
||||||
try {
|
|
||||||
e = engines.getRandom();
|
|
||||||
x = RNG.r.i(-29999970, 29999970);
|
|
||||||
z = RNG.r.i(-29999970, 29999970);
|
|
||||||
b = e.getRegion(x, z);
|
|
||||||
|
|
||||||
if (b != null && b.getLoadKey() != null && b.getLoadKey().equals(reg.getLoadKey())) {
|
|
||||||
found.lazySet(true);
|
|
||||||
location.lazySet(new Location(e.getWorld().realWorld(), x, e.getHeight(x, z) + e.getMinHeight(), z));
|
|
||||||
}
|
|
||||||
|
|
||||||
tries.getAndIncrement();
|
|
||||||
} catch (Throwable xe) {
|
|
||||||
Iris.reportError(xe);
|
|
||||||
xe.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!found.get() || location.get() != null) {
|
|
||||||
J.sleep(50);
|
|
||||||
|
|
||||||
if (cl.flip()) {
|
|
||||||
triesc.accept(tries.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (M.ms() - s > timeout) {
|
|
||||||
triesc.accept(tries.get());
|
|
||||||
running.set(false);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
triesc.accept(tries.get());
|
|
||||||
running.set(false);
|
|
||||||
return location.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
default int getParallaxChunkCount() {
|
|
||||||
int v = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < getCompound().getSize(); i++) {
|
|
||||||
v += getCompound().getEngine(i).getParallax().getChunkCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
default double getHeight(Location l) {
|
|
||||||
return getHeight(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.engine.framework.headless;
|
|
||||||
|
|
||||||
import com.volmit.iris.core.IrisSettings;
|
|
||||||
import com.volmit.iris.core.pregenerator.PregenListener;
|
|
||||||
import com.volmit.iris.engine.framework.EngineCompositeGenerator;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.math.Position2;
|
|
||||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
|
||||||
import com.volmit.iris.util.nbt.mca.NBTWorld;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class HeadlessGenerator {
|
|
||||||
private static KList<Position2> EMPTYPOINTS = new KList<>();
|
|
||||||
private final HeadlessWorld world;
|
|
||||||
private final EngineCompositeGenerator generator;
|
|
||||||
private final NBTWorld writer;
|
|
||||||
private final MultiBurst burst;
|
|
||||||
|
|
||||||
public HeadlessGenerator(HeadlessWorld world) {
|
|
||||||
this.world = world;
|
|
||||||
burst = new MultiBurst("Iris Headless Generator", 9, IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getPregenThreadCount()));
|
|
||||||
writer = new NBTWorld(world.getWorld().worldFolder());
|
|
||||||
generator = new EngineCompositeGenerator(world.getDimension().getLoadKey(), !world.isStudio());
|
|
||||||
generator.assignHeadlessGenerator(this);
|
|
||||||
generator.assignHeadlessNBTWriter(writer);
|
|
||||||
generator.initialize(world.getWorld());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateChunk(int x, int z) {
|
|
||||||
generator.directWriteChunk(world.getWorld(), x, z, writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateRegion(int x, int z) {
|
|
||||||
generator.directWriteMCA(world.getWorld(), x, z, writer, burst);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void generateRegion(int x, int z, PregenListener listener) {
|
|
||||||
generator.directWriteMCA(world.getWorld(), x, z, writer, burst, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public File generateRegionToFile(int x, int z, PregenListener listener) {
|
|
||||||
generateRegionToFile(x, z, listener);
|
|
||||||
flush();
|
|
||||||
return writer.getRegionFile(x, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void flush() {
|
|
||||||
writer.flushNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
|
||||||
writer.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
burst.shutdownAndAwait();
|
|
||||||
generator.close();
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public KList<Position2> getChunksInRegion(int x, int z) {
|
|
||||||
try {
|
|
||||||
return MCAUtil.sampleChunkPositions(writer.getRegionFile(x, z));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return EMPTYPOINTS;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
|
||||||
* Copyright (c) 2021 Arcane Arts (Volmit Software)
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.volmit.iris.engine.framework.headless;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.project.loader.IrisData;
|
|
||||||
import com.volmit.iris.core.tools.IrisWorlds;
|
|
||||||
import com.volmit.iris.engine.framework.EngineCompositeGenerator;
|
|
||||||
import com.volmit.iris.engine.object.common.IrisWorld;
|
|
||||||
import com.volmit.iris.engine.object.dimensional.IrisDimension;
|
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.WorldCreator;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
|
||||||
public class HeadlessWorld {
|
|
||||||
private final IrisDimension dimension;
|
|
||||||
private final String worldName;
|
|
||||||
private final IrisWorld world;
|
|
||||||
private boolean studio = false;
|
|
||||||
|
|
||||||
public HeadlessWorld(String worldName, IrisDimension dimension, long seed) {
|
|
||||||
this(worldName, dimension, seed, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HeadlessWorld(String worldName, IrisDimension dimension, long seed, boolean studio) {
|
|
||||||
this.worldName = worldName;
|
|
||||||
this.dimension = dimension;
|
|
||||||
this.studio = studio;
|
|
||||||
world = IrisWorld.builder()
|
|
||||||
.environment(dimension.getEnvironment())
|
|
||||||
.worldFolder(new File(worldName))
|
|
||||||
.seed(seed)
|
|
||||||
.maxHeight(256)
|
|
||||||
.minHeight(0)
|
|
||||||
.name(worldName)
|
|
||||||
.build();
|
|
||||||
world.worldFolder().mkdirs();
|
|
||||||
new File(world.worldFolder(), "region").mkdirs();
|
|
||||||
|
|
||||||
if (!studio && !new File(world.worldFolder(), "iris").exists()) {
|
|
||||||
Iris.proj.installIntoWorld(new VolmitSender(Bukkit.getConsoleSender(), Iris.instance.getTag("Headless")), dimension.getLoadKey(), world.worldFolder());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public HeadlessGenerator generate() {
|
|
||||||
return new HeadlessGenerator(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public World load() {
|
|
||||||
World w = new WorldCreator(worldName)
|
|
||||||
.environment(dimension.getEnvironment())
|
|
||||||
.seed(world.seed())
|
|
||||||
.generator(new EngineCompositeGenerator(dimension.getLoadKey(), !studio))
|
|
||||||
.createWorld();
|
|
||||||
world.realWorld(w);
|
|
||||||
return w;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HeadlessWorld from(World world) {
|
|
||||||
return new HeadlessWorld(world.getName(), IrisWorlds.access(world).getTarget().getDimension(), world.getSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HeadlessWorld from(String name, String dimension, long seed) {
|
|
||||||
return new HeadlessWorld(name, IrisData.loadAnyDimension(dimension), seed);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user