mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-28 11:39:07 +00:00
Restructure Packages (read commit description)
There are now three root packages - Engine: Generator & JSON Scaffolding - Core: Managers, Commands, NMS - Util: Random utility packages
This commit is contained in:
377
src/main/java/com/volmit/iris/engine/IrisComplex.java
Normal file
377
src/main/java/com/volmit/iris/engine/IrisComplex.java
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* 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.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.engine.actuator.IrisTerrainActuator;
|
||||
import com.volmit.iris.engine.modifier.IrisCaveModifier;
|
||||
import com.volmit.iris.engine.noise.CNG;
|
||||
import com.volmit.iris.core.IrisDataManager;
|
||||
import com.volmit.iris.engine.object.*;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.stream.ProceduralStream;
|
||||
import com.volmit.iris.engine.stream.interpolation.Interpolated;
|
||||
import com.volmit.iris.util.CaveResult;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.M;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
@Data
|
||||
public class IrisComplex implements DataProvider {
|
||||
private RNG rng;
|
||||
private double fluidHeight;
|
||||
private IrisDataManager data;
|
||||
private KList<IrisGenerator> generators;
|
||||
private static final BlockData AIR = Material.AIR.createBlockData();
|
||||
private ProceduralStream<IrisRegion> regionStream;
|
||||
private ProceduralStream<InferredType> bridgeStream;
|
||||
private ProceduralStream<IrisBiome> landBiomeStream;
|
||||
private ProceduralStream<IrisBiome> caveBiomeStream;
|
||||
private ProceduralStream<IrisBiome> seaBiomeStream;
|
||||
private ProceduralStream<IrisBiome> shoreBiomeStream;
|
||||
private ProceduralStream<IrisBiome> baseBiomeStream;
|
||||
private ProceduralStream<IrisBiome> trueBiomeStream;
|
||||
private ProceduralStream<Biome> trueBiomeDerivativeStream;
|
||||
private ProceduralStream<Double> heightStream;
|
||||
private ProceduralStream<Double> objectChanceStream;
|
||||
private ProceduralStream<Double> maxHeightStream;
|
||||
private ProceduralStream<Double> overlayStream;
|
||||
private ProceduralStream<Double> heightFluidStream;
|
||||
private ProceduralStream<Integer> trueHeightStream;
|
||||
private ProceduralStream<Double> slopeStream;
|
||||
private ProceduralStream<RNG> rngStream;
|
||||
private ProceduralStream<RNG> chunkRngStream;
|
||||
private ProceduralStream<IrisDecorator> terrainSurfaceDecoration;
|
||||
private ProceduralStream<IrisDecorator> terrainCeilingDecoration;
|
||||
private ProceduralStream<IrisDecorator> terrainCaveSurfaceDecoration;
|
||||
private ProceduralStream<IrisDecorator> terrainCaveCeilingDecoration;
|
||||
private ProceduralStream<IrisDecorator> seaSurfaceDecoration;
|
||||
private ProceduralStream<IrisDecorator> seaFloorDecoration;
|
||||
private ProceduralStream<IrisDecorator> shoreSurfaceDecoration;
|
||||
private ProceduralStream<BlockData> rockStream;
|
||||
private ProceduralStream<BlockData> fluidStream;
|
||||
private IrisBiome focus;
|
||||
|
||||
public ProceduralStream<IrisBiome> getBiomeStream(InferredType type) {
|
||||
switch (type) {
|
||||
case CAVE:
|
||||
return caveBiomeStream;
|
||||
case LAND:
|
||||
return landBiomeStream;
|
||||
case SEA:
|
||||
return seaBiomeStream;
|
||||
case SHORE:
|
||||
return shoreBiomeStream;
|
||||
case DEFER:
|
||||
case LAKE:
|
||||
case RIVER:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public IrisComplex(Engine engine) {
|
||||
this(engine, false);
|
||||
}
|
||||
|
||||
public IrisComplex(Engine engine, boolean simple) {
|
||||
int cacheSize = IrisSettings.get().getCache().getStreamingCacheSize();
|
||||
this.rng = new RNG(engine.getWorld().getSeed());
|
||||
this.data = engine.getData();
|
||||
double height = engine.getHeight();
|
||||
fluidHeight = engine.getDimension().getFluidHeight();
|
||||
generators = new KList<>();
|
||||
focus = engine.getFocus();
|
||||
IrisRegion focusRegion = focus != null ? findRegion(focus, engine) : null;
|
||||
RNG rng = new RNG(engine.getWorld().getSeed());
|
||||
//@builder
|
||||
engine.getDimension().getRegions().forEach((i) -> data.getRegionLoader().load(i)
|
||||
.getAllBiomes(this).forEach((b) -> b
|
||||
.getGenerators()
|
||||
.forEach((c) -> registerGenerator(c.getCachedGenerator(this)))));
|
||||
overlayStream = ProceduralStream.ofDouble((x, z) -> 0D);
|
||||
engine.getDimension().getOverlayNoise().forEach((i) -> overlayStream.add((x, z) -> i.get(rng, x, z)));
|
||||
rngStream = ProceduralStream.of((x, z) -> new RNG(((x.longValue()) << 32) | (z.longValue() & 0xffffffffL))
|
||||
.nextParallelRNG(engine.getWorld().getSeed()), Interpolated.RNG);
|
||||
chunkRngStream = rngStream.blockToChunkCoords();
|
||||
rockStream = engine.getDimension().getRockPalette().getLayerGenerator(rng.nextParallelRNG(45), data).stream()
|
||||
.select(engine.getDimension().getRockPalette().getBlockData(data));
|
||||
fluidStream = engine.getDimension().getFluidPalette().getLayerGenerator(rng.nextParallelRNG(78), data).stream()
|
||||
.select(engine.getDimension().getFluidPalette().getBlockData(data));
|
||||
regionStream = focusRegion != null ?
|
||||
ProceduralStream.of((x, z) -> focusRegion,
|
||||
Interpolated.of(a -> 0D, a -> focusRegion))
|
||||
: engine.getDimension().getRegionStyle().create(rng.nextParallelRNG(883)).stream()
|
||||
.zoom(engine.getDimension().getRegionZoom())
|
||||
.selectRarity(engine.getDimension().getRegions(), (i) -> data.getRegionLoader().load(i))
|
||||
.convertCached((s) -> data.getRegionLoader().load(s)).cache2D(cacheSize);
|
||||
caveBiomeStream = regionStream.convert((r)
|
||||
-> engine.getDimension().getCaveBiomeStyle().create(rng.nextParallelRNG(1221)).stream()
|
||||
.zoom(r.getCaveBiomeZoom())
|
||||
.selectRarity(r.getCaveBiomes(), (i) -> data.getBiomeLoader().load(i))
|
||||
.onNull("")
|
||||
.convertCached((s) -> {
|
||||
if (s.isEmpty()) {
|
||||
return new IrisBiome();
|
||||
}
|
||||
|
||||
return data.getBiomeLoader().load(s)
|
||||
.setInferredType(InferredType.CAVE);
|
||||
})
|
||||
).convertAware2D(ProceduralStream::get).cache2D(cacheSize);
|
||||
landBiomeStream = regionStream.convert((r)
|
||||
-> engine.getDimension().getLandBiomeStyle().create(rng.nextParallelRNG(234234234)).stream()
|
||||
.zoom(r.getLandBiomeZoom())
|
||||
.selectRarity(r.getLandBiomes(), (i) -> data.getBiomeLoader().load(i))
|
||||
.convertCached((s) -> data.getBiomeLoader().load(s)
|
||||
.setInferredType(InferredType.LAND))
|
||||
).convertAware2D(ProceduralStream::get)
|
||||
.cache2D(cacheSize);
|
||||
seaBiomeStream = regionStream.convert((r)
|
||||
-> engine.getDimension().getSeaBiomeStyle().create(rng.nextParallelRNG(11232323)).stream()
|
||||
.zoom(r.getSeaBiomeZoom())
|
||||
.selectRarity(r.getSeaBiomes(), (i) -> data.getBiomeLoader().load(i))
|
||||
.convertCached((s) -> data.getBiomeLoader().load(s)
|
||||
.setInferredType(InferredType.SEA))
|
||||
).convertAware2D(ProceduralStream::get)
|
||||
.cache2D(cacheSize);
|
||||
shoreBiomeStream = regionStream.convert((r)
|
||||
-> engine.getDimension().getShoreBiomeStyle().create(rng.nextParallelRNG(7787845)).stream()
|
||||
.zoom(r.getShoreBiomeZoom())
|
||||
.selectRarity(r.getShoreBiomes(), (i) -> data.getBiomeLoader().load(i))
|
||||
.convertCached((s) -> data.getBiomeLoader().load(s)
|
||||
.setInferredType(InferredType.SHORE))
|
||||
).convertAware2D(ProceduralStream::get).cache2D(cacheSize);
|
||||
bridgeStream = focus != null ? ProceduralStream.of((x, z) -> focus.getInferredType(),
|
||||
Interpolated.of(a -> 0D, a -> focus.getInferredType())) :
|
||||
engine.getDimension().getContinentalStyle().create(rng.nextParallelRNG(234234565))
|
||||
.bake().scale(1D / engine.getDimension().getContinentZoom()).bake().stream()
|
||||
.convert((v) -> v >= engine.getDimension().getLandChance() ? InferredType.SEA : InferredType.LAND);
|
||||
baseBiomeStream = focus != null ? ProceduralStream.of((x, z) -> focus,
|
||||
Interpolated.of(a -> 0D, a -> focus)) :
|
||||
bridgeStream.convertAware2D((t, x, z) -> t.equals(InferredType.SEA)
|
||||
? seaBiomeStream.get(x, z) : landBiomeStream.get(x, z))
|
||||
.convertAware2D(this::implode).cache2D(cacheSize);
|
||||
heightStream = ProceduralStream.of((x, z) -> {
|
||||
IrisBiome b = focus != null ? focus : baseBiomeStream.get(x, z);
|
||||
return getHeight(engine, b, x, z, engine.getWorld().getSeed());
|
||||
}, Interpolated.DOUBLE).cache2D(cacheSize);
|
||||
slopeStream = heightStream.slope(3).interpolate().bilinear(3, 3).cache2D(cacheSize);
|
||||
objectChanceStream = ProceduralStream.ofDouble((x, z) -> {
|
||||
AtomicDouble str = new AtomicDouble(1D);
|
||||
engine.getFramework().getEngineParallax().forEachFeature(x, z, (i)
|
||||
-> str.set(Math.min(str.get(), i.getObjectChanceModifier(x, z))));
|
||||
return str.get();
|
||||
});
|
||||
|
||||
trueBiomeStream = focus != null ? ProceduralStream.of((x, y) -> focus, Interpolated.of(a -> 0D,
|
||||
b -> focus)) : heightStream
|
||||
.convertAware2D((h, x, z) ->
|
||||
fixBiomeType(h, baseBiomeStream.get(x, z),
|
||||
regionStream.get(x, z), x, z, fluidHeight)).cache2D(cacheSize);
|
||||
trueBiomeDerivativeStream = trueBiomeStream.convert(IrisBiome::getDerivative).cache2D(cacheSize);
|
||||
heightFluidStream = heightStream.max(fluidHeight).cache2D(cacheSize);
|
||||
maxHeightStream = ProceduralStream.ofDouble((x, z) -> height);
|
||||
terrainSurfaceDecoration = trueBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.NONE));
|
||||
terrainCeilingDecoration = trueBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.CEILING));
|
||||
terrainCaveSurfaceDecoration = caveBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.NONE));
|
||||
terrainCaveCeilingDecoration = caveBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.CEILING));
|
||||
shoreSurfaceDecoration = trueBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.SHORE_LINE));
|
||||
seaSurfaceDecoration = trueBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.SEA_SURFACE));
|
||||
seaFloorDecoration = trueBiomeStream
|
||||
.convertAware2D((b, xx, zz) -> decorateFor(b, xx, zz, DecorationPart.SEA_FLOOR));
|
||||
trueHeightStream = ProceduralStream.of((x, z) -> {
|
||||
int rx = (int) Math.round(engine.modifyX(x));
|
||||
int rz = (int) Math.round(engine.modifyZ(z));
|
||||
int heightf = (int) Math.round(getHeightStream().get(rx, rz));
|
||||
int m = heightf;
|
||||
|
||||
if (engine.getDimension().isCarving()) {
|
||||
if (engine.getDimension().isCarved(rx, m, rz, ((IrisTerrainActuator) engine.getFramework().getTerrainActuator()).getRng(), heightf)) {
|
||||
m--;
|
||||
|
||||
while (engine.getDimension().isCarved(rx, m, rz, ((IrisTerrainActuator) engine.getFramework().getTerrainActuator()).getRng(), heightf)) {
|
||||
m--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (engine.getDimension().isCaves()) {
|
||||
KList<CaveResult> caves = ((IrisCaveModifier) engine.getFramework().getCaveModifier()).genCaves(rx, rz, 0, 0, null);
|
||||
boolean again = true;
|
||||
|
||||
while (again) {
|
||||
again = false;
|
||||
for (CaveResult i : caves) {
|
||||
if (i.getCeiling() > m && i.getFloor() < m) {
|
||||
m = i.getFloor();
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}, Interpolated.INT).cache2D(cacheSize);
|
||||
//@done
|
||||
}
|
||||
|
||||
private IrisRegion findRegion(IrisBiome focus, Engine engine) {
|
||||
for (IrisRegion i : engine.getDimension().getAllRegions(engine)) {
|
||||
if (i.getAllBiomeIds().contains(focus.getLoadKey())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IrisDecorator decorateFor(IrisBiome b, double x, double z, DecorationPart part) {
|
||||
RNG rngc = chunkRngStream.get(x, z);
|
||||
|
||||
for (IrisDecorator i : b.getDecorators()) {
|
||||
if (!i.getPartOf().equals(part)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockData block = i.getBlockData(b, rngc, x, z, data);
|
||||
|
||||
if (block != null) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private IrisBiome implode(IrisBiome b, Double x, Double z) {
|
||||
if (b.getChildren().isEmpty()) {
|
||||
return b;
|
||||
}
|
||||
|
||||
return implode(b, x, z, 3);
|
||||
}
|
||||
|
||||
private IrisBiome implode(IrisBiome b, Double x, Double z, int max) {
|
||||
if (max < 0) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (b.getChildren().isEmpty()) {
|
||||
return b;
|
||||
}
|
||||
|
||||
CNG childCell = b.getChildrenGenerator(rng, 123, b.getChildShrinkFactor());
|
||||
KList<IrisBiome> chx = b.getRealChildren(this).copy();
|
||||
chx.add(b);
|
||||
IrisBiome biome = childCell.fitRarity(chx, x, z);
|
||||
biome.setInferredType(b.getInferredType());
|
||||
return implode(biome, x, z, max - 1);
|
||||
}
|
||||
|
||||
private IrisBiome fixBiomeType(Double height, IrisBiome biome, IrisRegion region, Double x, Double z, double fluidHeight) {
|
||||
double sh = region.getShoreHeight(x, z);
|
||||
|
||||
if (height >= fluidHeight - 1 && height <= fluidHeight + sh && !biome.isShore()) {
|
||||
return shoreBiomeStream.get(x, z);
|
||||
}
|
||||
|
||||
if (height > fluidHeight + sh && !biome.isLand()) {
|
||||
return landBiomeStream.get(x, z);
|
||||
}
|
||||
|
||||
if (height < fluidHeight && !biome.isAquatic()) {
|
||||
return seaBiomeStream.get(x, z);
|
||||
}
|
||||
|
||||
if (height == fluidHeight && !biome.isShore()) {
|
||||
return shoreBiomeStream.get(x, z);
|
||||
}
|
||||
|
||||
return biome;
|
||||
}
|
||||
|
||||
private double getHeight(Engine engine, IrisBiome b, double x, double z, long seed) {
|
||||
double h = 0;
|
||||
|
||||
for (IrisGenerator gen : generators) {
|
||||
double hi = gen.getInterpolator().interpolate(x, z, (xx, zz) ->
|
||||
{
|
||||
try {
|
||||
IrisBiome bx = baseBiomeStream.get(xx, zz);
|
||||
|
||||
return bx.getGenLinkMax(gen.getLoadKey());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.warn("Failed to sample hi biome at " + xx + " " + zz + " using the generator " + gen.getLoadKey());
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
double lo = gen.getInterpolator().interpolate(x, z, (xx, zz) ->
|
||||
{
|
||||
try {
|
||||
IrisBiome bx = baseBiomeStream.get(xx, zz);
|
||||
|
||||
return bx.getGenLinkMin(gen.getLoadKey());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.warn("Failed to sample lo biome at " + xx + " " + zz + " using the generator " + gen.getLoadKey());
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
h += M.lerp(lo, hi, gen.getHeight(x, z, seed + 239945));
|
||||
}
|
||||
|
||||
AtomicDouble noise = new AtomicDouble(h + fluidHeight + overlayStream.get(x, z));
|
||||
engine.getFramework().getEngineParallax().forEachFeature(x, z, (i)
|
||||
-> noise.set(i.filter(x, z, noise.get())));
|
||||
return Math.min(engine.getHeight(), Math.max(noise.get(), 0));
|
||||
}
|
||||
|
||||
private void registerGenerator(IrisGenerator cachedGenerator) {
|
||||
for (IrisGenerator i : generators) {
|
||||
if (i.getLoadKey().equals(cachedGenerator.getLoadKey())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
generators.add(cachedGenerator);
|
||||
}
|
||||
}
|
||||
215
src/main/java/com/volmit/iris/engine/IrisEngine.java
Normal file
215
src/main/java/com/volmit/iris/engine/IrisEngine.java
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.engine.object.*;
|
||||
import com.volmit.iris.engine.framework.*;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.util.J;
|
||||
import com.volmit.iris.util.PrecisionStopwatch;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class IrisEngine extends BlockPopulator implements Engine {
|
||||
@Getter
|
||||
private final EngineCompound compound;
|
||||
|
||||
@Getter
|
||||
private final EngineTarget target;
|
||||
|
||||
@Getter
|
||||
private final EngineFramework framework;
|
||||
|
||||
@Getter
|
||||
private final EngineEffects effects;
|
||||
|
||||
@Getter
|
||||
private final EngineWorldManager worldManager;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private volatile int parallelism;
|
||||
|
||||
@Getter
|
||||
private final int index;
|
||||
|
||||
@Getter
|
||||
private final EngineMetrics metrics;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private volatile int minHeight;
|
||||
private boolean failing;
|
||||
private boolean closed;
|
||||
private int cacheId;
|
||||
private final int art;
|
||||
|
||||
@Getter
|
||||
private double maxBiomeObjectDensity;
|
||||
|
||||
@Getter
|
||||
private double maxBiomeLayerDensity;
|
||||
|
||||
@Getter
|
||||
private double maxBiomeDecoratorDensity;
|
||||
|
||||
public IrisEngine(EngineTarget target, EngineCompound compound, int index) {
|
||||
Iris.info("Initializing Engine: " + target.getWorld().getName() + "/" + target.getDimension().getLoadKey() + " (" + target.getHeight() + " height)");
|
||||
metrics = new EngineMetrics(32);
|
||||
this.target = target;
|
||||
this.framework = new IrisEngineFramework(this);
|
||||
worldManager = new IrisWorldManager(this);
|
||||
this.compound = compound;
|
||||
minHeight = 0;
|
||||
failing = false;
|
||||
closed = false;
|
||||
this.index = index;
|
||||
cacheId = RNG.r.nextInt();
|
||||
effects = new IrisEngineEffects(this);
|
||||
art = J.ar(effects::tickRandomPlayer, 0);
|
||||
J.a(this::computeBiomeMaxes);
|
||||
}
|
||||
|
||||
private void computeBiomeMaxes() {
|
||||
for(IrisBiome i : getDimension().getAllBiomes(this))
|
||||
{
|
||||
double density = 0;
|
||||
|
||||
for(IrisObjectPlacement j : i.getObjects())
|
||||
{
|
||||
density += j.getDensity() * j.getChance();
|
||||
}
|
||||
|
||||
maxBiomeObjectDensity = Math.max(maxBiomeObjectDensity, density);
|
||||
density = 0;
|
||||
|
||||
for(IrisDecorator j : i.getDecorators())
|
||||
{
|
||||
density += Math.max(j.getStackMax(), 1) * j.getChance();
|
||||
}
|
||||
|
||||
maxBiomeDecoratorDensity = Math.max(maxBiomeDecoratorDensity, density);
|
||||
density = 0;
|
||||
|
||||
for(IrisBiomePaletteLayer j : i.getLayers())
|
||||
{
|
||||
density++;
|
||||
}
|
||||
|
||||
maxBiomeLayerDensity = Math.max(maxBiomeLayerDensity, density);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
J.car(art);
|
||||
closed = true;
|
||||
getWorldManager().close();
|
||||
getFramework().close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle() {
|
||||
getFramework().recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double modifyX(double x) {
|
||||
return x / getDimension().getTerrainZoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double modifyZ(double z) {
|
||||
return z / getDimension().getTerrainZoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(int x, int z, Hunk<BlockData> vblocks, Hunk<Biome> vbiomes) {
|
||||
try {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
Hunk<BlockData> blocks = vblocks.synchronize().listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y + getMinHeight(), z + zz, t));
|
||||
getFramework().getEngineParallax().generateParallaxArea(x >> 4, z >> 4);
|
||||
getFramework().getBiomeActuator().actuate(x, z, vbiomes);
|
||||
getFramework().getTerrainActuator().actuate(x, z, blocks);
|
||||
getFramework().getCaveModifier().modify(x, z, blocks);
|
||||
getFramework().getRavineModifier().modify(x, z, blocks);
|
||||
getFramework().getPostModifier().modify(x, z, blocks);
|
||||
getFramework().getDecorantActuator().actuate(x, z, blocks);
|
||||
getFramework().getEngineParallax().insertParallax(x, z, blocks);
|
||||
getFramework().getDepositModifier().modify(x, z, blocks);
|
||||
getMetrics().getTotal().put(p.getMilliseconds());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
fail("Failed to generate " + x + ", " + z, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IrisBiome getFocus() {
|
||||
if (getDimension().getFocus() == null || getDimension().getFocus().trim().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getData().getBiomeLoader().load(getDimension().getFocus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk c) {
|
||||
getWorldManager().spawnInitialEntities(c);
|
||||
updateChunk(c);
|
||||
placeTiles(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fail(String error, Throwable e) {
|
||||
failing = true;
|
||||
Iris.error(error);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFailed() {
|
||||
return failing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCacheID() {
|
||||
return cacheId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hotload() {
|
||||
cacheId = RNG.r.nextInt();
|
||||
}
|
||||
}
|
||||
283
src/main/java/com/volmit/iris/engine/IrisEngineCompound.java
Normal file
283
src/main/java/com/volmit/iris/engine/IrisEngineCompound.java
Normal file
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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.IrisDataManager;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.engine.object.IrisDimensionIndex;
|
||||
import com.volmit.iris.engine.object.IrisPosition;
|
||||
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.hunk.Hunk;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.*;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.World;
|
||||
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 World 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(World world, IrisDimension rootDimension, IrisDataManager data, int maximumThreads) {
|
||||
wallClock = new AtomicRollingSequence(32);
|
||||
this.rootDimension = rootDimension;
|
||||
Iris.info("Initializing Engine Composite for " + world.getName());
|
||||
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(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.getWorldFolder(), "iris/engine-metadata.json");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(int x, int z, Hunk<BlockData> blocks, Hunk<BlockData> postblocks, Hunk<Biome> biomes) {
|
||||
recycle();
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
if (engines.length == 1 && !getEngine(0).getTarget().isInverted()) {
|
||||
engines[0].generate(x, z, blocks, biomes);
|
||||
} 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);
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IrisDataManager getData(int height) {
|
||||
return getEngineForHeight(height).getData();
|
||||
}
|
||||
|
||||
//TODO: FAIL
|
||||
@Override
|
||||
public boolean isFailing() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Engine getDefaultEngine() {
|
||||
return defaultEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWorld(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hotload() {
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
getEngine(i).hotload();
|
||||
}
|
||||
}
|
||||
}
|
||||
95
src/main/java/com/volmit/iris/engine/IrisEngineEffects.java
Normal file
95
src/main/java/com/volmit/iris/engine/IrisEngineEffects.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedComponent;
|
||||
import com.volmit.iris.engine.framework.EngineEffects;
|
||||
import com.volmit.iris.engine.framework.EnginePlayer;
|
||||
import com.volmit.iris.util.KMap;
|
||||
import com.volmit.iris.util.M;
|
||||
import com.volmit.iris.util.PrecisionStopwatch;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
public class IrisEngineEffects extends EngineAssignedComponent implements EngineEffects {
|
||||
private final KMap<UUID, EnginePlayer> players;
|
||||
private final Semaphore limit;
|
||||
|
||||
public IrisEngineEffects(Engine engine) {
|
||||
super(engine, "FX");
|
||||
players = new KMap<>();
|
||||
limit = new Semaphore(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePlayerMap() {
|
||||
List<Player> pr = getEngine().getWorld().getPlayers();
|
||||
|
||||
if (pr == null) return; //Fix for paper returning a world with a null playerlist
|
||||
|
||||
for (Player i : pr) {
|
||||
Location l = i.getLocation();
|
||||
boolean pcc = players.containsKey(i.getUniqueId());
|
||||
if (getEngine().contains(l)) {
|
||||
if (!pcc) {
|
||||
players.put(i.getUniqueId(), new EnginePlayer(getEngine(), i));
|
||||
}
|
||||
} else if (pcc) {
|
||||
players.remove(i.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID i : players.k()) {
|
||||
if (!pr.contains(players.get(i).getPlayer())) {
|
||||
players.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tickRandomPlayer() {
|
||||
if (limit.tryAcquire()) {
|
||||
if (M.r(0.02)) {
|
||||
updatePlayerMap();
|
||||
limit.release();
|
||||
return;
|
||||
}
|
||||
|
||||
if (players.isEmpty()) {
|
||||
limit.release();
|
||||
return;
|
||||
}
|
||||
|
||||
double limitms = 1.5;
|
||||
int max = players.size();
|
||||
PrecisionStopwatch p = new PrecisionStopwatch();
|
||||
|
||||
while (max-- > 0 && M.ms() - p.getMilliseconds() < limitms) {
|
||||
players.v().getRandom().tick();
|
||||
}
|
||||
|
||||
limit.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
124
src/main/java/com/volmit/iris/engine/IrisEngineFramework.java
Normal file
124
src/main/java/com/volmit/iris/engine/IrisEngineFramework.java
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.engine.actuator.IrisBiomeActuator;
|
||||
import com.volmit.iris.engine.actuator.IrisDecorantActuator;
|
||||
import com.volmit.iris.engine.actuator.IrisTerrainActuator;
|
||||
import com.volmit.iris.engine.modifier.IrisCaveModifier;
|
||||
import com.volmit.iris.engine.modifier.IrisDepositModifier;
|
||||
import com.volmit.iris.engine.modifier.IrisPostModifier;
|
||||
import com.volmit.iris.engine.modifier.IrisRavineModifier;
|
||||
import com.volmit.iris.engine.framework.*;
|
||||
import com.volmit.iris.util.ChronoLatch;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class IrisEngineFramework implements EngineFramework {
|
||||
|
||||
@Getter
|
||||
private final Engine engine;
|
||||
|
||||
@Getter
|
||||
private final IrisComplex complex;
|
||||
|
||||
@Getter
|
||||
final EngineParallaxManager engineParallax;
|
||||
|
||||
@Getter
|
||||
private final EngineActuator<BlockData> terrainActuator;
|
||||
|
||||
@Getter
|
||||
private final EngineActuator<BlockData> decorantActuator;
|
||||
|
||||
@Getter
|
||||
private final EngineActuator<Biome> biomeActuator;
|
||||
|
||||
@Getter
|
||||
private final EngineModifier<BlockData> depositModifier;
|
||||
|
||||
@Getter
|
||||
private final EngineModifier<BlockData> caveModifier;
|
||||
|
||||
@Getter
|
||||
private final EngineModifier<BlockData> ravineModifier;
|
||||
|
||||
@Getter
|
||||
private final EngineModifier<BlockData> postModifier;
|
||||
|
||||
private final AtomicBoolean cleaning;
|
||||
private final ChronoLatch cleanLatch;
|
||||
|
||||
public IrisEngineFramework(Engine engine) {
|
||||
this.engine = engine;
|
||||
this.complex = new IrisComplex(getEngine());
|
||||
this.engineParallax = new IrisEngineParallax(getEngine());
|
||||
this.terrainActuator = new IrisTerrainActuator(getEngine());
|
||||
this.decorantActuator = new IrisDecorantActuator(getEngine());
|
||||
this.biomeActuator = new IrisBiomeActuator(getEngine());
|
||||
this.depositModifier = new IrisDepositModifier(getEngine());
|
||||
this.ravineModifier = new IrisRavineModifier(getEngine());
|
||||
this.caveModifier = new IrisCaveModifier(engine);
|
||||
this.postModifier = new IrisPostModifier(engine);
|
||||
cleaning = new AtomicBoolean(false);
|
||||
cleanLatch = new ChronoLatch(Math.max(10000, Math.min(IrisSettings.get().getParallax().getParallaxChunkEvictionMS(), IrisSettings.get().getParallax().getParallaxRegionEvictionMS())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void recycle() {
|
||||
if (!cleanLatch.flip()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (cleaning.get()) {
|
||||
cleanLatch.flipDown();
|
||||
return;
|
||||
}
|
||||
|
||||
cleaning.set(true);
|
||||
|
||||
try {
|
||||
getEngine().getParallax().cleanup();
|
||||
getData().getObjectLoader().clean();
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.error("Cleanup failed!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
cleaning.lazySet(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
getEngineParallax().close();
|
||||
getTerrainActuator().close();
|
||||
getDecorantActuator().close();
|
||||
getBiomeActuator().close();
|
||||
getDepositModifier().close();
|
||||
getRavineModifier().close();
|
||||
getCaveModifier().close();
|
||||
getPostModifier().close();
|
||||
}
|
||||
}
|
||||
36
src/main/java/com/volmit/iris/engine/IrisEngineParallax.java
Normal file
36
src/main/java/com/volmit/iris/engine/IrisEngineParallax.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineParallaxManager;
|
||||
import lombok.Getter;
|
||||
|
||||
public class IrisEngineParallax implements EngineParallaxManager {
|
||||
@Getter
|
||||
private final Engine engine;
|
||||
|
||||
@Getter
|
||||
private final int parallaxSize;
|
||||
|
||||
public IrisEngineParallax(Engine engine) {
|
||||
this.engine = engine;
|
||||
parallaxSize = computeParallaxSize();
|
||||
}
|
||||
}
|
||||
97
src/main/java/com/volmit/iris/engine/IrisWorldCreator.java
Normal file
97
src/main/java/com/volmit/iris/engine/IrisWorldCreator.java
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.core.IrisDataManager;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.engine.framework.EngineCompositeGenerator;
|
||||
import com.volmit.iris.util.FakeWorld;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.WorldCreator;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class IrisWorldCreator {
|
||||
private String name;
|
||||
private boolean studio = false;
|
||||
private String dimensionName = null;
|
||||
private long seed = 1337;
|
||||
private int maxHeight = 256;
|
||||
private int minHeight = 0;
|
||||
|
||||
public IrisWorldCreator() {
|
||||
|
||||
}
|
||||
|
||||
public IrisWorldCreator dimension(String loadKey) {
|
||||
this.dimensionName = loadKey;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisWorldCreator height(int maxHeight) {
|
||||
this.maxHeight = maxHeight;
|
||||
this.minHeight = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisWorldCreator height(int minHeight, int maxHeight) {
|
||||
this.maxHeight = maxHeight;
|
||||
this.minHeight = minHeight;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisWorldCreator name(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisWorldCreator seed(long seed) {
|
||||
this.seed = seed;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisWorldCreator studioMode() {
|
||||
this.studio = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IrisWorldCreator productionMode() {
|
||||
this.studio = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WorldCreator create() {
|
||||
EngineCompositeGenerator g = new EngineCompositeGenerator(dimensionName, !studio);
|
||||
g.initialize(new FakeWorld(name, minHeight, maxHeight, seed, new File(name), findEnvironment()));
|
||||
|
||||
return new WorldCreator(name)
|
||||
.environment(findEnvironment())
|
||||
.generateStructures(true)
|
||||
.generator(g).seed(seed);
|
||||
}
|
||||
|
||||
private World.Environment findEnvironment() {
|
||||
IrisDimension dim = IrisDataManager.loadAnyDimension(dimensionName);
|
||||
if (dim == null || dim.getEnvironment() == null) {
|
||||
return World.Environment.NORMAL;
|
||||
} else {
|
||||
return dim.getEnvironment();
|
||||
}
|
||||
}
|
||||
}
|
||||
167
src/main/java/com/volmit/iris/engine/IrisWorldManager.java
Normal file
167
src/main/java/com/volmit/iris/engine/IrisWorldManager.java
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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.engine.object.*;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedWorldManager;
|
||||
import com.volmit.iris.util.J;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
|
||||
public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
private boolean spawnable;
|
||||
|
||||
public IrisWorldManager(Engine engine) {
|
||||
super(engine);
|
||||
spawnable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSave() {
|
||||
getEngine().getParallax().saveAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnInitialEntities(Chunk c) {
|
||||
RNG rng = new RNG(Cache.key(c));
|
||||
|
||||
getEngine().getParallaxAccess().getEntitiesR(c.getX(), c.getZ()).iterateSync((x, y, z, e) -> {
|
||||
if (e != null) {
|
||||
IrisEntity en = getData().getEntityLoader().load(e);
|
||||
|
||||
if (en != null) {
|
||||
en.spawn(getEngine(), new Location(c.getWorld(), x + (c.getX() << 4), y, z + (c.getZ() << 4)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
int x = (c.getX() * 16) + rng.nextInt(15);
|
||||
int z = (c.getZ() * 16) + rng.nextInt(15);
|
||||
int y = getEngine().getHeight(x, z) + 1;
|
||||
IrisDimension dim = getDimension();
|
||||
IrisRegion region = getEngine().getRegion(x, z);
|
||||
IrisBiome above = getEngine().getSurfaceBiome(x, z);
|
||||
trySpawn(above.getEntityInitialSpawns(), c, rng);
|
||||
trySpawn(region.getEntityInitialSpawns(), c, rng);
|
||||
trySpawn(dim.getEntityInitialSpawns(), c, rng);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntitySpawn(EntitySpawnEvent e) {
|
||||
if (getTarget().getWorld() == null || !getTarget().getWorld().equals(e.getEntity().getWorld())) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!IrisSettings.get().getGenerator().isSystemEntitySpawnOverrides()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x = e.getEntity().getLocation().getBlockX();
|
||||
int y = e.getEntity().getLocation().getBlockY();
|
||||
int z = e.getEntity().getLocation().getBlockZ();
|
||||
|
||||
J.a(() ->
|
||||
{
|
||||
if (spawnable) {
|
||||
IrisDimension dim = getDimension();
|
||||
IrisRegion region = getEngine().getRegion(x, z);
|
||||
IrisBiome above = getEngine().getSurfaceBiome(x, z);
|
||||
IrisBiome bbelow = getEngine().getBiome(x, y, z);
|
||||
if (above.getLoadKey().equals(bbelow.getLoadKey())) {
|
||||
bbelow = null;
|
||||
}
|
||||
|
||||
IrisBiome below = bbelow;
|
||||
|
||||
J.s(() ->
|
||||
{
|
||||
if (below != null) {
|
||||
if (trySpawn(below.getEntitySpawnOverrides(), e)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (trySpawn(above.getEntitySpawnOverrides(), e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (trySpawn(region.getEntitySpawnOverrides(), e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (trySpawn(dim.getEntitySpawnOverrides(), e)) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (Throwable ee) {
|
||||
Iris.reportError(ee);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean trySpawn(KList<IrisEntitySpawnOverride> s, EntitySpawnEvent e) {
|
||||
for (IrisEntitySpawnOverride i : s) {
|
||||
spawnable = false;
|
||||
|
||||
if (i.on(getEngine(), e.getLocation(), e.getEntityType(), e) != null) {
|
||||
e.setCancelled(true);
|
||||
e.getEntity().remove();
|
||||
return true;
|
||||
} else {
|
||||
spawnable = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void trySpawn(KList<IrisEntityInitialSpawn> s, Chunk c, RNG rng) {
|
||||
for (IrisEntityInitialSpawn i : s) {
|
||||
i.spawn(getEngine(), c, rng);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockBreak(BlockBreakEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockPlace(BlockPlaceEvent e) {
|
||||
|
||||
}
|
||||
}
|
||||
76
src/main/java/com/volmit/iris/engine/IrisWorlds.java
Normal file
76
src/main/java/com/volmit/iris/engine/IrisWorlds.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.engine.framework.IrisAccess;
|
||||
import com.volmit.iris.engine.framework.IrisAccessProvider;
|
||||
import com.volmit.iris.util.KMap;
|
||||
import com.volmit.iris.util.MortarSender;
|
||||
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 MortarSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world.");
|
||||
j.teleport(i.getSpawnLocation());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.generator.actuator;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.nms.INMS;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisBiomeCustom;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedActuator;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.engine.hunk.view.BiomeGridHunkView;
|
||||
import com.volmit.iris.util.PrecisionStopwatch;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import com.volmit.iris.util.TerrainChunk;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
|
||||
public class IrisBiomeActuator extends EngineAssignedActuator<Biome> {
|
||||
private final RNG rng;
|
||||
|
||||
public IrisBiomeActuator(Engine engine) {
|
||||
super(engine, "Biome");
|
||||
rng = new RNG(engine.getWorld().getSeed() + 243995);
|
||||
}
|
||||
|
||||
private boolean injectBiome(Hunk<Biome> h, int x, int y, int z, Object bb) {
|
||||
try {
|
||||
if (h instanceof BiomeGridHunkView hh) {
|
||||
ChunkGenerator.BiomeGrid g = hh.getChunk();
|
||||
if (g instanceof TerrainChunk) {
|
||||
((TerrainChunk) g).getBiomeBaseInjector().setBiome(x, y, z, bb);
|
||||
} else {
|
||||
hh.forceBiomeBaseInto(x, y, z, bb);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActuate(int x, int z, Hunk<Biome> h) {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
int zf;
|
||||
|
||||
for (int xf = 0; xf < h.getWidth(); xf++) {
|
||||
for (zf = 0; zf < h.getDepth(); zf++) {
|
||||
|
||||
IrisBiome ib = getComplex().getTrueBiomeStream().get(modX(xf + x), modZ(zf + z));
|
||||
|
||||
if (ib.isCustom()) {
|
||||
try {
|
||||
IrisBiomeCustom custom = ib.getCustomBiome(rng, x, 0, z);
|
||||
Object biomeBase = INMS.get().getCustomBiomeBaseFor(getDimension().getLoadKey() + ":" + custom.getId());
|
||||
|
||||
if (!injectBiome(h, x, 0, z, biomeBase)) {
|
||||
throw new RuntimeException("Cant inject biome!");
|
||||
}
|
||||
|
||||
for (int i = 0; i < h.getHeight(); i++) {
|
||||
injectBiome(h, xf, i, zf, biomeBase);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Biome v = ib.getSkyBiome(rng, x, 0, z);
|
||||
for (int i = 0; i < h.getHeight(); i++) {
|
||||
h.set(xf, i, zf, v);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Biome v = ib.getSkyBiome(rng, x, 0, z);
|
||||
for (int i = 0; i < h.getHeight(); i++) {
|
||||
h.set(xf, i, zf, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getEngine().getMetrics().getBiome().put(p.getMilliseconds());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.generator.actuator;
|
||||
|
||||
import com.volmit.iris.generator.decorator.*;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedActuator;
|
||||
import com.volmit.iris.engine.framework.EngineDecorator;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.util.PrecisionStopwatch;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class IrisDecorantActuator extends EngineAssignedActuator<BlockData> {
|
||||
private static final Predicate<BlockData> PREDICATE_SOLID = (b) -> b != null && !b.getMaterial().isAir() && !b.getMaterial().equals(Material.WATER) && !b.getMaterial().equals(Material.LAVA);
|
||||
private final RNG rng;
|
||||
@Getter
|
||||
private final EngineDecorator surfaceDecorator;
|
||||
@Getter
|
||||
private final EngineDecorator ceilingDecorator;
|
||||
@Getter
|
||||
private final EngineDecorator seaSurfaceDecorator;
|
||||
@Getter
|
||||
private final EngineDecorator seaFloorDecorator;
|
||||
@Getter
|
||||
private final EngineDecorator shoreLineDecorator;
|
||||
private final boolean shouldRay;
|
||||
|
||||
public IrisDecorantActuator(Engine engine) {
|
||||
super(engine, "Decorant");
|
||||
shouldRay = shouldRayDecorate();
|
||||
this.rng = new RNG(engine.getTarget().getWorld().getSeed());
|
||||
surfaceDecorator = new IrisSurfaceDecorator(getEngine());
|
||||
ceilingDecorator = new IrisCeilingDecorator(getEngine());
|
||||
seaSurfaceDecorator = new IrisSeaSurfaceDecorator(getEngine());
|
||||
shoreLineDecorator = new IrisShoreLineDecorator(getEngine());
|
||||
seaFloorDecorator = new IrisSeaFloorDecorator(getEngine());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActuate(int x, int z, Hunk<BlockData> output) {
|
||||
if (!getEngine().getDimension().isDecorate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
|
||||
int j, realX, realZ, height;
|
||||
IrisBiome biome, cave;
|
||||
|
||||
for (int i = 0; i < output.getWidth(); i++) {
|
||||
for (j = 0; j < output.getDepth(); j++) {
|
||||
boolean solid;
|
||||
int emptyFor = 0;
|
||||
int lastSolid = 0;
|
||||
realX = (int) Math.round(modX(x + i));
|
||||
realZ = (int) Math.round(modZ(z + j));
|
||||
height = (int) Math.round(getComplex().getHeightStream().get(realX, realZ));
|
||||
biome = getComplex().getTrueBiomeStream().get(realX, realZ);
|
||||
cave = shouldRay ? getComplex().getCaveBiomeStream().get(realX, realZ) : null;
|
||||
|
||||
if (biome.getDecorators().isEmpty() && (cave == null || cave.getDecorators().isEmpty())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (height == getDimension().getFluidHeight()) {
|
||||
getShoreLineDecorator().decorate(i, j,
|
||||
realX, (int) Math.round(modX(x + i + 1)), (int) Math.round(modX(x + i - 1)),
|
||||
realZ, (int) Math.round(modZ(z + j + 1)), (int) Math.round(modZ(z + j - 1)),
|
||||
output, biome, height, getEngine().getHeight() - height);
|
||||
} else if (height == getDimension().getFluidHeight() + 1) {
|
||||
getSeaSurfaceDecorator().decorate(i, j,
|
||||
realX, (int) Math.round(modX(x + i + 1)), (int) Math.round(modX(x + i - 1)),
|
||||
realZ, (int) Math.round(modZ(z + j + 1)), (int) Math.round(modZ(z + j - 1)),
|
||||
output, biome, height, getEngine().getHeight() - getDimension().getFluidHeight());
|
||||
} else if (height < getDimension().getFluidHeight()) {
|
||||
getSeaFloorDecorator().decorate(i, j, realX, realZ, output, biome, height + 1, getDimension().getFluidHeight());
|
||||
}
|
||||
|
||||
getSurfaceDecorator().decorate(i, j, realX, realZ, output, biome, height, getEngine().getHeight() - height);
|
||||
|
||||
if (cave != null && cave.getDecorators().isNotEmpty()) {
|
||||
for (int k = height; k > 0; k--) {
|
||||
solid = PREDICATE_SOLID.test(output.get(i, k, j));
|
||||
|
||||
if (solid) {
|
||||
if (emptyFor > 0) {
|
||||
getSurfaceDecorator().decorate(i, j, realX, realZ, output, cave, k, emptyFor);
|
||||
getCeilingDecorator().decorate(i, j, realX, realZ, output, cave, lastSolid - 1, emptyFor);
|
||||
emptyFor = 0;
|
||||
}
|
||||
lastSolid = k;
|
||||
} else {
|
||||
emptyFor++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getEngine().getMetrics().getDecoration().put(p.getMilliseconds());
|
||||
}
|
||||
|
||||
private boolean shouldRayDecorate() {
|
||||
return getEngine().getDimension().isCarving() || getEngine().getDimension().isCaves() || getEngine().getDimension().isRavines();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.generator.actuator;
|
||||
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedActuator;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.PrecisionStopwatch;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisTerrainActuator extends EngineAssignedActuator<BlockData> {
|
||||
private static final BlockData AIR = Material.AIR.createBlockData();
|
||||
private static final BlockData BEDROCK = Material.BEDROCK.createBlockData();
|
||||
private static final BlockData CAVE_AIR = Material.CAVE_AIR.createBlockData();
|
||||
@Getter
|
||||
private final RNG rng;
|
||||
private final boolean carving;
|
||||
private final boolean hasUnder;
|
||||
@Getter
|
||||
private int lastBedrock = -1;
|
||||
|
||||
public IrisTerrainActuator(Engine engine) {
|
||||
super(engine, "Terrain");
|
||||
rng = new RNG(engine.getWorld().getSeed());
|
||||
carving = getDimension().isCarving() && getDimension().getCarveLayers().isNotEmpty();
|
||||
hasUnder = getDimension().getUndercarriage() != null && !getDimension().getUndercarriage().getGenerator().isFlat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActuate(int x, int z, Hunk<BlockData> h) {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
int i, zf, depth, realX, realZ, hf, he, b, fdepth;
|
||||
IrisBiome biome;
|
||||
KList<BlockData> blocks, fblocks;
|
||||
|
||||
for (int xf = 0; xf < h.getWidth(); xf++) {
|
||||
for (zf = 0; zf < h.getDepth(); zf++) {
|
||||
realX = (int) modX(xf + x);
|
||||
realZ = (int) modZ(zf + z);
|
||||
b = hasUnder ? (int) Math.round(getDimension().getUndercarriage().get(rng, realX, realZ)) : 0;
|
||||
he = (int) Math.round(Math.min(h.getHeight(), getComplex().getHeightStream().get(realX, realZ)));
|
||||
hf = Math.round(Math.max(Math.min(h.getHeight(), getDimension().getFluidHeight()), he));
|
||||
biome = getComplex().getTrueBiomeStream().get(realX, realZ);
|
||||
blocks = null;
|
||||
fblocks = null;
|
||||
|
||||
if (hf < b) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = hf; i >= b; i--) {
|
||||
if (i >= h.getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == b) {
|
||||
if (getDimension().isBedrock()) {
|
||||
h.set(xf, i, zf, BEDROCK);
|
||||
lastBedrock = i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (carving && getDimension().isCarved(realX, i, realZ, rng, he)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > he && i <= hf) {
|
||||
fdepth = hf - i;
|
||||
|
||||
if (fblocks == null) {
|
||||
fblocks = biome.generateSeaLayers(realX, realZ, rng, hf - he, getData());
|
||||
}
|
||||
|
||||
if (fblocks.hasIndex(fdepth)) {
|
||||
h.set(xf, i, zf, fblocks.get(fdepth));
|
||||
continue;
|
||||
}
|
||||
|
||||
h.set(xf, i, zf, getComplex().getFluidStream().get(realX, +realZ));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i <= he) {
|
||||
depth = he - i;
|
||||
if (blocks == null) {
|
||||
blocks = biome.generateLayers(realX, realZ, rng, he, he, getData(), getComplex());
|
||||
}
|
||||
|
||||
if (blocks.hasIndex(depth)) {
|
||||
h.set(xf, i, zf, blocks.get(depth));
|
||||
continue;
|
||||
}
|
||||
|
||||
h.set(xf, i, zf, getComplex().getRockStream().get(realX, realZ));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getEngine().getMetrics().getTerrain().put(p.getMilliseconds());
|
||||
}
|
||||
}
|
||||
124
src/main/java/com/volmit/iris/engine/cache/AtomicCache.java
vendored
Normal file
124
src/main/java/com/volmit/iris/engine/cache/AtomicCache.java
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import com.volmit.iris.util.IrisLock;
|
||||
import com.volmit.iris.util.M;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class AtomicCache<T> {
|
||||
private transient volatile T t;
|
||||
private transient volatile long a;
|
||||
private transient volatile int validations;
|
||||
private final IrisLock check;
|
||||
private final IrisLock time;
|
||||
private final IrisLock write;
|
||||
private final boolean nullSupport;
|
||||
|
||||
public AtomicCache() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
public AtomicCache(boolean nullSupport) {
|
||||
this.nullSupport = nullSupport;
|
||||
check = new IrisLock("Check");
|
||||
write = new IrisLock("Write");
|
||||
time = new IrisLock("Time");
|
||||
validations = 0;
|
||||
a = -1;
|
||||
t = null;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
check.lock();
|
||||
write.lock();
|
||||
time.lock();
|
||||
a = -1;
|
||||
t = null;
|
||||
time.unlock();
|
||||
write.unlock();
|
||||
check.unlock();
|
||||
}
|
||||
|
||||
public T aquire(Supplier<T> t) {
|
||||
if (nullSupport) {
|
||||
return aquireNull(t);
|
||||
}
|
||||
|
||||
if (this.t != null && validations > 1000) {
|
||||
return this.t;
|
||||
}
|
||||
|
||||
if (this.t != null && M.ms() - a > 1000) {
|
||||
if (this.t != null) {
|
||||
//noinspection NonAtomicOperationOnVolatileField
|
||||
validations++;
|
||||
}
|
||||
|
||||
return this.t;
|
||||
}
|
||||
|
||||
check.lock();
|
||||
|
||||
if (this.t == null) {
|
||||
write.lock();
|
||||
this.t = t.get();
|
||||
|
||||
time.lock();
|
||||
|
||||
if (a == -1) {
|
||||
a = M.ms();
|
||||
}
|
||||
|
||||
time.unlock();
|
||||
write.unlock();
|
||||
}
|
||||
|
||||
check.unlock();
|
||||
return this.t;
|
||||
}
|
||||
|
||||
public T aquireNull(Supplier<T> t) {
|
||||
if (validations > 1000) {
|
||||
return this.t;
|
||||
}
|
||||
|
||||
if (M.ms() - a > 1000) {
|
||||
//noinspection NonAtomicOperationOnVolatileField
|
||||
validations++;
|
||||
return this.t;
|
||||
}
|
||||
|
||||
check.lock();
|
||||
write.lock();
|
||||
this.t = t.get();
|
||||
|
||||
time.lock();
|
||||
|
||||
if (a == -1) {
|
||||
a = M.ms();
|
||||
}
|
||||
|
||||
time.unlock();
|
||||
write.unlock();
|
||||
check.unlock();
|
||||
return this.t;
|
||||
}
|
||||
}
|
||||
43
src/main/java/com/volmit/iris/engine/cache/Cache.java
vendored
Normal file
43
src/main/java/com/volmit/iris/engine/cache/Cache.java
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import org.bukkit.Chunk;
|
||||
|
||||
public interface Cache<V> {
|
||||
static long key(Chunk chunk) {
|
||||
return key(chunk.getX(), chunk.getZ());
|
||||
}
|
||||
|
||||
int getId();
|
||||
|
||||
V get(int x, int z);
|
||||
|
||||
static long key(int x, int z) {
|
||||
return (((long) x) << 32) | (z & 0xffffffffL);
|
||||
}
|
||||
|
||||
static int keyX(long key) {
|
||||
return (int) (key >> 32);
|
||||
}
|
||||
|
||||
static int keyZ(long key) {
|
||||
return (int) key;
|
||||
}
|
||||
}
|
||||
26
src/main/java/com/volmit/iris/engine/cache/Multicache.java
vendored
Normal file
26
src/main/java/com/volmit/iris/engine/cache/Multicache.java
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
public interface Multicache {
|
||||
<V> Cache<V> getCache(int id);
|
||||
|
||||
<V> Cache<V> createCache();
|
||||
}
|
||||
|
||||
77
src/main/java/com/volmit/iris/engine/data/DataPalette.java
Normal file
77
src/main/java/com/volmit/iris/engine/data/DataPalette.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.data;
|
||||
|
||||
import com.volmit.iris.util.KList;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DataPalette<T> {
|
||||
private final KList<T> palette;
|
||||
|
||||
public DataPalette() {
|
||||
this(new KList<>(16));
|
||||
}
|
||||
|
||||
public DataPalette(KList<T> palette) {
|
||||
this.palette = palette;
|
||||
}
|
||||
|
||||
public KList<T> getPalette() {
|
||||
return palette;
|
||||
}
|
||||
|
||||
public int getIndex(T t) {
|
||||
int v = 0;
|
||||
|
||||
synchronized (palette) {
|
||||
v = palette.indexOf(t);
|
||||
|
||||
if (v == -1) {
|
||||
v = palette.size();
|
||||
palette.add(t);
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
public void write(IOAdapter<T> adapter, DataOutputStream dos) throws IOException {
|
||||
synchronized (palette) {
|
||||
dos.writeShort(getPalette().size() + Short.MIN_VALUE);
|
||||
|
||||
for (T t : palette) {
|
||||
adapter.write(t, dos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> DataPalette<T> getPalette(IOAdapter<T> adapter, DataInputStream din) throws IOException {
|
||||
KList<T> palette = new KList<>();
|
||||
int s = din.readShort() - Short.MIN_VALUE;
|
||||
|
||||
for (int i = 0; i < s; i++) {
|
||||
palette.add(adapter.read(din));
|
||||
}
|
||||
|
||||
return new DataPalette<>(palette);
|
||||
}
|
||||
}
|
||||
25
src/main/java/com/volmit/iris/engine/data/DataProvider.java
Normal file
25
src/main/java/com/volmit/iris/engine/data/DataProvider.java
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.data;
|
||||
|
||||
import com.volmit.iris.manager.IrisDataManager;
|
||||
|
||||
public interface DataProvider {
|
||||
IrisDataManager getData();
|
||||
}
|
||||
255
src/main/java/com/volmit/iris/engine/data/DirectWorldWriter.java
Normal file
255
src/main/java/com/volmit/iris/engine/data/DirectWorldWriter.java
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* 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.data;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.nms.INMS;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.data.mca.Chunk;
|
||||
import com.volmit.iris.engine.data.mca.MCAFile;
|
||||
import com.volmit.iris.engine.data.mca.MCAUtil;
|
||||
import com.volmit.iris.engine.data.mca.Section;
|
||||
import com.volmit.iris.engine.data.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.engine.data.nbt.tag.StringTag;
|
||||
import com.volmit.iris.engine.parallel.BurstExecutor;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.B;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.KMap;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("EmptyMethod")
|
||||
public class DirectWorldWriter {
|
||||
private final File worldFolder;
|
||||
private final Map<Long, MCAFile> writeBuffer;
|
||||
private static final Map<String, CompoundTag> blockDataCache = new KMap<>();
|
||||
private static final Map<Biome, Integer> biomeIds = computeBiomeIDs();
|
||||
|
||||
public DirectWorldWriter(File worldFolder) {
|
||||
this.worldFolder = worldFolder;
|
||||
writeBuffer = new KMap<>();
|
||||
new File(worldFolder, "iris/mca-region").mkdirs();
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
BurstExecutor ex2 = MultiBurst.burst.burst(writeBuffer.size());
|
||||
|
||||
for (Long i : new KList<>(writeBuffer.keySet())) {
|
||||
ex2.queue(() -> {
|
||||
int x = Cache.keyX(i);
|
||||
int z = Cache.keyZ(i);
|
||||
try {
|
||||
File f = getMCAFile(x, z);
|
||||
|
||||
if (!f.exists()) {
|
||||
f.getParentFile().mkdirs();
|
||||
f.createNewFile();
|
||||
}
|
||||
|
||||
MCAUtil.write(writeBuffer.get(i), f, true);
|
||||
writeBuffer.remove(i);
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ex2.complete();
|
||||
}
|
||||
|
||||
public void optimizeChunk(int x, int z) {
|
||||
getChunk(x, z).cleanupPalettesAndBlockStates();
|
||||
}
|
||||
|
||||
public File getMCAFile(int x, int z) {
|
||||
return new File(worldFolder, "iris/mca-region/r." + x + "." + z + ".mca");
|
||||
}
|
||||
|
||||
public static BlockData getBlockData(CompoundTag tag) {
|
||||
if (tag == null) {
|
||||
return B.getAir();
|
||||
}
|
||||
|
||||
StringBuilder p = new StringBuilder(tag.getString("Name"));
|
||||
|
||||
if (tag.containsKey("Properties")) {
|
||||
CompoundTag props = tag.getCompoundTag("Properties");
|
||||
p.append('[');
|
||||
|
||||
for (String i : props.keySet()) {
|
||||
p.append(i).append('=').append(props.getString(i)).append(',');
|
||||
}
|
||||
|
||||
p.deleteCharAt(p.length() - 1).append(']');
|
||||
}
|
||||
|
||||
BlockData b = B.getOrNull(p.toString());
|
||||
|
||||
if (b == null) {
|
||||
return B.getAir();
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
public static CompoundTag getCompound(BlockData blockData) {
|
||||
String data = blockData.getAsString(true);
|
||||
|
||||
if (blockDataCache.containsKey(data)) {
|
||||
return blockDataCache.get(data).clone();
|
||||
}
|
||||
|
||||
CompoundTag s = new CompoundTag();
|
||||
NamespacedKey key = blockData.getMaterial().getKey();
|
||||
s.putString("Name", key.getNamespace() + ":" + key.getKey());
|
||||
|
||||
|
||||
if (data.contains("[")) {
|
||||
String raw = data.split("\\Q[\\E")[1].replaceAll("\\Q]\\E", "");
|
||||
CompoundTag props = new CompoundTag();
|
||||
if (raw.contains(",")) {
|
||||
for (String i : raw.split("\\Q,\\E")) {
|
||||
String[] m = i.split("\\Q=\\E");
|
||||
String k = m[0];
|
||||
String v = m[1];
|
||||
props.put(k, new StringTag(v));
|
||||
}
|
||||
} else {
|
||||
String[] m = raw.split("\\Q=\\E");
|
||||
String k = m[0];
|
||||
String v = m[1];
|
||||
props.put(k, new StringTag(v));
|
||||
}
|
||||
s.put("Properties", props);
|
||||
}
|
||||
|
||||
blockDataCache.put(data, s.clone());
|
||||
return s;
|
||||
}
|
||||
|
||||
public BlockData getBlockData(int x, int y, int z) {
|
||||
try {
|
||||
CompoundTag tag = getChunkSection(x >> 4, y >> 4, z >> 4).getBlockStateAt(x & 15, y & 15, z & 15);
|
||||
|
||||
if (tag == null) {
|
||||
return B.get("AIR");
|
||||
}
|
||||
|
||||
return getBlockData(tag);
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
return B.get("AIR");
|
||||
}
|
||||
|
||||
public void setBlockData(int x, int y, int z, BlockData data) {
|
||||
getChunkSection(x >> 4, y >> 4, z >> 4).setBlockStateAt(x & 15, y & 15, z & 15, getCompound(data), false);
|
||||
}
|
||||
|
||||
public void setBiome(int x, int y, int z, Biome biome) {
|
||||
getChunk(x >> 4, z >> 4).setBiomeAt(x & 15, y, z & 15, biomeIds.get(biome));
|
||||
}
|
||||
|
||||
public Section getChunkSection(int x, int y, int z) {
|
||||
Chunk c = getChunk(x, z);
|
||||
Section s = c.getSection(y);
|
||||
|
||||
if (s == null) {
|
||||
s = Section.newSection();
|
||||
c.setSection(y, s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public void deleteChunk(int x, int z) {
|
||||
|
||||
}
|
||||
|
||||
public Chunk getChunk(int x, int z) {
|
||||
MCAFile mca = getMCA(x >> 5, z >> 5);
|
||||
Chunk c = mca.getChunk(x & 31, z & 31);
|
||||
|
||||
if (c == null) {
|
||||
c = Chunk.newChunk();
|
||||
mca.setChunk(x & 31, z & 31, c);
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public MCAFile getMCA(int x, int z) {
|
||||
long key = Cache.key(x, z);
|
||||
MCAFile mca = writeBuffer.get(key);
|
||||
|
||||
if (mca != null) {
|
||||
return mca;
|
||||
}
|
||||
|
||||
File f = getMCAFile(x, z);
|
||||
try {
|
||||
mca = f.exists() ? MCAUtil.read(f) : new MCAFile(x, z);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
mca = new MCAFile(x, z);
|
||||
}
|
||||
|
||||
writeBuffer.put(key, mca);
|
||||
return mca;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return writeBuffer.size();
|
||||
}
|
||||
|
||||
private static Map<Biome, Integer> computeBiomeIDs() {
|
||||
Map<Biome, Integer> biomeIds = new KMap<>();
|
||||
|
||||
for (Biome biome : Biome.values()) {
|
||||
if (!biome.name().equals("CUSTOM")) {
|
||||
biomeIds.put(biome, INMS.get().getBiomeId(biome));
|
||||
}
|
||||
}
|
||||
|
||||
return biomeIds;
|
||||
}
|
||||
|
||||
public void verify(int mcaox, int mcaoz) {
|
||||
MCAFile file = getMCA(mcaox, mcaoz);
|
||||
|
||||
for(int i = 0; i < 32; i++)
|
||||
{
|
||||
for(int j = 0; j < 32; j++)
|
||||
{
|
||||
Chunk c = file.getChunk(i, j);
|
||||
|
||||
if(c == null)
|
||||
{
|
||||
Iris.warn("Chunk " + ((mcaox << 5) + i) + ", " + ((mcaoz << 5) + j) + " is null in MCA File " + mcaox + ", " + mcaoz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/main/java/com/volmit/iris/engine/data/IOAdapter.java
Normal file
29
src/main/java/com/volmit/iris/engine/data/IOAdapter.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.data;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface IOAdapter<T> {
|
||||
void write(T t, DataOutputStream dos) throws IOException;
|
||||
|
||||
T read(DataInputStream din) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
|
||||
public interface Deserializer<T> {
|
||||
|
||||
T fromStream(InputStream stream) throws IOException;
|
||||
|
||||
default T fromFile(File file) throws IOException {
|
||||
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
|
||||
return fromStream(bis);
|
||||
}
|
||||
}
|
||||
|
||||
default T fromBytes(byte[] data) throws IOException {
|
||||
ByteArrayInputStream stream = new ByteArrayInputStream(data);
|
||||
return fromStream(stream);
|
||||
}
|
||||
|
||||
default T fromResource(Class<?> clazz, String path) throws IOException {
|
||||
try (InputStream stream = clazz.getClassLoader().getResourceAsStream(path)) {
|
||||
if (stream == null) {
|
||||
throw new IOException("resource \"" + path + "\" not found");
|
||||
}
|
||||
return fromStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
default T fromURL(URL url) throws IOException {
|
||||
try (InputStream stream = url.openStream()) {
|
||||
return fromStream(stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ExceptionBiFunction<T, U, R, E extends Exception> {
|
||||
|
||||
R accept(T t, U u) throws E;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ExceptionTriConsumer<T, U, V, E extends Exception> {
|
||||
|
||||
void accept(T t, U u, V v) throws E;
|
||||
}
|
||||
31
src/main/java/com/volmit/iris/engine/data/io/MaxDepthIO.java
Normal file
31
src/main/java/com/volmit/iris/engine/data/io/MaxDepthIO.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
public interface MaxDepthIO {
|
||||
|
||||
default int decrementMaxDepth(int maxDepth) {
|
||||
if (maxDepth < 0) {
|
||||
throw new IllegalArgumentException("negative maximum depth is not allowed");
|
||||
} else if (maxDepth == 0) {
|
||||
throw new MaxDepthReachedException("reached maximum depth of NBT structure");
|
||||
}
|
||||
return --maxDepth;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
/**
|
||||
* Exception indicating that the maximum (de-)serialization depth has been reached.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class MaxDepthReachedException extends RuntimeException {
|
||||
|
||||
public MaxDepthReachedException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
39
src/main/java/com/volmit/iris/engine/data/io/Serializer.java
Normal file
39
src/main/java/com/volmit/iris/engine/data/io/Serializer.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface Serializer<T> {
|
||||
|
||||
void toStream(T object, OutputStream out) throws IOException;
|
||||
|
||||
default void toFile(T object, File file) throws IOException {
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
|
||||
toStream(object, bos);
|
||||
}
|
||||
}
|
||||
|
||||
default byte[] toBytes(T object) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
toStream(object, bos);
|
||||
bos.close();
|
||||
return bos.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface StringDeserializer<T> extends Deserializer<T> {
|
||||
|
||||
T fromReader(Reader reader) throws IOException;
|
||||
|
||||
default T fromString(String s) throws IOException {
|
||||
return fromReader(new StringReader(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
default T fromStream(InputStream stream) throws IOException {
|
||||
try (Reader reader = new InputStreamReader(stream)) {
|
||||
return fromReader(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default T fromFile(File file) throws IOException {
|
||||
try (Reader reader = new FileReader(file)) {
|
||||
return fromReader(reader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default T fromBytes(byte[] data) throws IOException {
|
||||
return fromReader(new StringReader(new String(data)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.data.io;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public interface StringSerializer<T> extends Serializer<T> {
|
||||
|
||||
void toWriter(T object, Writer writer) throws IOException;
|
||||
|
||||
default String toString(T object) throws IOException {
|
||||
Writer writer = new StringWriter();
|
||||
toWriter(object, writer);
|
||||
writer.flush();
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void toStream(T object, OutputStream stream) throws IOException {
|
||||
Writer writer = new OutputStreamWriter(stream);
|
||||
toWriter(object, writer);
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
default void toFile(T object, File file) throws IOException {
|
||||
try (Writer writer = new FileWriter(file)) {
|
||||
toWriter(object, writer);
|
||||
}
|
||||
}
|
||||
}
|
||||
684
src/main/java/com/volmit/iris/engine/data/mca/Chunk.java
Normal file
684
src/main/java/com/volmit/iris/engine/data/mca/Chunk.java
Normal file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
import com.volmit.iris.engine.data.nbt.io.NBTDeserializer;
|
||||
import com.volmit.iris.engine.data.nbt.io.NBTSerializer;
|
||||
import com.volmit.iris.engine.data.nbt.io.NamedTag;
|
||||
import com.volmit.iris.engine.data.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.engine.data.nbt.tag.ListTag;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.volmit.iris.engine.data.mca.LoadFlags.*;
|
||||
|
||||
public class Chunk {
|
||||
|
||||
public static final int DEFAULT_DATA_VERSION = 1628;
|
||||
|
||||
private boolean partial;
|
||||
|
||||
private int lastMCAUpdate;
|
||||
|
||||
private CompoundTag data;
|
||||
|
||||
private int dataVersion;
|
||||
private long lastUpdate;
|
||||
private long inhabitedTime;
|
||||
private int[] biomes;
|
||||
private CompoundTag heightMaps;
|
||||
private CompoundTag carvingMasks;
|
||||
private final Section[] sections = new Section[16];
|
||||
private ListTag<CompoundTag> entities;
|
||||
private ListTag<CompoundTag> tileEntities;
|
||||
private ListTag<CompoundTag> tileTicks;
|
||||
private ListTag<CompoundTag> liquidTicks;
|
||||
private ListTag<ListTag<?>> lights;
|
||||
private ListTag<ListTag<?>> liquidsToBeTicked;
|
||||
private ListTag<ListTag<?>> toBeTicked;
|
||||
private ListTag<ListTag<?>> postProcessing;
|
||||
private String status;
|
||||
private CompoundTag structures;
|
||||
|
||||
Chunk(int lastMCAUpdate) {
|
||||
this.lastMCAUpdate = lastMCAUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new chunk based on raw base data from a region file.
|
||||
*
|
||||
* @param data The raw base data to be used.
|
||||
*/
|
||||
public Chunk(CompoundTag data) {
|
||||
this.data = data;
|
||||
initReferences(ALL_DATA);
|
||||
}
|
||||
|
||||
private void initReferences(long loadFlags) {
|
||||
if (data == null) {
|
||||
throw new NullPointerException("data cannot be null");
|
||||
}
|
||||
CompoundTag level;
|
||||
if ((level = data.getCompoundTag("Level")) == null) {
|
||||
throw new IllegalArgumentException("data does not contain \"Level\" tag");
|
||||
}
|
||||
dataVersion = data.getInt("DataVersion");
|
||||
inhabitedTime = level.getLong("InhabitedTime");
|
||||
lastUpdate = level.getLong("LastUpdate");
|
||||
if ((loadFlags & BIOMES) != 0) {
|
||||
biomes = level.getIntArray("Biomes");
|
||||
}
|
||||
if ((loadFlags & HEIGHTMAPS) != 0) {
|
||||
heightMaps = level.getCompoundTag("Heightmaps");
|
||||
}
|
||||
if ((loadFlags & CARVING_MASKS) != 0) {
|
||||
carvingMasks = level.getCompoundTag("CarvingMasks");
|
||||
}
|
||||
if ((loadFlags & ENTITIES) != 0) {
|
||||
entities = level.containsKey("Entities") ? level.getListTag("Entities").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & TILE_ENTITIES) != 0) {
|
||||
tileEntities = level.containsKey("TileEntities") ? level.getListTag("TileEntities").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & TILE_TICKS) != 0) {
|
||||
tileTicks = level.containsKey("TileTicks") ? level.getListTag("TileTicks").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & LIQUID_TICKS) != 0) {
|
||||
liquidTicks = level.containsKey("LiquidTicks") ? level.getListTag("LiquidTicks").asCompoundTagList() : null;
|
||||
}
|
||||
if ((loadFlags & LIGHTS) != 0) {
|
||||
lights = level.containsKey("Lights") ? level.getListTag("Lights").asListTagList() : null;
|
||||
}
|
||||
if ((loadFlags & LIQUIDS_TO_BE_TICKED) != 0) {
|
||||
liquidsToBeTicked = level.containsKey("LiquidsToBeTicked") ? level.getListTag("LiquidsToBeTicked").asListTagList() : null;
|
||||
}
|
||||
if ((loadFlags & TO_BE_TICKED) != 0) {
|
||||
toBeTicked = level.containsKey("ToBeTicked") ? level.getListTag("ToBeTicked").asListTagList() : null;
|
||||
}
|
||||
if ((loadFlags & POST_PROCESSING) != 0) {
|
||||
postProcessing = level.containsKey("PostProcessing") ? level.getListTag("PostProcessing").asListTagList() : null;
|
||||
}
|
||||
status = level.getString("Status");
|
||||
if ((loadFlags & STRUCTURES) != 0) {
|
||||
structures = level.getCompoundTag("Structures");
|
||||
}
|
||||
if ((loadFlags & (BLOCK_LIGHTS | BLOCK_STATES | SKY_LIGHT)) != 0 && level.containsKey("Sections")) {
|
||||
for (CompoundTag section : level.getListTag("Sections").asCompoundTagList()) {
|
||||
int sectionIndex = section.getByte("Y");
|
||||
if (sectionIndex > 15 || sectionIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
Section newSection = new Section(section, dataVersion, loadFlags);
|
||||
if (newSection.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
sections[sectionIndex] = newSection;
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't requested the full set of data we can drop the underlying raw data to let the GC handle it.
|
||||
if (loadFlags != ALL_DATA) {
|
||||
data = null;
|
||||
partial = true;
|
||||
} else {
|
||||
partial = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this chunk to a <code>RandomAccessFile</code>.
|
||||
*
|
||||
* @param raf The RandomAccessFile to be written to.
|
||||
* @param xPos The x-coordinate of the chunk.
|
||||
* @param zPos The z-coodrinate of the chunk.
|
||||
* @return The amount of bytes written to the RandomAccessFile.
|
||||
* @throws UnsupportedOperationException When something went wrong during writing.
|
||||
* @throws IOException When something went wrong during writing.
|
||||
*/
|
||||
public int serialize(RandomAccessFile raf, int xPos, int zPos) throws IOException {
|
||||
if (partial) {
|
||||
throw new UnsupportedOperationException("Partially loaded chunks cannot be serialized");
|
||||
}
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream(4096);
|
||||
try (BufferedOutputStream nbtOut = new BufferedOutputStream(CompressionType.ZLIB.compress(baos))) {
|
||||
new NBTSerializer(false).toStream(new NamedTag(null, updateHandle(xPos, zPos)), nbtOut);
|
||||
}
|
||||
byte[] rawData = baos.toByteArray();
|
||||
raf.writeInt(rawData.length + 1); // including the byte to store the compression type
|
||||
raf.writeByte(CompressionType.ZLIB.getID());
|
||||
raf.write(rawData);
|
||||
return rawData.length + 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads chunk data from a RandomAccessFile. The RandomAccessFile must already be at the correct position.
|
||||
*
|
||||
* @param raf The RandomAccessFile to read the chunk data from.
|
||||
* @throws IOException When something went wrong during reading.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf) throws IOException {
|
||||
deserialize(raf, ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads chunk data from a RandomAccessFile. The RandomAccessFile must already be at the correct position.
|
||||
*
|
||||
* @param raf The RandomAccessFile to read the chunk data from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @throws IOException When something went wrong during reading.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf, long loadFlags) throws IOException {
|
||||
byte compressionTypeByte = raf.readByte();
|
||||
CompressionType compressionType = CompressionType.getFromID(compressionTypeByte);
|
||||
if (compressionType == null) {
|
||||
throw new IOException("invalid compression type " + compressionTypeByte);
|
||||
}
|
||||
BufferedInputStream dis = new BufferedInputStream(compressionType.decompress(new FileInputStream(raf.getFD())));
|
||||
NamedTag tag = new NBTDeserializer(false).fromStream(dis);
|
||||
if (tag != null && tag.getTag() instanceof CompoundTag) {
|
||||
data = (CompoundTag) tag.getTag();
|
||||
initReferences(loadFlags);
|
||||
} else {
|
||||
throw new IOException("invalid data tag: " + (tag == null ? "null" : tag.getClass().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBiomeAt(int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public int getBiomeAt(int blockX, int blockZ) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
return -1;
|
||||
}
|
||||
return biomes[getBlockIndex(blockX, blockZ)];
|
||||
} else {
|
||||
throw new IllegalStateException("cannot get biome using Chunk#getBiomeAt(int,int) from biome data with DataVersion of 2202 or higher, use Chunk#getBiomeAt(int,int,int) instead");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a biome id at a specific block in this chunk.
|
||||
* The coordinates can be absolute coordinates or relative to the region or chunk.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @return The biome id or -1 if the biomes are not correctly initialized.
|
||||
*/
|
||||
public int getBiomeAt(int blockX, int blockY, int blockZ) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
return -1;
|
||||
}
|
||||
return biomes[getBlockIndex(blockX, blockZ)];
|
||||
} else {
|
||||
if (biomes == null || biomes.length != 1024) {
|
||||
return -1;
|
||||
}
|
||||
int biomeX = (blockX & 0xF) >> 2;
|
||||
int biomeY = (blockY & 0xF) >> 2;
|
||||
int biomeZ = (blockZ & 0xF) >> 2;
|
||||
|
||||
return biomes[getBiomeIndex(biomeX, biomeY, biomeZ)];
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setBiomeAt(int blockX, int blockZ, int biomeID) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
biomes = new int[256];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
biomes[getBlockIndex(blockX, blockZ)] = biomeID;
|
||||
} else {
|
||||
if (biomes == null || biomes.length != 1024) {
|
||||
biomes = new int[1024];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
|
||||
int biomeX = (blockX & 0xF) >> 2;
|
||||
int biomeZ = (blockZ & 0xF) >> 2;
|
||||
|
||||
for (int y = 0; y < 64; y++) {
|
||||
biomes[getBiomeIndex(biomeX, y, biomeZ)] = biomeID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a biome id at a specific block column.
|
||||
* The coordinates can be absolute coordinates or relative to the region or chunk.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block column.
|
||||
* @param blockZ The z-coordinate of the block column.
|
||||
* @param biomeID The biome id to be set.
|
||||
* When set to a negative number, Minecraft will replace it with the block column's default biome.
|
||||
*/
|
||||
public void setBiomeAt(int blockX, int blockY, int blockZ, int biomeID) {
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes == null || biomes.length != 256) {
|
||||
biomes = new int[256];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
biomes[getBlockIndex(blockX, blockZ)] = biomeID;
|
||||
} else {
|
||||
if (biomes == null || biomes.length != 1024) {
|
||||
biomes = new int[1024];
|
||||
Arrays.fill(biomes, -1);
|
||||
}
|
||||
|
||||
int biomeX = (blockX & 0xF) >> 2;
|
||||
int biomeZ = (blockZ & 0xF) >> 2;
|
||||
|
||||
biomes[getBiomeIndex(biomeX, blockY, biomeZ)] = biomeID;
|
||||
}
|
||||
}
|
||||
|
||||
int getBiomeIndex(int biomeX, int biomeY, int biomeZ) {
|
||||
return biomeY * 64 + biomeZ * 4 + biomeX;
|
||||
}
|
||||
|
||||
public CompoundTag getBlockStateAt(int blockX, int blockY, int blockZ) {
|
||||
Section section = sections[MCAUtil.blockToChunk(blockY)];
|
||||
if (section == null) {
|
||||
return null;
|
||||
}
|
||||
return section.getBlockStateAt(blockX, blockY, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a block state at a specific location.
|
||||
* The block coordinates can be absolute or relative to the region or chunk.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @param state The block state to be set.
|
||||
* @param cleanup When <code>true</code>, it will cleanup all palettes of this chunk.
|
||||
* This option should only be used moderately to avoid unnecessary recalculation of the palette indices.
|
||||
* Recalculating the Palette should only be executed once right before saving the Chunk to file.
|
||||
*/
|
||||
public void setBlockStateAt(int blockX, int blockY, int blockZ, CompoundTag state, boolean cleanup) {
|
||||
int sectionIndex = MCAUtil.blockToChunk(blockY);
|
||||
Section section = sections[sectionIndex];
|
||||
if (section == null) {
|
||||
section = sections[sectionIndex] = Section.newSection();
|
||||
}
|
||||
section.setBlockStateAt(blockX, blockY, blockZ, state, cleanup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The DataVersion of this chunk.
|
||||
*/
|
||||
public int getDataVersion() {
|
||||
return dataVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the DataVersion of this chunk. This does not check if the data of this chunk conforms
|
||||
* to that DataVersion, that is the responsibility of the developer.
|
||||
*
|
||||
* @param dataVersion The DataVersion to be set.
|
||||
*/
|
||||
public void setDataVersion(int dataVersion) {
|
||||
this.dataVersion = dataVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The timestamp when this region file was last updated in seconds since 1970-01-01.
|
||||
*/
|
||||
public int getLastMCAUpdate() {
|
||||
return lastMCAUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the timestamp when this region file was last updated in seconds since 1970-01-01.
|
||||
*
|
||||
* @param lastMCAUpdate The time in seconds since 1970-01-01.
|
||||
*/
|
||||
public void setLastMCAUpdate(int lastMCAUpdate) {
|
||||
this.lastMCAUpdate = lastMCAUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The generation station of this chunk.
|
||||
*/
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the generation status of this chunk.
|
||||
*
|
||||
* @param status The generation status of this chunk.
|
||||
*/
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the section at the given y-coordinate.
|
||||
*
|
||||
* @param sectionY The y-coordinate of the section in this chunk ranging from 0 to 15.
|
||||
* @return The Section.
|
||||
*/
|
||||
public Section getSection(int sectionY) {
|
||||
return sections[sectionY];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a section at a givesn y-coordinate
|
||||
*
|
||||
* @param sectionY The y-coordinate of the section in this chunk ranging from 0 to 15.
|
||||
* @param section The section to be set.
|
||||
*/
|
||||
public void setSection(int sectionY, Section section) {
|
||||
sections[sectionY] = section;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The timestamp when this chunk was last updated as a UNIX timestamp.
|
||||
*/
|
||||
public long getLastUpdate() {
|
||||
return lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time when this chunk was last updated as a UNIX timestamp.
|
||||
*
|
||||
* @param lastUpdate The UNIX timestamp.
|
||||
*/
|
||||
public void setLastUpdate(long lastUpdate) {
|
||||
this.lastUpdate = lastUpdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The cumulative amount of time players have spent in this chunk in ticks.
|
||||
*/
|
||||
public long getInhabitedTime() {
|
||||
return inhabitedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cumulative amount of time players have spent in this chunk in ticks.
|
||||
*
|
||||
* @param inhabitedTime The time in ticks.
|
||||
*/
|
||||
public void setInhabitedTime(long inhabitedTime) {
|
||||
this.inhabitedTime = inhabitedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A matrix of biome IDs for all block columns in this chunk.
|
||||
*/
|
||||
public int[] getBiomes() {
|
||||
return biomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the biome IDs for this chunk.
|
||||
*
|
||||
* @param biomes The biome ID matrix of this chunk. Must have a length of <code>256</code>.
|
||||
* @throws IllegalArgumentException When the biome matrix does not have a length of <code>256</code>
|
||||
* or is <code>null</code>
|
||||
*/
|
||||
public void setBiomes(int[] biomes) {
|
||||
if (biomes != null) {
|
||||
if (dataVersion < 2202 && biomes.length != 256 || dataVersion >= 2202 && biomes.length != 1024) {
|
||||
throw new IllegalArgumentException("biomes array must have a length of " + (dataVersion < 2202 ? "256" : "1024"));
|
||||
}
|
||||
}
|
||||
this.biomes = biomes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The height maps of this chunk.
|
||||
*/
|
||||
public CompoundTag getHeightMaps() {
|
||||
return heightMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the height maps of this chunk.
|
||||
*
|
||||
* @param heightMaps The height maps.
|
||||
*/
|
||||
public void setHeightMaps(CompoundTag heightMaps) {
|
||||
this.heightMaps = heightMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The carving masks of this chunk.
|
||||
*/
|
||||
public CompoundTag getCarvingMasks() {
|
||||
return carvingMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the carving masks of this chunk.
|
||||
*
|
||||
* @param carvingMasks The carving masks.
|
||||
*/
|
||||
public void setCarvingMasks(CompoundTag carvingMasks) {
|
||||
this.carvingMasks = carvingMasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The entities of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getEntities() {
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the entities of this chunk.
|
||||
*
|
||||
* @param entities The entities.
|
||||
*/
|
||||
public void setEntities(ListTag<CompoundTag> entities) {
|
||||
this.entities = entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tile entities of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getTileEntities() {
|
||||
return tileEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tile entities of this chunk.
|
||||
*
|
||||
* @param tileEntities The tile entities of this chunk.
|
||||
*/
|
||||
public void setTileEntities(ListTag<CompoundTag> tileEntities) {
|
||||
this.tileEntities = tileEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tile ticks of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getTileTicks() {
|
||||
return tileTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tile ticks of this chunk.
|
||||
*
|
||||
* @param tileTicks Thee tile ticks.
|
||||
*/
|
||||
public void setTileTicks(ListTag<CompoundTag> tileTicks) {
|
||||
this.tileTicks = tileTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The liquid ticks of this chunk.
|
||||
*/
|
||||
public ListTag<CompoundTag> getLiquidTicks() {
|
||||
return liquidTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the liquid ticks of this chunk.
|
||||
*
|
||||
* @param liquidTicks The liquid ticks.
|
||||
*/
|
||||
public void setLiquidTicks(ListTag<CompoundTag> liquidTicks) {
|
||||
this.liquidTicks = liquidTicks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The light sources in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getLights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the light sources in this chunk.
|
||||
*
|
||||
* @param lights The light sources.
|
||||
*/
|
||||
public void setLights(ListTag<ListTag<?>> lights) {
|
||||
this.lights = lights;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return THe liquids to be ticked in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getLiquidsToBeTicked() {
|
||||
return liquidsToBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the liquids to be ticked in this chunk.
|
||||
*
|
||||
* @param liquidsToBeTicked The liquids to be ticked.
|
||||
*/
|
||||
public void setLiquidsToBeTicked(ListTag<ListTag<?>> liquidsToBeTicked) {
|
||||
this.liquidsToBeTicked = liquidsToBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Stuff to be ticked in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getToBeTicked() {
|
||||
return toBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets stuff to be ticked in this chunk.
|
||||
*
|
||||
* @param toBeTicked The stuff to be ticked.
|
||||
*/
|
||||
public void setToBeTicked(ListTag<ListTag<?>> toBeTicked) {
|
||||
this.toBeTicked = toBeTicked;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Things that are in post processing in this chunk.
|
||||
*/
|
||||
public ListTag<ListTag<?>> getPostProcessing() {
|
||||
return postProcessing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets things to be post processed in this chunk.
|
||||
*
|
||||
* @param postProcessing The things to be post processed.
|
||||
*/
|
||||
public void setPostProcessing(ListTag<ListTag<?>> postProcessing) {
|
||||
this.postProcessing = postProcessing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Data about structures in this chunk.
|
||||
*/
|
||||
public CompoundTag getStructures() {
|
||||
return structures;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data about structures in this chunk.
|
||||
*
|
||||
* @param structures The data about structures.
|
||||
*/
|
||||
public void setStructures(CompoundTag structures) {
|
||||
this.structures = structures;
|
||||
}
|
||||
|
||||
int getBlockIndex(int blockX, int blockZ) {
|
||||
return (blockZ & 0xF) * 16 + (blockX & 0xF);
|
||||
}
|
||||
|
||||
public void cleanupPalettesAndBlockStates() {
|
||||
for (Section section : sections) {
|
||||
if (section != null) {
|
||||
section.cleanupPaletteAndBlockStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Chunk newChunk() {
|
||||
Chunk c = new Chunk(0);
|
||||
c.dataVersion = DEFAULT_DATA_VERSION;
|
||||
c.data = new CompoundTag();
|
||||
c.data.put("Level", new CompoundTag());
|
||||
c.status = "mobs_spawned";
|
||||
return c;
|
||||
}
|
||||
|
||||
public CompoundTag updateHandle(int xPos, int zPos) {
|
||||
data.putInt("DataVersion", dataVersion);
|
||||
CompoundTag level = data.getCompoundTag("Level");
|
||||
level.putInt("xPos", xPos);
|
||||
level.putInt("zPos", zPos);
|
||||
level.putLong("LastUpdate", lastUpdate);
|
||||
level.putLong("InhabitedTime", inhabitedTime);
|
||||
if (dataVersion < 2202) {
|
||||
if (biomes != null && biomes.length == 256) level.putIntArray("Biomes", biomes);
|
||||
} else {
|
||||
if (biomes != null && biomes.length == 1024) level.putIntArray("Biomes", biomes);
|
||||
}
|
||||
if (heightMaps != null) level.put("Heightmaps", heightMaps);
|
||||
if (carvingMasks != null) level.put("CarvingMasks", carvingMasks);
|
||||
if (entities != null) level.put("Entities", entities);
|
||||
if (tileEntities != null) level.put("TileEntities", tileEntities);
|
||||
if (tileTicks != null) level.put("TileTicks", tileTicks);
|
||||
if (liquidTicks != null) level.put("LiquidTicks", liquidTicks);
|
||||
if (lights != null) level.put("Lights", lights);
|
||||
if (liquidsToBeTicked != null) level.put("LiquidsToBeTicked", liquidsToBeTicked);
|
||||
if (toBeTicked != null) level.put("ToBeTicked", toBeTicked);
|
||||
if (postProcessing != null) level.put("PostProcessing", postProcessing);
|
||||
level.putString("Status", status);
|
||||
if (structures != null) level.put("Structures", structures);
|
||||
ListTag<CompoundTag> sections = new ListTag<>(CompoundTag.class);
|
||||
for (int i = 0; i < this.sections.length; i++) {
|
||||
if (this.sections[i] != null) {
|
||||
sections.add(this.sections[i].updateHandle(i));
|
||||
}
|
||||
}
|
||||
level.put("Sections", sections);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
public enum CompressionType {
|
||||
|
||||
NONE(0, t -> t, t -> t),
|
||||
GZIP(1, GZIPOutputStream::new, GZIPInputStream::new),
|
||||
ZLIB(2, DeflaterOutputStream::new, InflaterInputStream::new);
|
||||
|
||||
private final byte id;
|
||||
private final ExceptionFunction<OutputStream, ? extends OutputStream, IOException> compressor;
|
||||
private final ExceptionFunction<InputStream, ? extends InputStream, IOException> decompressor;
|
||||
|
||||
CompressionType(int id,
|
||||
ExceptionFunction<OutputStream, ? extends OutputStream, IOException> compressor,
|
||||
ExceptionFunction<InputStream, ? extends InputStream, IOException> decompressor) {
|
||||
this.id = (byte) id;
|
||||
this.compressor = compressor;
|
||||
this.decompressor = decompressor;
|
||||
}
|
||||
|
||||
public byte getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public OutputStream compress(OutputStream out) throws IOException {
|
||||
return compressor.accept(out);
|
||||
}
|
||||
|
||||
public InputStream decompress(InputStream in) throws IOException {
|
||||
return decompressor.accept(in);
|
||||
}
|
||||
|
||||
public static CompressionType getFromID(byte id) {
|
||||
for (CompressionType c : CompressionType.values()) {
|
||||
if (c.id == id) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ExceptionFunction<T, R, E extends Exception> {
|
||||
|
||||
R accept(T t) throws E;
|
||||
}
|
||||
42
src/main/java/com/volmit/iris/engine/data/mca/LoadFlags.java
Normal file
42
src/main/java/com/volmit/iris/engine/data/mca/LoadFlags.java
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
public class LoadFlags {
|
||||
|
||||
public static final long BIOMES = 0x0001;
|
||||
public static final long HEIGHTMAPS = 0x0002;
|
||||
public static final long CARVING_MASKS = 0x0004;
|
||||
public static final long ENTITIES = 0x0008;
|
||||
public static final long TILE_ENTITIES = 0x0010;
|
||||
public static final long TILE_TICKS = 0x0040;
|
||||
public static final long LIQUID_TICKS = 0x0080;
|
||||
public static final long TO_BE_TICKED = 0x0100;
|
||||
public static final long POST_PROCESSING = 0x0200;
|
||||
public static final long STRUCTURES = 0x0400;
|
||||
public static final long BLOCK_LIGHTS = 0x0800;
|
||||
public static final long BLOCK_STATES = 0x1000;
|
||||
public static final long SKY_LIGHT = 0x2000;
|
||||
public static final long LIGHTS = 0x4000;
|
||||
public static final long LIQUIDS_TO_BE_TICKED = 0x8000;
|
||||
|
||||
public static final long ALL_DATA = 0xffffffffffffffffL;
|
||||
|
||||
|
||||
}
|
||||
333
src/main/java/com/volmit/iris/engine/data/mca/MCAFile.java
Normal file
333
src/main/java/com/volmit/iris/engine/data/mca/MCAFile.java
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
import com.volmit.iris.engine.data.nbt.tag.CompoundTag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class MCAFile {
|
||||
|
||||
/**
|
||||
* The default chunk data version used when no custom version is supplied.
|
||||
*/
|
||||
public static final int DEFAULT_DATA_VERSION = 1628;
|
||||
|
||||
private final int regionX;
|
||||
private final int regionZ;
|
||||
private Chunk[] chunks;
|
||||
|
||||
/**
|
||||
* MCAFile represents a world save file used by Minecraft to store world
|
||||
* data on the hard drive.
|
||||
* This constructor needs the x- and z-coordinates of the stored region,
|
||||
* which can usually be taken from the file name {@code r.x.z.mca}
|
||||
*
|
||||
* @param regionX The x-coordinate of this region.
|
||||
* @param regionZ The z-coordinate of this region.
|
||||
*/
|
||||
public MCAFile(int regionX, int regionZ) {
|
||||
this.regionX = regionX;
|
||||
this.regionZ = regionZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an .mca file from a {@code RandomAccessFile} into this object.
|
||||
* This method does not perform any cleanups on the data.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to read from.
|
||||
* @throws IOException If something went wrong during deserialization.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf) throws IOException {
|
||||
deserialize(raf, LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an .mca file from a {@code RandomAccessFile} into this object.
|
||||
* This method does not perform any cleanups on the data.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to read from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @throws IOException If something went wrong during deserialization.
|
||||
*/
|
||||
public void deserialize(RandomAccessFile raf, long loadFlags) throws IOException {
|
||||
chunks = new Chunk[1024];
|
||||
for (int i = 0; i < 1024; i++) {
|
||||
raf.seek(i * 4);
|
||||
int offset = raf.read() << 16;
|
||||
offset |= (raf.read() & 0xFF) << 8;
|
||||
offset |= raf.read() & 0xFF;
|
||||
if (raf.readByte() == 0) {
|
||||
continue;
|
||||
}
|
||||
raf.seek(4096 + i * 4);
|
||||
int timestamp = raf.readInt();
|
||||
Chunk chunk = new Chunk(timestamp);
|
||||
raf.seek(4096L * offset + 4); //+4: skip data size
|
||||
chunk.deserialize(raf, loadFlags);
|
||||
chunks[i] = chunk;
|
||||
}
|
||||
}
|
||||
|
||||
public Chunk[] getChunks() {
|
||||
return chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MCAFile#serialize(RandomAccessFile, boolean)} without updating any timestamps.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to write to.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something went wrong during serialization.
|
||||
* @see MCAFile#serialize(RandomAccessFile, boolean)
|
||||
*/
|
||||
public int serialize(RandomAccessFile raf) throws IOException {
|
||||
return serialize(raf, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes this object to an .mca file.
|
||||
* This method does not perform any cleanups on the data.
|
||||
*
|
||||
* @param raf The {@code RandomAccessFile} to write to.
|
||||
* @param changeLastUpdate Whether it should update all timestamps that show
|
||||
* when this file was last updated.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something went wrong during serialization.
|
||||
*/
|
||||
public int serialize(RandomAccessFile raf, boolean changeLastUpdate) throws IOException {
|
||||
int globalOffset = 2;
|
||||
int lastWritten = 0;
|
||||
int timestamp = (int) (System.currentTimeMillis() / 1000L);
|
||||
int chunksWritten = 0;
|
||||
int chunkXOffset = MCAUtil.regionToChunk(regionX);
|
||||
int chunkZOffset = MCAUtil.regionToChunk(regionZ);
|
||||
|
||||
if (chunks == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int cx = 0; cx < 32; cx++) {
|
||||
for (int cz = 0; cz < 32; cz++) {
|
||||
int index = getChunkIndex(cx, cz);
|
||||
Chunk chunk = chunks[index];
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
raf.seek(4096L * globalOffset);
|
||||
lastWritten = chunk.serialize(raf, chunkXOffset + cx, chunkZOffset + cz);
|
||||
|
||||
if (lastWritten == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
chunksWritten++;
|
||||
|
||||
int sectors = (lastWritten >> 12) + (lastWritten % 4096 == 0 ? 0 : 1);
|
||||
|
||||
raf.seek(index * 4L);
|
||||
raf.writeByte(globalOffset >>> 16);
|
||||
raf.writeByte(globalOffset >> 8 & 0xFF);
|
||||
raf.writeByte(globalOffset & 0xFF);
|
||||
raf.writeByte(sectors);
|
||||
|
||||
// write timestamp
|
||||
raf.seek(index * 4L + 4096);
|
||||
raf.writeInt(changeLastUpdate ? timestamp : chunk.getLastMCAUpdate());
|
||||
|
||||
globalOffset += sectors;
|
||||
}
|
||||
}
|
||||
|
||||
// padding
|
||||
if (lastWritten % 4096 != 0) {
|
||||
raf.seek(globalOffset * 4096L - 1);
|
||||
raf.write(0);
|
||||
}
|
||||
return chunksWritten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific Chunk at a specific index. The index must be in range of 0 - 1023.
|
||||
*
|
||||
* @param index The index of the Chunk.
|
||||
* @param chunk The Chunk to be set.
|
||||
* @throws IndexOutOfBoundsException If index is not in the range.
|
||||
*/
|
||||
public void setChunk(int index, Chunk chunk) {
|
||||
checkIndex(index);
|
||||
if (chunks == null) {
|
||||
chunks = new Chunk[1024];
|
||||
}
|
||||
chunks[index] = chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific Chunk at a specific chunk location.
|
||||
* The x- and z-value can be absolute chunk coordinates or they can be relative to the region origin.
|
||||
*
|
||||
* @param chunkX The x-coordinate of the Chunk.
|
||||
* @param chunkZ The z-coordinate of the Chunk.
|
||||
* @param chunk The chunk to be set.
|
||||
*/
|
||||
public void setChunk(int chunkX, int chunkZ, Chunk chunk) {
|
||||
setChunk(getChunkIndex(chunkX, chunkZ), chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chunk data of a chunk at a specific index in this file.
|
||||
*
|
||||
* @param index The index of the chunk in this file.
|
||||
* @return The chunk data.
|
||||
*/
|
||||
public Chunk getChunk(int index) {
|
||||
checkIndex(index);
|
||||
if (chunks == null) {
|
||||
return null;
|
||||
}
|
||||
return chunks[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chunk data of a chunk in this file.
|
||||
*
|
||||
* @param chunkX The x-coordinate of the chunk.
|
||||
* @param chunkZ The z-coordinate of the chunk.
|
||||
* @return The chunk data.
|
||||
*/
|
||||
public Chunk getChunk(int chunkX, int chunkZ) {
|
||||
return getChunk(getChunkIndex(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the index of a chunk from its x- and z-coordinates in this region.
|
||||
* This works with absolute and relative coordinates.
|
||||
*
|
||||
* @param chunkX The x-coordinate of the chunk.
|
||||
* @param chunkZ The z-coordinate of the chunk.
|
||||
* @return The index of this chunk.
|
||||
*/
|
||||
public static int getChunkIndex(int chunkX, int chunkZ) {
|
||||
return (chunkX & 0x1F) + (chunkZ & 0x1F) * 32;
|
||||
}
|
||||
|
||||
private int checkIndex(int index) {
|
||||
if (index < 0 || index > 1023) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private Chunk createChunkIfMissing(int blockX, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(chunkX, chunkZ);
|
||||
if (chunk == null) {
|
||||
chunk = Chunk.newChunk();
|
||||
setChunk(getChunkIndex(chunkX, chunkZ), chunk);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBiomeAt(int, int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setBiomeAt(int blockX, int blockZ, int biomeID) {
|
||||
createChunkIfMissing(blockX, blockZ).setBiomeAt(blockX, blockZ, biomeID);
|
||||
}
|
||||
|
||||
public void setBiomeAt(int blockX, int blockY, int blockZ, int biomeID) {
|
||||
createChunkIfMissing(blockX, blockZ).setBiomeAt(blockX, blockY, blockZ, biomeID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #getBiomeAt(int, int, int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public int getBiomeAt(int blockX, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(getChunkIndex(chunkX, chunkZ));
|
||||
if (chunk == null) {
|
||||
return -1;
|
||||
}
|
||||
return chunk.getBiomeAt(blockX, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the biome id at a specific block.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @return The biome id if the chunk exists and the chunk has biomes, otherwise -1.
|
||||
*/
|
||||
public int getBiomeAt(int blockX, int blockY, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(getChunkIndex(chunkX, chunkZ));
|
||||
if (chunk == null) {
|
||||
return -1;
|
||||
}
|
||||
return chunk.getBiomeAt(blockX, blockY, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a block state at a specific block location.
|
||||
* The block coordinates can be absolute coordinates or they can be relative to the region.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @param state The block state to be set.
|
||||
* @param cleanup Whether the Palette and the BLockStates should be recalculated after adding the block state.
|
||||
*/
|
||||
public void setBlockStateAt(int blockX, int blockY, int blockZ, CompoundTag state, boolean cleanup) {
|
||||
createChunkIfMissing(blockX, blockZ).setBlockStateAt(blockX, blockY, blockZ, state, cleanup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a block state at a specific block location.
|
||||
* The block coordinates can be absolute coordinates or they can be relative to the region.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block.
|
||||
* @param blockY The y-coordinate of the block.
|
||||
* @param blockZ The z-coordinate of the block.
|
||||
* @return The block state or <code>null</code> if the chunk or the section do not exist.
|
||||
*/
|
||||
public CompoundTag getBlockStateAt(int blockX, int blockY, int blockZ) {
|
||||
int chunkX = MCAUtil.blockToChunk(blockX), chunkZ = MCAUtil.blockToChunk(blockZ);
|
||||
Chunk chunk = getChunk(chunkX, chunkZ);
|
||||
if (chunk == null) {
|
||||
return null;
|
||||
}
|
||||
return chunk.getBlockStateAt(blockX, blockY, blockZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the Palette and the BlockStates of all chunks and sections of this region.
|
||||
*/
|
||||
public void cleanupPalettesAndBlockStates() {
|
||||
for (Chunk chunk : chunks) {
|
||||
if (chunk != null) {
|
||||
chunk.cleanupPalettesAndBlockStates();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
260
src/main/java/com/volmit/iris/engine/data/mca/MCAUtil.java
Normal file
260
src/main/java/com/volmit/iris/engine/data/mca/MCAUtil.java
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Provides main and utility functions to read and write .mca files and
|
||||
* to convert block, chunk and region coordinates.
|
||||
*/
|
||||
public final class MCAUtil {
|
||||
|
||||
private MCAUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file to read the data from.
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data.
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
* @see MCAUtil#read(File)
|
||||
*/
|
||||
public static MCAFile read(String file) throws IOException {
|
||||
return read(new File(file), LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an MCA file and loads all of its chunks.
|
||||
*
|
||||
* @param file The file to read the data from.
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data.
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
*/
|
||||
public static MCAFile read(File file) throws IOException {
|
||||
return read(file, LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file to read the data from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data.
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
* @see MCAUtil#read(File)
|
||||
*/
|
||||
public static MCAFile read(String file, long loadFlags) throws IOException {
|
||||
return read(new File(file), loadFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an MCA file and loads all of its chunks.
|
||||
*
|
||||
* @param file The file to read the data from.
|
||||
* @param loadFlags A logical or of {@link LoadFlags} constants indicating what data should be loaded
|
||||
* @return An in-memory representation of the MCA file with decompressed chunk data
|
||||
* @throws IOException if something during deserialization goes wrong.
|
||||
*/
|
||||
public static MCAFile read(File file, long loadFlags) throws IOException {
|
||||
MCAFile mcaFile = newMCAFile(file);
|
||||
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
|
||||
mcaFile.deserialize(raf, loadFlags);
|
||||
return mcaFile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MCAUtil#write(MCAFile, File, boolean)} without changing the timestamps.
|
||||
*
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
* @see MCAUtil#write(MCAFile, File, boolean)
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, String file) throws IOException {
|
||||
return write(mcaFile, new File(file), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link MCAUtil#write(MCAFile, File, boolean)} without changing the timestamps.
|
||||
*
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
* @see MCAUtil#write(MCAFile, File, boolean)
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, File file) throws IOException {
|
||||
return write(mcaFile, file, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @param changeLastUpdate Whether to adjust the timestamps of when the file was saved.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
* @see MCAUtil#write(MCAFile, File, boolean)
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, String file, boolean changeLastUpdate) throws IOException {
|
||||
return write(mcaFile, new File(file), changeLastUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes an {@code MCAFile} object to disk. It optionally adjusts the timestamps
|
||||
* when the file was last saved to the current date and time or leaves them at
|
||||
* the value set by either loading an already existing MCA file or setting them manually.<br>
|
||||
* If the file already exists, it is completely overwritten by the new file (no modification).
|
||||
*
|
||||
* @param file The file to write to.
|
||||
* @param mcaFile The data of the MCA file to write.
|
||||
* @param changeLastUpdate Whether to adjust the timestamps of when the file was saved.
|
||||
* @return The amount of chunks written to the file.
|
||||
* @throws IOException If something goes wrong during serialization.
|
||||
*/
|
||||
public static int write(MCAFile mcaFile, File file, boolean changeLastUpdate) throws IOException {
|
||||
if (mcaFile == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
File to = file;
|
||||
if (file.exists()) {
|
||||
to = File.createTempFile(to.getName(), null);
|
||||
}
|
||||
int chunks;
|
||||
try (RandomAccessFile raf = new RandomAccessFile(to, "rw")) {
|
||||
chunks = mcaFile.serialize(raf, changeLastUpdate);
|
||||
}
|
||||
|
||||
if (chunks > 0 && to != file) {
|
||||
Files.move(to.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the chunks coordinates into region coordinates and calls
|
||||
* {@link MCAUtil#createNameFromRegionLocation(int, int)}
|
||||
*
|
||||
* @param chunkX The x-value of the location of the chunk.
|
||||
* @param chunkZ The z-value of the location of the chunk.
|
||||
* @return A mca filename in the format "r.{regionX}.{regionZ}.mca"
|
||||
*/
|
||||
public static String createNameFromChunkLocation(int chunkX, int chunkZ) {
|
||||
return createNameFromRegionLocation(chunkToRegion(chunkX), chunkToRegion(chunkZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the block coordinates into region coordinates and calls
|
||||
* {@link MCAUtil#createNameFromRegionLocation(int, int)}
|
||||
*
|
||||
* @param blockX The x-value of the location of the block.
|
||||
* @param blockZ The z-value of the location of the block.
|
||||
* @return A mca filename in the format "r.{regionX}.{regionZ}.mca"
|
||||
*/
|
||||
public static String createNameFromBlockLocation(int blockX, int blockZ) {
|
||||
return createNameFromRegionLocation(blockToRegion(blockX), blockToRegion(blockZ));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a filename string from provided chunk coordinates.
|
||||
*
|
||||
* @param regionX The x-value of the location of the region.
|
||||
* @param regionZ The z-value of the location of the region.
|
||||
* @return A mca filename in the format "r.{regionX}.{regionZ}.mca"
|
||||
*/
|
||||
public static String createNameFromRegionLocation(int regionX, int regionZ) {
|
||||
return "r." + regionX + "." + regionZ + ".mca";
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a block coordinate value into a chunk coordinate value.
|
||||
*
|
||||
* @param block The block coordinate value.
|
||||
* @return The chunk coordinate value.
|
||||
*/
|
||||
public static int blockToChunk(int block) {
|
||||
return block >> 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a block coordinate value into a region coordinate value.
|
||||
*
|
||||
* @param block The block coordinate value.
|
||||
* @return The region coordinate value.
|
||||
*/
|
||||
public static int blockToRegion(int block) {
|
||||
return block >> 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a chunk coordinate value into a region coordinate value.
|
||||
*
|
||||
* @param chunk The chunk coordinate value.
|
||||
* @return The region coordinate value.
|
||||
*/
|
||||
public static int chunkToRegion(int chunk) {
|
||||
return chunk >> 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a region coordinate value into a chunk coordinate value.
|
||||
*
|
||||
* @param region The region coordinate value.
|
||||
* @return The chunk coordinate value.
|
||||
*/
|
||||
public static int regionToChunk(int region) {
|
||||
return region << 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a region coordinate value into a block coordinate value.
|
||||
*
|
||||
* @param region The region coordinate value.
|
||||
* @return The block coordinate value.
|
||||
*/
|
||||
public static int regionToBlock(int region) {
|
||||
return region << 9;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a chunk coordinate value into a block coordinate value.
|
||||
*
|
||||
* @param chunk The chunk coordinate value.
|
||||
* @return The block coordinate value.
|
||||
*/
|
||||
public static int chunkToBlock(int chunk) {
|
||||
return chunk << 4;
|
||||
}
|
||||
|
||||
private static final Pattern mcaFilePattern = Pattern.compile("^.*r\\.(?<regionX>-?\\d+)\\.(?<regionZ>-?\\d+)\\.mca$");
|
||||
|
||||
public static MCAFile newMCAFile(File file) {
|
||||
Matcher m = mcaFilePattern.matcher(file.getName());
|
||||
if (m.find()) {
|
||||
return new MCAFile(Integer.parseInt(m.group("regionX")), Integer.parseInt(m.group("regionZ")));
|
||||
}
|
||||
throw new IllegalArgumentException("invalid mca file name: " + file.getName());
|
||||
}
|
||||
}
|
||||
438
src/main/java/com/volmit/iris/engine/data/mca/Section.java
Normal file
438
src/main/java/com/volmit/iris/engine/data/mca/Section.java
Normal file
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* 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.data.mca;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.nbt.tag.ByteArrayTag;
|
||||
import com.volmit.iris.engine.data.nbt.tag.CompoundTag;
|
||||
import com.volmit.iris.engine.data.nbt.tag.ListTag;
|
||||
import com.volmit.iris.engine.data.nbt.tag.LongArrayTag;
|
||||
import com.volmit.iris.util.KMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Section {
|
||||
|
||||
private CompoundTag data;
|
||||
private Map<String, List<PaletteIndex>> valueIndexedPalette = new KMap<>();
|
||||
private ListTag<CompoundTag> palette;
|
||||
private byte[] blockLight;
|
||||
private long[] blockStates;
|
||||
private byte[] skyLight;
|
||||
private int dataVersion;
|
||||
|
||||
public Section(CompoundTag sectionRoot, int dataVersion) {
|
||||
this(sectionRoot, dataVersion, LoadFlags.ALL_DATA);
|
||||
}
|
||||
|
||||
public Section(CompoundTag sectionRoot, int dataVersion, long loadFlags) {
|
||||
data = sectionRoot;
|
||||
this.dataVersion = dataVersion;
|
||||
ListTag<?> rawPalette = sectionRoot.getListTag("Palette");
|
||||
if (rawPalette == null) {
|
||||
return;
|
||||
}
|
||||
palette = rawPalette.asCompoundTagList();
|
||||
for (int i = 0; i < palette.size(); i++) {
|
||||
CompoundTag data = palette.get(i);
|
||||
putValueIndexedPalette(data, i);
|
||||
}
|
||||
|
||||
ByteArrayTag blockLight = sectionRoot.getByteArrayTag("BlockLight");
|
||||
LongArrayTag blockStates = sectionRoot.getLongArrayTag("BlockStates");
|
||||
ByteArrayTag skyLight = sectionRoot.getByteArrayTag("SkyLight");
|
||||
|
||||
if ((loadFlags & LoadFlags.BLOCK_LIGHTS) != 0) {
|
||||
this.blockLight = blockLight != null ? blockLight.getValue() : null;
|
||||
}
|
||||
if ((loadFlags & LoadFlags.BLOCK_STATES) != 0) {
|
||||
this.blockStates = blockStates != null ? blockStates.getValue() : null;
|
||||
}
|
||||
if ((loadFlags & LoadFlags.SKY_LIGHT) != 0) {
|
||||
this.skyLight = skyLight != null ? skyLight.getValue() : null;
|
||||
}
|
||||
}
|
||||
|
||||
Section() {
|
||||
}
|
||||
|
||||
void putValueIndexedPalette(CompoundTag data, int index) {
|
||||
PaletteIndex leaf = new PaletteIndex(data, index);
|
||||
String name = data.getString("Name");
|
||||
List<PaletteIndex> leaves = valueIndexedPalette.get(name);
|
||||
if (leaves == null) {
|
||||
leaves = new ArrayList<>(1);
|
||||
leaves.add(leaf);
|
||||
valueIndexedPalette.put(name, leaves);
|
||||
} else {
|
||||
for (PaletteIndex pal : leaves) {
|
||||
if (pal.data.equals(data)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
leaves.add(leaf);
|
||||
}
|
||||
}
|
||||
|
||||
PaletteIndex getValueIndexedPalette(CompoundTag data) {
|
||||
List<PaletteIndex> leaves = valueIndexedPalette.get(data.getString("Name"));
|
||||
if (leaves == null) {
|
||||
return null;
|
||||
}
|
||||
for (PaletteIndex leaf : leaves) {
|
||||
if (leaf.data.equals(data)) {
|
||||
return leaf;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
private static class PaletteIndex {
|
||||
|
||||
final CompoundTag data;
|
||||
final int index;
|
||||
|
||||
PaletteIndex(CompoundTag data, int index) {
|
||||
this.data = data;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data of this Section is empty.
|
||||
*
|
||||
* @return true if empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return data == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a block state based on a block location from this section.
|
||||
* The coordinates represent the location of the block inside of this Section.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block in this Section
|
||||
* @param blockY The y-coordinate of the block in this Section
|
||||
* @param blockZ The z-coordinate of the block in this Section
|
||||
* @return The block state data of this block.
|
||||
*/
|
||||
public CompoundTag getBlockStateAt(int blockX, int blockY, int blockZ) {
|
||||
try {
|
||||
int index = getBlockIndex(blockX, blockY, blockZ);
|
||||
int paletteIndex = getPaletteIndex(index);
|
||||
return palette.get(paletteIndex);
|
||||
} catch (Throwable ignored) {
|
||||
Iris.reportError(ignored);
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to add a block state for a specific block location in this Section.
|
||||
*
|
||||
* @param blockX The x-coordinate of the block in this Section
|
||||
* @param blockY The y-coordinate of the block in this Section
|
||||
* @param blockZ The z-coordinate of the block in this Section
|
||||
* @param state The block state to be set
|
||||
* @param cleanup When <code>true</code>, it will cleanup the palette of this section.
|
||||
* This option should only be used moderately to avoid unnecessary recalculation of the palette indices.
|
||||
* Recalculating the Palette should only be executed once right before saving the Section to file.
|
||||
*/
|
||||
public void setBlockStateAt(int blockX, int blockY, int blockZ, CompoundTag state, boolean cleanup) {
|
||||
int paletteSizeBefore = palette.size();
|
||||
int paletteIndex = addToPalette(state);
|
||||
//power of 2 --> bits must increase, but only if the palette size changed
|
||||
//otherwise we would attempt to update all blockstates and the entire palette
|
||||
//every time an existing blockstate was added while having 2^x blockstates in the palette
|
||||
if (paletteSizeBefore != palette.size() && (paletteIndex & (paletteIndex - 1)) == 0) {
|
||||
adjustBlockStateBits(null, blockStates);
|
||||
cleanup = true;
|
||||
}
|
||||
|
||||
setPaletteIndex(getBlockIndex(blockX, blockY, blockZ), paletteIndex, blockStates);
|
||||
|
||||
if (cleanup) {
|
||||
cleanupPaletteAndBlockStates();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the block data in the palette.
|
||||
*
|
||||
* @param blockStateIndex The index of the block in this section, ranging from 0-4095.
|
||||
* @return The index of the block data in the palette.
|
||||
*/
|
||||
public int getPaletteIndex(int blockStateIndex) {
|
||||
int bits = blockStates.length >> 6;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
double blockStatesIndex = blockStateIndex / (4096D / blockStates.length);
|
||||
int longIndex = (int) blockStatesIndex;
|
||||
int startBit = (int) ((blockStatesIndex - Math.floor(blockStatesIndex)) * 64D);
|
||||
if (startBit + bits > 64) {
|
||||
long prev = bitRange(blockStates[longIndex], startBit, 64);
|
||||
long next = bitRange(blockStates[longIndex + 1], 0, startBit + bits - 64);
|
||||
return (int) ((next << 64 - startBit) + prev);
|
||||
} else {
|
||||
return (int) bitRange(blockStates[longIndex], startBit, startBit + bits);
|
||||
}
|
||||
} else {
|
||||
int indicesPerLong = (int) (64D / bits);
|
||||
int blockStatesIndex = blockStateIndex / indicesPerLong;
|
||||
int startBit = (blockStateIndex % indicesPerLong) * bits;
|
||||
return (int) bitRange(blockStates[blockStatesIndex], startBit, startBit + bits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the block data in the BlockStates. Does not adjust the size of the BlockStates array.
|
||||
*
|
||||
* @param blockIndex The index of the block in this section, ranging from 0-4095.
|
||||
* @param paletteIndex The block state to be set (index of block data in the palette).
|
||||
* @param blockStates The block states to be updated.
|
||||
*/
|
||||
public void setPaletteIndex(int blockIndex, int paletteIndex, long[] blockStates) {
|
||||
int bits = blockStates.length >> 6;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
double blockStatesIndex = blockIndex / (4096D / blockStates.length);
|
||||
int longIndex = (int) blockStatesIndex;
|
||||
int startBit = (int) ((blockStatesIndex - Math.floor(longIndex)) * 64D);
|
||||
if (startBit + bits > 64) {
|
||||
blockStates[longIndex] = updateBits(blockStates[longIndex], paletteIndex, startBit, 64);
|
||||
blockStates[longIndex + 1] = updateBits(blockStates[longIndex + 1], paletteIndex, startBit - 64, startBit + bits - 64);
|
||||
} else {
|
||||
blockStates[longIndex] = updateBits(blockStates[longIndex], paletteIndex, startBit, startBit + bits);
|
||||
}
|
||||
} else {
|
||||
int indicesPerLong = (int) (64D / bits);
|
||||
int blockStatesIndex = blockIndex / indicesPerLong;
|
||||
int startBit = (blockIndex % indicesPerLong) * bits;
|
||||
blockStates[blockStatesIndex] = updateBits(blockStates[blockStatesIndex], paletteIndex, startBit, startBit + bits);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the palette of this Section.
|
||||
*
|
||||
* @return The palette of this Section.
|
||||
*/
|
||||
public ListTag<CompoundTag> getPalette() {
|
||||
return palette;
|
||||
}
|
||||
|
||||
int addToPalette(CompoundTag data) {
|
||||
PaletteIndex index;
|
||||
if ((index = getValueIndexedPalette(data)) != null) {
|
||||
return index.index;
|
||||
}
|
||||
palette.add(data);
|
||||
putValueIndexedPalette(data, palette.size() - 1);
|
||||
return palette.size() - 1;
|
||||
}
|
||||
|
||||
int getBlockIndex(int blockX, int blockY, int blockZ) {
|
||||
return (blockY & 0xF) * 256 + (blockZ & 0xF) * 16 + (blockX & 0xF);
|
||||
}
|
||||
|
||||
static long updateBits(long n, long m, int i, int j) {
|
||||
//replace i to j in n with j - i bits of m
|
||||
long mShifted = i > 0 ? (m & ((1L << j - i) - 1)) << i : (m & ((1L << j - i) - 1)) >>> -i;
|
||||
return ((n & ((j > 63 ? 0 : (~0L << j)) | (i < 0 ? 0 : ((1L << i) - 1L)))) | mShifted);
|
||||
}
|
||||
|
||||
static long bitRange(long value, int from, int to) {
|
||||
int waste = 64 - to;
|
||||
return (value << waste) >>> (waste + from);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method recalculates the palette and its indices.
|
||||
* This should only be used moderately to avoid unnecessary recalculation of the palette indices.
|
||||
* Recalculating the Palette should only be executed once right before saving the Section to file.
|
||||
*/
|
||||
public void cleanupPaletteAndBlockStates() {
|
||||
Map<Integer, Integer> oldToNewMapping = cleanupPalette();
|
||||
adjustBlockStateBits(oldToNewMapping, blockStates);
|
||||
}
|
||||
|
||||
private Map<Integer, Integer> cleanupPalette() {
|
||||
//create index - palette mapping
|
||||
Map<Integer, Integer> allIndices = new HashMap<>();
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
int paletteIndex = getPaletteIndex(i);
|
||||
allIndices.put(paletteIndex, paletteIndex);
|
||||
}
|
||||
//delete unused blocks from palette
|
||||
//start at index 1 because we need to keep minecraft:air
|
||||
int index = 1;
|
||||
valueIndexedPalette = new HashMap<>(valueIndexedPalette.size());
|
||||
putValueIndexedPalette(palette.get(0), 0);
|
||||
for (int i = 1; i < palette.size(); i++) {
|
||||
if (!allIndices.containsKey(index)) {
|
||||
palette.remove(i);
|
||||
i--;
|
||||
} else {
|
||||
putValueIndexedPalette(palette.get(i), i);
|
||||
allIndices.put(index, i);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return allIndices;
|
||||
}
|
||||
|
||||
void adjustBlockStateBits(Map<Integer, Integer> oldToNewMapping, long[] blockStates) {
|
||||
//increases or decreases the amount of bits used per BlockState
|
||||
//based on the size of the palette. oldToNewMapping can be used to update indices
|
||||
//if the palette had been cleaned up before using MCAFile#cleanupPalette().
|
||||
|
||||
int newBits = 32 - Integer.numberOfLeadingZeros(palette.size() - 1);
|
||||
newBits = Math.max(newBits, 4);
|
||||
|
||||
long[] newBlockStates;
|
||||
|
||||
if (dataVersion < 2527) {
|
||||
newBlockStates = newBits == blockStates.length / 64 ? blockStates : new long[newBits * 64];
|
||||
} else {
|
||||
int newLength = (int) Math.ceil(4096D / (64D / newBits));
|
||||
newBlockStates = newBits == blockStates.length / 64 ? blockStates : new long[newLength];
|
||||
}
|
||||
if (oldToNewMapping != null) {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
setPaletteIndex(i, oldToNewMapping.get(getPaletteIndex(i)), newBlockStates);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4096; i++) {
|
||||
setPaletteIndex(i, getPaletteIndex(i), newBlockStates);
|
||||
}
|
||||
}
|
||||
this.blockStates = newBlockStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The block light array of this Section
|
||||
*/
|
||||
public byte[] getBlockLight() {
|
||||
return blockLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the block light array for this section.
|
||||
*
|
||||
* @param blockLight The block light array
|
||||
* @throws IllegalArgumentException When the length of the array is not 2048
|
||||
*/
|
||||
public void setBlockLight(byte[] blockLight) {
|
||||
if (blockLight != null && blockLight.length != 2048) {
|
||||
throw new IllegalArgumentException("BlockLight array must have a length of 2048");
|
||||
}
|
||||
this.blockLight = blockLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The indices of the block states of this Section.
|
||||
*/
|
||||
public long[] getBlockStates() {
|
||||
return blockStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the block state indices to a custom value.
|
||||
*
|
||||
* @param blockStates The block state indices.
|
||||
* @throws NullPointerException If <code>blockStates</code> is <code>null</code>
|
||||
* @throws IllegalArgumentException When <code>blockStates</code>' length is < 256 or > 4096 and is not a multiple of 64
|
||||
*/
|
||||
public void setBlockStates(long[] blockStates) {
|
||||
if (blockStates == null) {
|
||||
throw new NullPointerException("BlockStates cannot be null");
|
||||
} else if (blockStates.length % 64 != 0 || blockStates.length < 256 || blockStates.length > 4096) {
|
||||
throw new IllegalArgumentException("BlockStates must have a length > 255 and < 4097 and must be divisible by 64");
|
||||
}
|
||||
this.blockStates = blockStates;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The sky light values of this Section
|
||||
*/
|
||||
public byte[] getSkyLight() {
|
||||
return skyLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sky light values of this section.
|
||||
*
|
||||
* @param skyLight The custom sky light values
|
||||
* @throws IllegalArgumentException If the length of the array is not 2048
|
||||
*/
|
||||
public void setSkyLight(byte[] skyLight) {
|
||||
if (skyLight != null && skyLight.length != 2048) {
|
||||
throw new IllegalArgumentException("SkyLight array must have a length of 2048");
|
||||
}
|
||||
this.skyLight = skyLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Section with base values.
|
||||
*
|
||||
* @return An empty Section
|
||||
*/
|
||||
public static Section newSection() {
|
||||
Section s = new Section();
|
||||
s.blockStates = new long[256];
|
||||
s.palette = new ListTag<>(CompoundTag.class);
|
||||
CompoundTag air = new CompoundTag();
|
||||
air.putString("Name", "minecraft:air");
|
||||
s.palette.add(air);
|
||||
s.data = new CompoundTag();
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the raw CompoundTag that this Section is based on.
|
||||
* This must be called before saving a Section to disk if the Section was manually created
|
||||
* to set the Y of this Section.
|
||||
*
|
||||
* @param y The Y-value of this Section
|
||||
* @return A reference to the raw CompoundTag this Section is based on
|
||||
*/
|
||||
public CompoundTag updateHandle(int y) {
|
||||
data.putByte("Y", (byte) y);
|
||||
if (palette != null) {
|
||||
data.put("Palette", palette);
|
||||
}
|
||||
if (blockLight != null) {
|
||||
data.putByteArray("BlockLight", blockLight);
|
||||
}
|
||||
if (blockStates != null) {
|
||||
data.putLongArray("BlockStates", blockStates);
|
||||
}
|
||||
if (skyLight != null) {
|
||||
data.putByteArray("SkyLight", skyLight);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.Deserializer;
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class NBTDeserializer implements Deserializer<NamedTag> {
|
||||
|
||||
private final boolean compressed;
|
||||
|
||||
public NBTDeserializer() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public NBTDeserializer(boolean compressed) {
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedTag fromStream(InputStream stream) throws IOException {
|
||||
NBTInputStream nbtIn;
|
||||
if (compressed) {
|
||||
nbtIn = new NBTInputStream(new GZIPInputStream(stream));
|
||||
} else {
|
||||
nbtIn = new NBTInputStream(stream);
|
||||
}
|
||||
return nbtIn.readTag(Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.ExceptionBiFunction;
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.engine.data.nbt.tag.*;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NBTInputStream extends DataInputStream implements MaxDepthIO {
|
||||
|
||||
private static final Map<Byte, ExceptionBiFunction<NBTInputStream, Integer, ? extends Tag<?>, IOException>> readers = new HashMap<>();
|
||||
private static final Map<Byte, Class<?>> idClassMapping = new HashMap<>();
|
||||
|
||||
static {
|
||||
put(EndTag.ID, (i, d) -> EndTag.INSTANCE, EndTag.class);
|
||||
put(ByteTag.ID, (i, d) -> readByte(i), ByteTag.class);
|
||||
put(ShortTag.ID, (i, d) -> readShort(i), ShortTag.class);
|
||||
put(IntTag.ID, (i, d) -> readInt(i), IntTag.class);
|
||||
put(LongTag.ID, (i, d) -> readLong(i), LongTag.class);
|
||||
put(FloatTag.ID, (i, d) -> readFloat(i), FloatTag.class);
|
||||
put(DoubleTag.ID, (i, d) -> readDouble(i), DoubleTag.class);
|
||||
put(ByteArrayTag.ID, (i, d) -> readByteArray(i), ByteArrayTag.class);
|
||||
put(StringTag.ID, (i, d) -> readString(i), StringTag.class);
|
||||
put(ListTag.ID, NBTInputStream::readListTag, ListTag.class);
|
||||
put(CompoundTag.ID, NBTInputStream::readCompound, CompoundTag.class);
|
||||
put(IntArrayTag.ID, (i, d) -> readIntArray(i), IntArrayTag.class);
|
||||
put(LongArrayTag.ID, (i, d) -> readLongArray(i), LongArrayTag.class);
|
||||
}
|
||||
|
||||
private static void put(byte id, ExceptionBiFunction<NBTInputStream, Integer, ? extends Tag<?>, IOException> reader, Class<?> clazz) {
|
||||
readers.put(id, reader);
|
||||
idClassMapping.put(id, clazz);
|
||||
}
|
||||
|
||||
public NBTInputStream(InputStream in) {
|
||||
super(in);
|
||||
}
|
||||
|
||||
public NamedTag readTag(int maxDepth) throws IOException {
|
||||
byte id = readByte();
|
||||
return new NamedTag(readUTF(), readTag(id, maxDepth));
|
||||
}
|
||||
|
||||
public Tag<?> readRawTag(int maxDepth) throws IOException {
|
||||
byte id = readByte();
|
||||
return readTag(id, maxDepth);
|
||||
}
|
||||
|
||||
private Tag<?> readTag(byte type, int maxDepth) throws IOException {
|
||||
ExceptionBiFunction<NBTInputStream, Integer, ? extends Tag<?>, IOException> f;
|
||||
if ((f = readers.get(type)) == null) {
|
||||
throw new IOException("invalid tag id \"" + type + "\"");
|
||||
}
|
||||
return f.accept(this, maxDepth);
|
||||
}
|
||||
|
||||
private static ByteTag readByte(NBTInputStream in) throws IOException {
|
||||
return new ByteTag(in.readByte());
|
||||
}
|
||||
|
||||
private static ShortTag readShort(NBTInputStream in) throws IOException {
|
||||
return new ShortTag(in.readShort());
|
||||
}
|
||||
|
||||
private static IntTag readInt(NBTInputStream in) throws IOException {
|
||||
return new IntTag(in.readInt());
|
||||
}
|
||||
|
||||
private static LongTag readLong(NBTInputStream in) throws IOException {
|
||||
return new LongTag(in.readLong());
|
||||
}
|
||||
|
||||
private static FloatTag readFloat(NBTInputStream in) throws IOException {
|
||||
return new FloatTag(in.readFloat());
|
||||
}
|
||||
|
||||
private static DoubleTag readDouble(NBTInputStream in) throws IOException {
|
||||
return new DoubleTag(in.readDouble());
|
||||
}
|
||||
|
||||
private static StringTag readString(NBTInputStream in) throws IOException {
|
||||
return new StringTag(in.readUTF());
|
||||
}
|
||||
|
||||
private static ByteArrayTag readByteArray(NBTInputStream in) throws IOException {
|
||||
ByteArrayTag bat = new ByteArrayTag(new byte[in.readInt()]);
|
||||
in.readFully(bat.getValue());
|
||||
return bat;
|
||||
}
|
||||
|
||||
private static IntArrayTag readIntArray(NBTInputStream in) throws IOException {
|
||||
int l = in.readInt();
|
||||
int[] data = new int[l];
|
||||
IntArrayTag iat = new IntArrayTag(data);
|
||||
for (int i = 0; i < l; i++) {
|
||||
data[i] = in.readInt();
|
||||
}
|
||||
return iat;
|
||||
}
|
||||
|
||||
private static LongArrayTag readLongArray(NBTInputStream in) throws IOException {
|
||||
int l = in.readInt();
|
||||
long[] data = new long[l];
|
||||
LongArrayTag iat = new LongArrayTag(data);
|
||||
for (int i = 0; i < l; i++) {
|
||||
data[i] = in.readLong();
|
||||
}
|
||||
return iat;
|
||||
}
|
||||
|
||||
private static ListTag<?> readListTag(NBTInputStream in, int maxDepth) throws IOException {
|
||||
byte listType = in.readByte();
|
||||
ListTag<?> list = ListTag.createUnchecked(idClassMapping.get(listType));
|
||||
int length = in.readInt();
|
||||
if (length < 0) {
|
||||
length = 0;
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
list.addUnchecked(in.readTag(listType, in.decrementMaxDepth(maxDepth)));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static CompoundTag readCompound(NBTInputStream in, int maxDepth) throws IOException {
|
||||
CompoundTag comp = new CompoundTag();
|
||||
for (int id = in.readByte() & 0xFF; id != 0; id = in.readByte() & 0xFF) {
|
||||
String key = in.readUTF();
|
||||
Tag<?> element = in.readTag((byte) id, in.decrementMaxDepth(maxDepth));
|
||||
comp.put(key, element);
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.ExceptionTriConsumer;
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.engine.data.nbt.tag.*;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class NBTOutputStream extends DataOutputStream implements MaxDepthIO {
|
||||
|
||||
private static final Map<Byte, ExceptionTriConsumer<NBTOutputStream, Tag<?>, Integer, IOException>> writers = new HashMap<>();
|
||||
private static final Map<Class<?>, Byte> classIdMapping = new HashMap<>();
|
||||
|
||||
static {
|
||||
put(EndTag.ID, (o, t, d) -> {
|
||||
}, EndTag.class);
|
||||
put(ByteTag.ID, (o, t, d) -> writeByte(o, t), ByteTag.class);
|
||||
put(ShortTag.ID, (o, t, d) -> writeShort(o, t), ShortTag.class);
|
||||
put(IntTag.ID, (o, t, d) -> writeInt(o, t), IntTag.class);
|
||||
put(LongTag.ID, (o, t, d) -> writeLong(o, t), LongTag.class);
|
||||
put(FloatTag.ID, (o, t, d) -> writeFloat(o, t), FloatTag.class);
|
||||
put(DoubleTag.ID, (o, t, d) -> writeDouble(o, t), DoubleTag.class);
|
||||
put(ByteArrayTag.ID, (o, t, d) -> writeByteArray(o, t), ByteArrayTag.class);
|
||||
put(StringTag.ID, (o, t, d) -> writeString(o, t), StringTag.class);
|
||||
put(ListTag.ID, NBTOutputStream::writeList, ListTag.class);
|
||||
put(CompoundTag.ID, NBTOutputStream::writeCompound, CompoundTag.class);
|
||||
put(IntArrayTag.ID, (o, t, d) -> writeIntArray(o, t), IntArrayTag.class);
|
||||
put(LongArrayTag.ID, (o, t, d) -> writeLongArray(o, t), LongArrayTag.class);
|
||||
}
|
||||
|
||||
private static void put(byte id, ExceptionTriConsumer<NBTOutputStream, Tag<?>, Integer, IOException> f, Class<?> clazz) {
|
||||
writers.put(id, f);
|
||||
classIdMapping.put(clazz, id);
|
||||
}
|
||||
|
||||
public NBTOutputStream(OutputStream out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
public void writeTag(NamedTag tag, int maxDepth) throws IOException {
|
||||
writeByte(tag.getTag().getID());
|
||||
if (tag.getTag().getID() != 0) {
|
||||
writeUTF(tag.getName() == null ? "" : tag.getName());
|
||||
}
|
||||
writeRawTag(tag.getTag(), maxDepth);
|
||||
}
|
||||
|
||||
public void writeTag(Tag<?> tag, int maxDepth) throws IOException {
|
||||
writeByte(tag.getID());
|
||||
if (tag.getID() != 0) {
|
||||
writeUTF("");
|
||||
}
|
||||
writeRawTag(tag, maxDepth);
|
||||
}
|
||||
|
||||
public void writeRawTag(Tag<?> tag, int maxDepth) throws IOException {
|
||||
ExceptionTriConsumer<NBTOutputStream, Tag<?>, Integer, IOException> f;
|
||||
if ((f = writers.get(tag.getID())) == null) {
|
||||
throw new IOException("invalid tag \"" + tag.getID() + "\"");
|
||||
}
|
||||
f.accept(this, tag, maxDepth);
|
||||
}
|
||||
|
||||
static byte idFromClass(Class<?> clazz) {
|
||||
Byte id = classIdMapping.get(clazz);
|
||||
if (id == null) {
|
||||
throw new IllegalArgumentException("unknown Tag class " + clazz.getName());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void writeByte(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeByte(((ByteTag) tag).asByte());
|
||||
}
|
||||
|
||||
private static void writeShort(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeShort(((ShortTag) tag).asShort());
|
||||
}
|
||||
|
||||
private static void writeInt(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((IntTag) tag).asInt());
|
||||
}
|
||||
|
||||
private static void writeLong(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeLong(((LongTag) tag).asLong());
|
||||
}
|
||||
|
||||
private static void writeFloat(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeFloat(((FloatTag) tag).asFloat());
|
||||
}
|
||||
|
||||
private static void writeDouble(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeDouble(((DoubleTag) tag).asDouble());
|
||||
}
|
||||
|
||||
private static void writeString(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeUTF(((StringTag) tag).getValue());
|
||||
}
|
||||
|
||||
private static void writeByteArray(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((ByteArrayTag) tag).length());
|
||||
out.write(((ByteArrayTag) tag).getValue());
|
||||
}
|
||||
|
||||
private static void writeIntArray(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((IntArrayTag) tag).length());
|
||||
for (int i : ((IntArrayTag) tag).getValue()) {
|
||||
out.writeInt(i);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLongArray(NBTOutputStream out, Tag<?> tag) throws IOException {
|
||||
out.writeInt(((LongArrayTag) tag).length());
|
||||
for (long l : ((LongArrayTag) tag).getValue()) {
|
||||
out.writeLong(l);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeList(NBTOutputStream out, Tag<?> tag, int maxDepth) throws IOException {
|
||||
out.writeByte(idFromClass(((ListTag<?>) tag).getTypeClass()));
|
||||
out.writeInt(((ListTag<?>) tag).size());
|
||||
for (Tag<?> t : ((ListTag<?>) tag)) {
|
||||
out.writeRawTag(t, out.decrementMaxDepth(maxDepth));
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeCompound(NBTOutputStream out, Tag<?> tag, int maxDepth) throws IOException {
|
||||
for (Map.Entry<String, Tag<?>> entry : (CompoundTag) tag) {
|
||||
if (entry.getValue().getID() == 0) {
|
||||
throw new IOException("end tag not allowed");
|
||||
}
|
||||
out.writeByte(entry.getValue().getID());
|
||||
out.writeUTF(entry.getKey());
|
||||
out.writeRawTag(entry.getValue(), out.decrementMaxDepth(maxDepth));
|
||||
}
|
||||
out.writeByte(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.Serializer;
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class NBTSerializer implements Serializer<NamedTag> {
|
||||
|
||||
private final boolean compressed;
|
||||
|
||||
public NBTSerializer() {
|
||||
this(true);
|
||||
}
|
||||
|
||||
public NBTSerializer(boolean compressed) {
|
||||
this.compressed = compressed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toStream(NamedTag object, OutputStream out) throws IOException {
|
||||
NBTOutputStream nbtOut;
|
||||
if (compressed) {
|
||||
nbtOut = new NBTOutputStream(new GZIPOutputStream(out, true));
|
||||
} else {
|
||||
nbtOut = new NBTOutputStream(out);
|
||||
}
|
||||
nbtOut.writeTag(object, Tag.DEFAULT_MAX_DEPTH);
|
||||
nbtOut.flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public final class NBTUtil {
|
||||
|
||||
private NBTUtil() {
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, File file, boolean compressed) throws IOException {
|
||||
try (FileOutputStream fos = new FileOutputStream(file)) {
|
||||
new NBTSerializer(compressed).toStream(tag, fos);
|
||||
}
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, String file, boolean compressed) throws IOException {
|
||||
write(tag, new File(file), compressed);
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, File file) throws IOException {
|
||||
write(tag, file, true);
|
||||
}
|
||||
|
||||
public static void write(NamedTag tag, String file) throws IOException {
|
||||
write(tag, new File(file), true);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, File file, boolean compressed) throws IOException {
|
||||
write(new NamedTag(null, tag), file, compressed);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, String file, boolean compressed) throws IOException {
|
||||
write(new NamedTag(null, tag), new File(file), compressed);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, File file) throws IOException {
|
||||
write(new NamedTag(null, tag), file, true);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, String file) throws IOException {
|
||||
write(new NamedTag(null, tag), new File(file), true);
|
||||
}
|
||||
|
||||
public static NamedTag read(File file, boolean compressed) throws IOException {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
return new NBTDeserializer(compressed).fromStream(fis);
|
||||
}
|
||||
}
|
||||
|
||||
public static NamedTag read(String file, boolean compressed) throws IOException {
|
||||
return read(new File(file), compressed);
|
||||
}
|
||||
|
||||
public static NamedTag read(File file) throws IOException {
|
||||
try (FileInputStream fis = new FileInputStream(file)) {
|
||||
return new NBTDeserializer(false).fromStream(detectDecompression(fis));
|
||||
}
|
||||
}
|
||||
|
||||
public static NamedTag read(String file) throws IOException {
|
||||
return read(new File(file));
|
||||
}
|
||||
|
||||
private static InputStream detectDecompression(InputStream is) throws IOException {
|
||||
PushbackInputStream pbis = new PushbackInputStream(is, 2);
|
||||
int signature = (pbis.read() & 0xFF) + (pbis.read() << 8);
|
||||
pbis.unread(signature >> 8);
|
||||
pbis.unread(signature & 0xFF);
|
||||
if (signature == GZIPInputStream.GZIP_MAGIC) {
|
||||
return new GZIPInputStream(pbis);
|
||||
}
|
||||
return pbis;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
public class NamedTag {
|
||||
|
||||
private String name;
|
||||
private Tag<?> tag;
|
||||
|
||||
public NamedTag(String name, Tag<?> tag) {
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setTag(Tag<?> tag) {
|
||||
this.tag = tag;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Tag<?> getTag() {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ParseException extends IOException {
|
||||
|
||||
public ParseException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public ParseException(String msg, String value, int index) {
|
||||
super(msg + " at: " + formatError(value, index));
|
||||
}
|
||||
|
||||
private static String formatError(String value, int index) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int i = Math.min(value.length(), index);
|
||||
if (i > 35) {
|
||||
builder.append("...");
|
||||
}
|
||||
builder.append(value, Math.max(0, i - 35), i);
|
||||
builder.append("<--[HERE]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.StringDeserializer;
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SNBTDeserializer implements StringDeserializer<Tag<?>> {
|
||||
|
||||
@Override
|
||||
public Tag<?> fromReader(Reader reader) throws IOException {
|
||||
return fromReader(reader, Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
public Tag<?> fromReader(Reader reader, int maxDepth) throws IOException {
|
||||
BufferedReader bufferedReader;
|
||||
if (reader instanceof BufferedReader) {
|
||||
bufferedReader = (BufferedReader) reader;
|
||||
} else {
|
||||
bufferedReader = new BufferedReader(reader);
|
||||
}
|
||||
return SNBTParser.parse(bufferedReader.lines().collect(Collectors.joining()), maxDepth);
|
||||
}
|
||||
}
|
||||
251
src/main/java/com/volmit/iris/engine/data/nbt/io/SNBTParser.java
Normal file
251
src/main/java/com/volmit/iris/engine/data/nbt/io/SNBTParser.java
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.engine.data.nbt.tag.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class SNBTParser implements MaxDepthIO {
|
||||
|
||||
private static final Pattern
|
||||
FLOAT_LITERAL_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[-+]?\\d+)?f$", Pattern.CASE_INSENSITIVE),
|
||||
DOUBLE_LITERAL_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.?|\\d*\\.\\d+)(?:e[-+]?\\d+)?d$", Pattern.CASE_INSENSITIVE),
|
||||
DOUBLE_LITERAL_NO_SUFFIX_PATTERN = Pattern.compile("^[-+]?(?:\\d+\\.|\\d*\\.\\d+)(?:e[-+]?\\d+)?$", Pattern.CASE_INSENSITIVE),
|
||||
BYTE_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+b$", Pattern.CASE_INSENSITIVE),
|
||||
SHORT_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+s$", Pattern.CASE_INSENSITIVE),
|
||||
INT_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+$", Pattern.CASE_INSENSITIVE),
|
||||
LONG_LITERAL_PATTERN = Pattern.compile("^[-+]?\\d+l$", Pattern.CASE_INSENSITIVE),
|
||||
NUMBER_PATTERN = Pattern.compile("^[-+]?\\d+$");
|
||||
|
||||
private final StringPointer ptr;
|
||||
|
||||
private SNBTParser(String string) {
|
||||
this.ptr = new StringPointer(string);
|
||||
}
|
||||
|
||||
public static Tag<?> parse(String string, int maxDepth) throws ParseException {
|
||||
SNBTParser parser = new SNBTParser(string);
|
||||
Tag<?> tag = parser.parseAnything(maxDepth);
|
||||
parser.ptr.skipWhitespace();
|
||||
if (parser.ptr.hasNext()) {
|
||||
throw parser.ptr.parseException("invalid characters after end of snbt");
|
||||
}
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static Tag<?> parse(String string) throws ParseException {
|
||||
return parse(string, Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
private Tag<?> parseAnything(int maxDepth) throws ParseException {
|
||||
ptr.skipWhitespace();
|
||||
switch (ptr.currentChar()) {
|
||||
case '{':
|
||||
return parseCompoundTag(maxDepth);
|
||||
case '[':
|
||||
if (ptr.hasCharsLeft(2) && ptr.lookAhead(1) != '"' && ptr.lookAhead(2) == ';') {
|
||||
return parseNumArray();
|
||||
}
|
||||
return parseListTag(maxDepth);
|
||||
}
|
||||
return parseStringOrLiteral();
|
||||
}
|
||||
|
||||
private Tag<?> parseStringOrLiteral() throws ParseException {
|
||||
ptr.skipWhitespace();
|
||||
if (ptr.currentChar() == '"') {
|
||||
return new StringTag(ptr.parseQuotedString());
|
||||
}
|
||||
String s = ptr.parseSimpleString();
|
||||
if (s.isEmpty()) {
|
||||
throw new ParseException("expected non empty value");
|
||||
}
|
||||
if (FLOAT_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
return new FloatTag(Float.parseFloat(s.substring(0, s.length() - 1)));
|
||||
} else if (BYTE_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new ByteTag(Byte.parseByte(s.substring(0, s.length() - 1)));
|
||||
} catch (NumberFormatException ex) {
|
||||
Iris.reportError(ex);
|
||||
throw ptr.parseException("byte not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (SHORT_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new ShortTag(Short.parseShort(s.substring(0, s.length() - 1)));
|
||||
} catch (NumberFormatException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException("short not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (LONG_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new LongTag(Long.parseLong(s.substring(0, s.length() - 1)));
|
||||
} catch (NumberFormatException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException("long not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (INT_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
return new IntTag(Integer.parseInt(s));
|
||||
} catch (NumberFormatException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException("int not in range: \"" + s.substring(0, s.length() - 1) + "\"");
|
||||
}
|
||||
} else if (DOUBLE_LITERAL_PATTERN.matcher(s).matches()) {
|
||||
return new DoubleTag(Double.parseDouble(s.substring(0, s.length() - 1)));
|
||||
} else if (DOUBLE_LITERAL_NO_SUFFIX_PATTERN.matcher(s).matches()) {
|
||||
return new DoubleTag(Double.parseDouble(s));
|
||||
} else if ("true".equalsIgnoreCase(s)) {
|
||||
return new ByteTag(true);
|
||||
} else if ("false".equalsIgnoreCase(s)) {
|
||||
return new ByteTag(false);
|
||||
}
|
||||
return new StringTag(s);
|
||||
}
|
||||
|
||||
private CompoundTag parseCompoundTag(int maxDepth) throws ParseException {
|
||||
ptr.expectChar('{');
|
||||
|
||||
CompoundTag compoundTag = new CompoundTag();
|
||||
|
||||
ptr.skipWhitespace();
|
||||
while (ptr.hasNext() && ptr.currentChar() != '}') {
|
||||
ptr.skipWhitespace();
|
||||
String key = ptr.currentChar() == '"' ? ptr.parseQuotedString() : ptr.parseSimpleString();
|
||||
if (key.isEmpty()) {
|
||||
throw new ParseException("empty keys are not allowed");
|
||||
}
|
||||
ptr.expectChar(':');
|
||||
|
||||
compoundTag.put(key, parseAnything(decrementMaxDepth(maxDepth)));
|
||||
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar('}');
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
private ListTag<?> parseListTag(int maxDepth) throws ParseException {
|
||||
ptr.expectChar('[');
|
||||
ptr.skipWhitespace();
|
||||
ListTag<?> list = ListTag.createUnchecked(EndTag.class);
|
||||
while (ptr.currentChar() != ']') {
|
||||
Tag<?> element = parseAnything(decrementMaxDepth(maxDepth));
|
||||
try {
|
||||
list.addUnchecked(element);
|
||||
} catch (IllegalArgumentException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException(ex.getMessage());
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
return list;
|
||||
}
|
||||
|
||||
private ArrayTag<?> parseNumArray() throws ParseException {
|
||||
ptr.expectChar('[');
|
||||
char arrayType = ptr.next();
|
||||
ptr.expectChar(';');
|
||||
ptr.skipWhitespace();
|
||||
switch (arrayType) {
|
||||
case 'B':
|
||||
return parseByteArrayTag();
|
||||
case 'I':
|
||||
return parseIntArrayTag();
|
||||
case 'L':
|
||||
return parseLongArrayTag();
|
||||
}
|
||||
throw new ParseException("invalid array type '" + arrayType + "'");
|
||||
}
|
||||
|
||||
private ByteArrayTag parseByteArrayTag() throws ParseException {
|
||||
List<Byte> byteList = new ArrayList<>();
|
||||
while (ptr.currentChar() != ']') {
|
||||
String s = ptr.parseSimpleString();
|
||||
ptr.skipWhitespace();
|
||||
if (NUMBER_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
byteList.add(Byte.parseByte(s));
|
||||
} catch (NumberFormatException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException("byte not in range: \"" + s + "\"");
|
||||
}
|
||||
} else {
|
||||
throw ptr.parseException("invalid byte in ByteArrayTag: \"" + s + "\"");
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
byte[] bytes = new byte[byteList.size()];
|
||||
for (int i = 0; i < byteList.size(); i++) {
|
||||
bytes[i] = byteList.get(i);
|
||||
}
|
||||
return new ByteArrayTag(bytes);
|
||||
}
|
||||
|
||||
private IntArrayTag parseIntArrayTag() throws ParseException {
|
||||
List<Integer> intList = new ArrayList<>();
|
||||
while (ptr.currentChar() != ']') {
|
||||
String s = ptr.parseSimpleString();
|
||||
ptr.skipWhitespace();
|
||||
if (NUMBER_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
intList.add(Integer.parseInt(s));
|
||||
} catch (NumberFormatException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException("int not in range: \"" + s + "\"");
|
||||
}
|
||||
} else {
|
||||
throw ptr.parseException("invalid int in IntArrayTag: \"" + s + "\"");
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
return new IntArrayTag(intList.stream().mapToInt(i -> i).toArray());
|
||||
}
|
||||
|
||||
private LongArrayTag parseLongArrayTag() throws ParseException {
|
||||
List<Long> longList = new ArrayList<>();
|
||||
while (ptr.currentChar() != ']') {
|
||||
String s = ptr.parseSimpleString();
|
||||
ptr.skipWhitespace();
|
||||
if (NUMBER_PATTERN.matcher(s).matches()) {
|
||||
try {
|
||||
longList.add(Long.parseLong(s));
|
||||
} catch (NumberFormatException ex) {Iris.reportError(ex);
|
||||
throw ptr.parseException("long not in range: \"" + s + "\"");
|
||||
}
|
||||
} else {
|
||||
throw ptr.parseException("invalid long in LongArrayTag: \"" + s + "\"");
|
||||
}
|
||||
if (!ptr.nextArrayElement()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr.expectChar(']');
|
||||
return new LongArrayTag(longList.stream().mapToLong(l -> l).toArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.StringSerializer;
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
public class SNBTSerializer implements StringSerializer<Tag<?>> {
|
||||
|
||||
@Override
|
||||
public void toWriter(Tag<?> tag, Writer writer) throws IOException {
|
||||
SNBTWriter.write(tag, writer);
|
||||
}
|
||||
|
||||
public void toWriter(Tag<?> tag, Writer writer, int maxDepth) throws IOException {
|
||||
SNBTWriter.write(tag, writer, maxDepth);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.nbt.tag.Tag;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SNBTUtil {
|
||||
|
||||
public static String toSNBT(Tag<?> tag) throws IOException {
|
||||
return new SNBTSerializer().toString(tag);
|
||||
}
|
||||
|
||||
public static Tag<?> fromSNBT(String string) throws IOException {
|
||||
return new SNBTDeserializer().fromString(string);
|
||||
}
|
||||
}
|
||||
135
src/main/java/com/volmit/iris/engine/data/nbt/io/SNBTWriter.java
Normal file
135
src/main/java/com/volmit/iris/engine/data/nbt/io/SNBTWriter.java
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.engine.data.nbt.tag.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* SNBTWriter creates an SNBT String.
|
||||
*/
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
public final class SNBTWriter implements MaxDepthIO {
|
||||
|
||||
private static final Pattern NON_QUOTE_PATTERN = Pattern.compile("[a-zA-Z_.+\\-]+");
|
||||
|
||||
private final Writer writer;
|
||||
|
||||
private SNBTWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, Writer writer, int maxDepth) throws IOException {
|
||||
new SNBTWriter(writer).writeAnything(tag, maxDepth);
|
||||
}
|
||||
|
||||
public static void write(Tag<?> tag, Writer writer) throws IOException {
|
||||
write(tag, writer, Tag.DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
private void writeAnything(Tag<?> tag, int maxDepth) throws IOException {
|
||||
switch (tag.getID()) {
|
||||
case EndTag.ID:
|
||||
//do nothing
|
||||
break;
|
||||
case ByteTag.ID:
|
||||
writer.append(Byte.toString(((ByteTag) tag).asByte())).write('b');
|
||||
break;
|
||||
case ShortTag.ID:
|
||||
writer.append(Short.toString(((ShortTag) tag).asShort())).write('s');
|
||||
break;
|
||||
case IntTag.ID:
|
||||
writer.write(Integer.toString(((IntTag) tag).asInt()));
|
||||
break;
|
||||
case LongTag.ID:
|
||||
writer.append(Long.toString(((LongTag) tag).asLong())).write('l');
|
||||
break;
|
||||
case FloatTag.ID:
|
||||
writer.append(Float.toString(((FloatTag) tag).asFloat())).write('f');
|
||||
break;
|
||||
case DoubleTag.ID:
|
||||
writer.append(Double.toString(((DoubleTag) tag).asDouble())).write('d');
|
||||
break;
|
||||
case ByteArrayTag.ID:
|
||||
writeArray(((ByteArrayTag) tag).getValue(), ((ByteArrayTag) tag).length(), "B");
|
||||
break;
|
||||
case StringTag.ID:
|
||||
writer.write(escapeString(((StringTag) tag).getValue()));
|
||||
break;
|
||||
case ListTag.ID:
|
||||
writer.write('[');
|
||||
for (int i = 0; i < ((ListTag<?>) tag).size(); i++) {
|
||||
writer.write(i == 0 ? "" : ",");
|
||||
writeAnything(((ListTag<?>) tag).get(i), decrementMaxDepth(maxDepth));
|
||||
}
|
||||
writer.write(']');
|
||||
break;
|
||||
case CompoundTag.ID:
|
||||
writer.write('{');
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Tag<?>> entry : (CompoundTag) tag) {
|
||||
writer.write(first ? "" : ",");
|
||||
writer.append(escapeString(entry.getKey())).write(':');
|
||||
writeAnything(entry.getValue(), decrementMaxDepth(maxDepth));
|
||||
first = false;
|
||||
}
|
||||
writer.write('}');
|
||||
break;
|
||||
case IntArrayTag.ID:
|
||||
writeArray(((IntArrayTag) tag).getValue(), ((IntArrayTag) tag).length(), "I");
|
||||
break;
|
||||
case LongArrayTag.ID:
|
||||
writeArray(((LongArrayTag) tag).getValue(), ((LongArrayTag) tag).length(), "L");
|
||||
break;
|
||||
default:
|
||||
throw new IOException("unknown tag with id \"" + tag.getID() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void writeArray(Object array, int length, String prefix) throws IOException {
|
||||
writer.append('[').append(prefix).write(';');
|
||||
for (int i = 0; i < length; i++) {
|
||||
writer.append(i == 0 ? "" : ",").write(Array.get(array, i).toString());
|
||||
}
|
||||
writer.write(']');
|
||||
}
|
||||
|
||||
public static String escapeString(String s) {
|
||||
if (!NON_QUOTE_PATTERN.matcher(s).matches()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('"');
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '\\' || c == '"') {
|
||||
sb.append('\\');
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append('"');
|
||||
return sb.toString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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.data.nbt.io;
|
||||
|
||||
public class StringPointer {
|
||||
|
||||
private final String value;
|
||||
private int index;
|
||||
|
||||
public StringPointer(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String parseSimpleString() {
|
||||
int oldIndex = index;
|
||||
while (hasNext() && isSimpleChar(currentChar())) {
|
||||
index++;
|
||||
}
|
||||
return value.substring(oldIndex, index);
|
||||
}
|
||||
|
||||
public String parseQuotedString() throws ParseException {
|
||||
int oldIndex = ++index; //ignore beginning quotes
|
||||
StringBuilder sb = null;
|
||||
boolean escape = false;
|
||||
while (hasNext()) {
|
||||
char c = next();
|
||||
if (escape) {
|
||||
if (c != '\\' && c != '"') {
|
||||
throw parseException("invalid escape of '" + c + "'");
|
||||
}
|
||||
escape = false;
|
||||
} else {
|
||||
if (c == '\\') { //escape
|
||||
escape = true;
|
||||
if (sb != null) {
|
||||
continue;
|
||||
}
|
||||
sb = new StringBuilder(value.substring(oldIndex, index - 1));
|
||||
continue;
|
||||
}
|
||||
if (c == '"') {
|
||||
return sb == null ? value.substring(oldIndex, index - 1) : sb.toString();
|
||||
}
|
||||
}
|
||||
if (sb != null) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
throw parseException("missing end quote");
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public boolean nextArrayElement() {
|
||||
skipWhitespace();
|
||||
if (hasNext() && currentChar() == ',') {
|
||||
index++;
|
||||
skipWhitespace();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void expectChar(char c) throws ParseException {
|
||||
skipWhitespace();
|
||||
boolean hasNext = hasNext();
|
||||
if (hasNext && currentChar() == c) {
|
||||
index++;
|
||||
return;
|
||||
}
|
||||
throw parseException("expected '" + c + "' but got " + (hasNext ? "'" + currentChar() + "'" : "EOF"));
|
||||
}
|
||||
|
||||
public void skipWhitespace() {
|
||||
while (hasNext() && Character.isWhitespace(currentChar())) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return index < value.length();
|
||||
}
|
||||
|
||||
public boolean hasCharsLeft(int num) {
|
||||
return this.index + num < value.length();
|
||||
}
|
||||
|
||||
public char currentChar() {
|
||||
return value.charAt(index);
|
||||
}
|
||||
|
||||
public char next() {
|
||||
return value.charAt(index++);
|
||||
}
|
||||
|
||||
public void skip(int offset) {
|
||||
index += offset;
|
||||
}
|
||||
|
||||
public char lookAhead(int offset) {
|
||||
return value.charAt(index + offset);
|
||||
}
|
||||
|
||||
private static boolean isSimpleChar(char c) {
|
||||
return c >= 'a' && c <= 'z'
|
||||
|| c >= 'A' && c <= 'Z'
|
||||
|| c >= '0' && c <= '9'
|
||||
|| c == '-'
|
||||
|| c == '+'
|
||||
|| c == '.'
|
||||
|| c == '_';
|
||||
}
|
||||
|
||||
public ParseException parseException(String msg) {
|
||||
return new ParseException(msg, value, index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
|
||||
/**
|
||||
* ArrayTag is an abstract representation of any NBT array tag.
|
||||
* For implementations see {@link ByteArrayTag}, {@link IntArrayTag}, {@link LongArrayTag}.
|
||||
*
|
||||
* @param <T> The array type.
|
||||
*/
|
||||
public abstract class ArrayTag<T> extends Tag<T> {
|
||||
|
||||
public ArrayTag(T value) {
|
||||
super(value);
|
||||
if (!value.getClass().isArray()) {
|
||||
throw new UnsupportedOperationException("type of array tag must be an array");
|
||||
}
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return Array.getLength(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(T value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return arrayToString("", "");
|
||||
}
|
||||
|
||||
protected String arrayToString(@SuppressWarnings("SameParameterValue") String prefix, @SuppressWarnings("SameParameterValue") String suffix) {
|
||||
StringBuilder sb = new StringBuilder("[").append(prefix).append("".equals(prefix) ? "" : ";");
|
||||
for (int i = 0; i < length(); i++) {
|
||||
sb.append(i == 0 ? "" : ",").append(Array.get(getValue(), i)).append(suffix);
|
||||
}
|
||||
sb.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ByteArrayTag extends ArrayTag<byte[]> implements Comparable<ByteArrayTag> {
|
||||
|
||||
public static final byte ID = 7;
|
||||
public static final byte[] ZERO_VALUE = new byte[0];
|
||||
|
||||
public ByteArrayTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public ByteArrayTag(byte[] value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && Arrays.equals(getValue(), ((ByteArrayTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ByteArrayTag other) {
|
||||
return Integer.compare(length(), other.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteArrayTag clone() {
|
||||
return new ByteArrayTag(Arrays.copyOf(getValue(), length()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class ByteTag extends NumberTag<Byte> implements Comparable<ByteTag> {
|
||||
|
||||
public static final byte ID = 1;
|
||||
public static final byte ZERO_VALUE = 0;
|
||||
|
||||
public ByteTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public ByteTag(byte value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
public ByteTag(boolean value) {
|
||||
super((byte) (value ? 1 : 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public boolean asBoolean() {
|
||||
return getValue() > 0;
|
||||
}
|
||||
|
||||
public void setValue(byte value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asByte() == ((ByteTag) other).asByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ByteTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteTag clone() {
|
||||
return new ByteTag(getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.KMap;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
public class CompoundTag extends Tag<Map<String, Tag<?>>> implements Iterable<Map.Entry<String, Tag<?>>>, Comparable<CompoundTag>, MaxDepthIO {
|
||||
|
||||
public static final byte ID = 10;
|
||||
|
||||
public CompoundTag() {
|
||||
super(createEmptyValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
private static Map<String, Tag<?>> createEmptyValue() {
|
||||
return new KMap<>();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return getValue().size();
|
||||
}
|
||||
|
||||
public Tag<?> remove(String key) {
|
||||
return getValue().remove(key);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
getValue().clear();
|
||||
}
|
||||
|
||||
public boolean containsKey(String key) {
|
||||
return getValue().containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Tag<?> value) {
|
||||
return getValue().containsValue(value);
|
||||
}
|
||||
|
||||
public Collection<Tag<?>> values() {
|
||||
return getValue().values();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return getValue().keySet();
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String, Tag<?>>> entrySet() {
|
||||
return new NonNullEntrySet<>(getValue().entrySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, Tag<?>>> iterator() {
|
||||
return entrySet().iterator();
|
||||
}
|
||||
|
||||
public void forEach(BiConsumer<String, Tag<?>> action) {
|
||||
getValue().forEach(action);
|
||||
}
|
||||
|
||||
public <C extends Tag<?>> C get(String key, Class<C> type) {
|
||||
Tag<?> t = getValue().get(key);
|
||||
if (t != null) {
|
||||
return type.cast(t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Tag<?> get(String key) {
|
||||
return getValue().get(key);
|
||||
}
|
||||
|
||||
public ByteTag getByteTag(String key) {
|
||||
return get(key, ByteTag.class);
|
||||
}
|
||||
|
||||
public ShortTag getShortTag(String key) {
|
||||
return get(key, ShortTag.class);
|
||||
}
|
||||
|
||||
public IntTag getIntTag(String key) {
|
||||
return get(key, IntTag.class);
|
||||
}
|
||||
|
||||
public LongTag getLongTag(String key) {
|
||||
return get(key, LongTag.class);
|
||||
}
|
||||
|
||||
public FloatTag getFloatTag(String key) {
|
||||
return get(key, FloatTag.class);
|
||||
}
|
||||
|
||||
public DoubleTag getDoubleTag(String key) {
|
||||
return get(key, DoubleTag.class);
|
||||
}
|
||||
|
||||
public StringTag getStringTag(String key) {
|
||||
return get(key, StringTag.class);
|
||||
}
|
||||
|
||||
public ByteArrayTag getByteArrayTag(String key) {
|
||||
return get(key, ByteArrayTag.class);
|
||||
}
|
||||
|
||||
public IntArrayTag getIntArrayTag(String key) {
|
||||
return get(key, IntArrayTag.class);
|
||||
}
|
||||
|
||||
public LongArrayTag getLongArrayTag(String key) {
|
||||
return get(key, LongArrayTag.class);
|
||||
}
|
||||
|
||||
public ListTag<?> getListTag(String key) {
|
||||
return get(key, ListTag.class);
|
||||
}
|
||||
|
||||
public CompoundTag getCompoundTag(String key) {
|
||||
return get(key, CompoundTag.class);
|
||||
}
|
||||
|
||||
public boolean getBoolean(String key) {
|
||||
Tag<?> t = get(key);
|
||||
return t instanceof ByteTag && ((ByteTag) t).asByte() > 0;
|
||||
}
|
||||
|
||||
public byte getByte(String key) {
|
||||
ByteTag t = getByteTag(key);
|
||||
return t == null ? ByteTag.ZERO_VALUE : t.asByte();
|
||||
}
|
||||
|
||||
public short getShort(String key) {
|
||||
ShortTag t = getShortTag(key);
|
||||
return t == null ? ShortTag.ZERO_VALUE : t.asShort();
|
||||
}
|
||||
|
||||
public int getInt(String key) {
|
||||
IntTag t = getIntTag(key);
|
||||
return t == null ? IntTag.ZERO_VALUE : t.asInt();
|
||||
}
|
||||
|
||||
public long getLong(String key) {
|
||||
LongTag t = getLongTag(key);
|
||||
return t == null ? LongTag.ZERO_VALUE : t.asLong();
|
||||
}
|
||||
|
||||
public float getFloat(String key) {
|
||||
FloatTag t = getFloatTag(key);
|
||||
return t == null ? FloatTag.ZERO_VALUE : t.asFloat();
|
||||
}
|
||||
|
||||
public double getDouble(String key) {
|
||||
DoubleTag t = getDoubleTag(key);
|
||||
return t == null ? DoubleTag.ZERO_VALUE : t.asDouble();
|
||||
}
|
||||
|
||||
public String getString(String key) {
|
||||
StringTag t = getStringTag(key);
|
||||
return t == null ? StringTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public byte[] getByteArray(String key) {
|
||||
ByteArrayTag t = getByteArrayTag(key);
|
||||
return t == null ? ByteArrayTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public int[] getIntArray(String key) {
|
||||
IntArrayTag t = getIntArrayTag(key);
|
||||
return t == null ? IntArrayTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public long[] getLongArray(String key) {
|
||||
LongArrayTag t = getLongArrayTag(key);
|
||||
return t == null ? LongArrayTag.ZERO_VALUE : t.getValue();
|
||||
}
|
||||
|
||||
public Tag<?> put(String key, Tag<?> tag) {
|
||||
return getValue().put(Objects.requireNonNull(key), Objects.requireNonNull(tag));
|
||||
}
|
||||
|
||||
public Tag<?> putBoolean(String key, boolean value) {
|
||||
return put(key, new ByteTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putByte(String key, byte value) {
|
||||
return put(key, new ByteTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putShort(String key, short value) {
|
||||
return put(key, new ShortTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putInt(String key, int value) {
|
||||
return put(key, new IntTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putLong(String key, long value) {
|
||||
return put(key, new LongTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putFloat(String key, float value) {
|
||||
return put(key, new FloatTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putDouble(String key, double value) {
|
||||
return put(key, new DoubleTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putString(String key, String value) {
|
||||
return put(key, new StringTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putByteArray(String key, byte[] value) {
|
||||
return put(key, new ByteArrayTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putIntArray(String key, int[] value) {
|
||||
return put(key, new IntArrayTag(value));
|
||||
}
|
||||
|
||||
public Tag<?> putLongArray(String key, long[] value) {
|
||||
return put(key, new LongArrayTag(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
|
||||
sb.append(first ? "" : ",")
|
||||
.append(escapeString(e.getKey(), false)).append(":")
|
||||
.append(e.getValue().toString(decrementMaxDepth(maxDepth)));
|
||||
first = false;
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other) || size() != ((CompoundTag) other).size()) {
|
||||
return false;
|
||||
}
|
||||
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
|
||||
Tag<?> v;
|
||||
if ((v = ((CompoundTag) other).get(e.getKey())) == null || !e.getValue().equals(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CompoundTag o) {
|
||||
return Integer.compare(size(), o.getValue().size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag clone() {
|
||||
CompoundTag copy = new CompoundTag();
|
||||
for (Map.Entry<String, Tag<?>> e : getValue().entrySet()) {
|
||||
copy.put(e.getKey(), e.getValue().clone());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class DoubleTag extends NumberTag<Double> implements Comparable<DoubleTag> {
|
||||
|
||||
public static final byte ID = 6;
|
||||
public static final double ZERO_VALUE = 0.0D;
|
||||
|
||||
public DoubleTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public DoubleTag(double value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(double value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && getValue().equals(((DoubleTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(DoubleTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoubleTag clone() {
|
||||
return new DoubleTag(getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public final class EndTag extends Tag<Void> {
|
||||
|
||||
public static final byte ID = 0;
|
||||
public static final EndTag INSTANCE = new EndTag();
|
||||
|
||||
private EndTag() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void checkValue(Void value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return "\"end\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public EndTag clone() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class FloatTag extends NumberTag<Float> implements Comparable<FloatTag> {
|
||||
|
||||
public static final byte ID = 5;
|
||||
public static final float ZERO_VALUE = 0.0F;
|
||||
|
||||
public FloatTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public FloatTag(float value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(float value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && getValue().equals(((FloatTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(FloatTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatTag clone() {
|
||||
return new FloatTag(getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class IntArrayTag extends ArrayTag<int[]> implements Comparable<IntArrayTag> {
|
||||
|
||||
public static final byte ID = 11;
|
||||
public static final int[] ZERO_VALUE = new int[0];
|
||||
|
||||
public IntArrayTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public IntArrayTag(int[] value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && Arrays.equals(getValue(), ((IntArrayTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IntArrayTag other) {
|
||||
return Integer.compare(length(), other.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntArrayTag clone() {
|
||||
return new IntArrayTag(Arrays.copyOf(getValue(), length()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class IntTag extends NumberTag<Integer> implements Comparable<IntTag> {
|
||||
|
||||
public static final byte ID = 3;
|
||||
public static final int ZERO_VALUE = 0;
|
||||
|
||||
public IntTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public IntTag(int value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asInt() == ((IntTag) other).asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(IntTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntTag clone() {
|
||||
return new IntTag(getValue());
|
||||
}
|
||||
}
|
||||
343
src/main/java/com/volmit/iris/engine/data/nbt/tag/ListTag.java
Normal file
343
src/main/java/com/volmit/iris/engine/data/nbt/tag/ListTag.java
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthIO;
|
||||
import com.volmit.iris.util.KList;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ListTag represents a typed List in the nbt structure.
|
||||
* An empty {@link ListTag} created using {@link ListTag#createUnchecked(Class)} will be of unknown type
|
||||
* and returns an {@link EndTag}{@code .class} in {@link ListTag#getTypeClass()}.
|
||||
* The type of an empty untyped {@link ListTag} can be set by using any of the {@code add()}
|
||||
* methods or any of the {@code as...List()} methods.
|
||||
*/
|
||||
@SuppressWarnings("ALL")
|
||||
public class ListTag<T extends Tag<?>> extends Tag<List<T>> implements Iterable<T>, Comparable<ListTag<T>>, MaxDepthIO {
|
||||
|
||||
public static final byte ID = 9;
|
||||
|
||||
private Class<?> typeClass = null;
|
||||
|
||||
private ListTag() {
|
||||
super(createEmptyValue(3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates a non-type-safe ListTag. Its element type will be set after the first
|
||||
* element was added.</p>
|
||||
*
|
||||
* <p>This is an internal helper method for cases where the element type is not known
|
||||
* at construction time. Use {@link #ListTag(Class)} when the type is known.</p>
|
||||
*
|
||||
* @return A new non-type-safe ListTag
|
||||
*/
|
||||
public static ListTag<?> createUnchecked(Class<?> typeClass) {
|
||||
ListTag<?> list = new ListTag<>();
|
||||
list.typeClass = typeClass;
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Creates an empty mutable list to be used as empty value of ListTags.</p>
|
||||
*
|
||||
* @param <T> Type of the list elements
|
||||
* @param initialCapacity The initial capacity of the returned List
|
||||
* @return An instance of {@link List} with an initial capacity of 3
|
||||
*/
|
||||
private static <T> List<T> createEmptyValue(@SuppressWarnings("SameParameterValue") int initialCapacity) {
|
||||
return new KList<>(initialCapacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param typeClass The exact class of the elements
|
||||
* @throws IllegalArgumentException When {@code typeClass} is {@link EndTag}{@code .class}
|
||||
* @throws NullPointerException When {@code typeClass} is {@code null}
|
||||
*/
|
||||
public ListTag(Class<? super T> typeClass) throws IllegalArgumentException, NullPointerException {
|
||||
super(createEmptyValue(3));
|
||||
if (typeClass == EndTag.class) {
|
||||
throw new IllegalArgumentException("cannot create ListTag with EndTag elements");
|
||||
}
|
||||
this.typeClass = Objects.requireNonNull(typeClass);
|
||||
}
|
||||
|
||||
public Class<?> getTypeClass() {
|
||||
return typeClass == null ? EndTag.class : typeClass;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return getValue().size();
|
||||
}
|
||||
|
||||
public T remove(int index) {
|
||||
return getValue().remove(index);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
getValue().clear();
|
||||
}
|
||||
|
||||
public boolean contains(T t) {
|
||||
return getValue().contains(t);
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<Tag<?>> tags) {
|
||||
return getValue().containsAll(tags);
|
||||
}
|
||||
|
||||
public void sort(Comparator<T> comparator) {
|
||||
getValue().sort(comparator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return getValue().iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<? super T> action) {
|
||||
getValue().forEach(action);
|
||||
}
|
||||
|
||||
public T set(int index, T t) {
|
||||
return getValue().set(index, Objects.requireNonNull(t));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Tag to this ListTag after the last index.
|
||||
*
|
||||
* @param t The element to be added.
|
||||
*/
|
||||
public void add(T t) {
|
||||
add(size(), t);
|
||||
}
|
||||
|
||||
public void add(int index, T t) {
|
||||
Objects.requireNonNull(t);
|
||||
if (typeClass == null || typeClass == EndTag.class) {
|
||||
typeClass = t.getClass();
|
||||
} else if (typeClass != t.getClass()) {
|
||||
throw new ClassCastException(
|
||||
String.format("cannot add %s to ListTag<%s>",
|
||||
t.getClass().getSimpleName(),
|
||||
typeClass.getSimpleName()));
|
||||
}
|
||||
getValue().add(index, t);
|
||||
}
|
||||
|
||||
public void addAll(Collection<T> t) {
|
||||
for (T tt : t) {
|
||||
add(tt);
|
||||
}
|
||||
}
|
||||
|
||||
public void addAll(int index, Collection<T> t) {
|
||||
int i = 0;
|
||||
for (T tt : t) {
|
||||
add(index + i, tt);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public void addBoolean(boolean value) {
|
||||
addUnchecked(new ByteTag(value));
|
||||
}
|
||||
|
||||
public void addByte(byte value) {
|
||||
addUnchecked(new ByteTag(value));
|
||||
}
|
||||
|
||||
public void addShort(short value) {
|
||||
addUnchecked(new ShortTag(value));
|
||||
}
|
||||
|
||||
public void addInt(int value) {
|
||||
addUnchecked(new IntTag(value));
|
||||
}
|
||||
|
||||
public void addLong(long value) {
|
||||
addUnchecked(new LongTag(value));
|
||||
}
|
||||
|
||||
public void addFloat(float value) {
|
||||
addUnchecked(new FloatTag(value));
|
||||
}
|
||||
|
||||
public void addDouble(double value) {
|
||||
addUnchecked(new DoubleTag(value));
|
||||
}
|
||||
|
||||
public void addString(String value) {
|
||||
addUnchecked(new StringTag(value));
|
||||
}
|
||||
|
||||
public void addByteArray(byte[] value) {
|
||||
addUnchecked(new ByteArrayTag(value));
|
||||
}
|
||||
|
||||
public void addIntArray(int[] value) {
|
||||
addUnchecked(new IntArrayTag(value));
|
||||
}
|
||||
|
||||
public void addLongArray(long[] value) {
|
||||
addUnchecked(new LongArrayTag(value));
|
||||
}
|
||||
|
||||
public T get(int index) {
|
||||
return getValue().get(index);
|
||||
}
|
||||
|
||||
public int indexOf(T t) {
|
||||
return getValue().indexOf(t);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <L extends Tag<?>> ListTag<L> asTypedList(Class<L> type) {
|
||||
checkTypeClass(type);
|
||||
typeClass = type;
|
||||
return (ListTag<L>) this;
|
||||
}
|
||||
|
||||
public ListTag<ByteTag> asByteTagList() {
|
||||
return asTypedList(ByteTag.class);
|
||||
}
|
||||
|
||||
public ListTag<ShortTag> asShortTagList() {
|
||||
return asTypedList(ShortTag.class);
|
||||
}
|
||||
|
||||
public ListTag<IntTag> asIntTagList() {
|
||||
return asTypedList(IntTag.class);
|
||||
}
|
||||
|
||||
public ListTag<LongTag> asLongTagList() {
|
||||
return asTypedList(LongTag.class);
|
||||
}
|
||||
|
||||
public ListTag<FloatTag> asFloatTagList() {
|
||||
return asTypedList(FloatTag.class);
|
||||
}
|
||||
|
||||
public ListTag<DoubleTag> asDoubleTagList() {
|
||||
return asTypedList(DoubleTag.class);
|
||||
}
|
||||
|
||||
public ListTag<StringTag> asStringTagList() {
|
||||
return asTypedList(StringTag.class);
|
||||
}
|
||||
|
||||
public ListTag<ByteArrayTag> asByteArrayTagList() {
|
||||
return asTypedList(ByteArrayTag.class);
|
||||
}
|
||||
|
||||
public ListTag<IntArrayTag> asIntArrayTagList() {
|
||||
return asTypedList(IntArrayTag.class);
|
||||
}
|
||||
|
||||
public ListTag<LongArrayTag> asLongArrayTagList() {
|
||||
return asTypedList(LongArrayTag.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListTag<ListTag<?>> asListTagList() {
|
||||
checkTypeClass(ListTag.class);
|
||||
typeClass = ListTag.class;
|
||||
return (ListTag<ListTag<?>>) this;
|
||||
}
|
||||
|
||||
public ListTag<CompoundTag> asCompoundTagList() {
|
||||
return asTypedList(CompoundTag.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
StringBuilder sb = new StringBuilder("{\"type\":\"").append(getTypeClass().getSimpleName()).append("\",\"list\":[");
|
||||
for (int i = 0; i < size(); i++) {
|
||||
sb.append(i > 0 ? "," : "").append(get(i).valueToString(decrementMaxDepth(maxDepth)));
|
||||
}
|
||||
sb.append("]}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!super.equals(other) || size() != ((ListTag<?>) other).size() || getTypeClass() != ((ListTag<?>) other).getTypeClass()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < size(); i++) {
|
||||
if (!get(i).equals(((ListTag<?>) other).get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getTypeClass().hashCode(), getValue().hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ListTag<T> o) {
|
||||
return Integer.compare(size(), o.getValue().size());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public ListTag<T> clone() {
|
||||
ListTag<T> copy = new ListTag<>();
|
||||
// assure type safety for clone
|
||||
copy.typeClass = typeClass;
|
||||
for (T t : getValue()) {
|
||||
copy.add((T) t.clone());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
//TODO: make private
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addUnchecked(Tag<?> tag) {
|
||||
if (typeClass != null && typeClass != tag.getClass() && typeClass != EndTag.class) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
"cannot add %s to ListTag<%s>",
|
||||
tag.getClass().getSimpleName(), typeClass.getSimpleName()));
|
||||
}
|
||||
add(size(), (T) tag);
|
||||
}
|
||||
|
||||
private void checkTypeClass(Class<?> clazz) {
|
||||
if (typeClass != null && typeClass != EndTag.class && typeClass != clazz) {
|
||||
throw new ClassCastException(String.format(
|
||||
"cannot cast ListTag<%s> to ListTag<%s>",
|
||||
typeClass.getSimpleName(), clazz.getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class LongArrayTag extends ArrayTag<long[]> implements Comparable<LongArrayTag> {
|
||||
|
||||
public static final byte ID = 12;
|
||||
public static final long[] ZERO_VALUE = new long[0];
|
||||
|
||||
public LongArrayTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public LongArrayTag(long[] value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && Arrays.equals(getValue(), ((LongArrayTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LongArrayTag other) {
|
||||
return Integer.compare(length(), other.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongArrayTag clone() {
|
||||
return new LongArrayTag(Arrays.copyOf(getValue(), length()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class LongTag extends NumberTag<Long> implements Comparable<LongTag> {
|
||||
|
||||
public static final byte ID = 4;
|
||||
public static final long ZERO_VALUE = 0L;
|
||||
|
||||
public LongTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public LongTag(long value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(long value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asLong() == ((LongTag) other).asLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LongTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongTag clone() {
|
||||
return new LongTag(getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A decorator for the Set returned by CompoundTag#entrySet()
|
||||
* that disallows setting null values.
|
||||
*/
|
||||
@SuppressWarnings("ClassCanBeRecord")
|
||||
class NonNullEntrySet<K, V> implements Set<Map.Entry<K, V>> {
|
||||
|
||||
private final Set<Map.Entry<K, V>> set;
|
||||
|
||||
NonNullEntrySet(Set<Map.Entry<K, V>> set) {
|
||||
this.set = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return set.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return set.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new NonNullEntrySetIterator(set.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] toArray() {
|
||||
return set.toArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T[] toArray(@NotNull T[] a) {
|
||||
return set.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(Map.Entry<K, V> kvEntry) {
|
||||
return set.add(kvEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return set.remove(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> c) {
|
||||
return set.containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends Map.Entry<K, V>> c) {
|
||||
return set.addAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
return set.retainAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
return set.removeAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
set.clear();
|
||||
}
|
||||
|
||||
class NonNullEntrySetIterator implements Iterator<Map.Entry<K, V>> {
|
||||
|
||||
private final Iterator<Map.Entry<K, V>> iterator;
|
||||
|
||||
NonNullEntrySetIterator(Iterator<Map.Entry<K, V>> iterator) {
|
||||
this.iterator = iterator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return iterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, V> next() {
|
||||
return new NonNullEntry(iterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
class NonNullEntry implements Map.Entry<K, V> {
|
||||
|
||||
private final Map.Entry<K, V> entry;
|
||||
|
||||
NonNullEntry(Map.Entry<K, V> entry) {
|
||||
this.entry = entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getKey() {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue() {
|
||||
return entry.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V setValue(V value) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException(getClass().getSimpleName() + " does not allow setting null");
|
||||
}
|
||||
return entry.setValue(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("EqualsWhichDoesntCheckParameterClass")
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return entry.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return entry.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public abstract class NumberTag<T extends Number & Comparable<T>> extends Tag<T> {
|
||||
|
||||
public NumberTag(T value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
public byte asByte() {
|
||||
return getValue().byteValue();
|
||||
}
|
||||
|
||||
public short asShort() {
|
||||
return getValue().shortValue();
|
||||
}
|
||||
|
||||
public int asInt() {
|
||||
return getValue().intValue();
|
||||
}
|
||||
|
||||
public long asLong() {
|
||||
return getValue().longValue();
|
||||
}
|
||||
|
||||
public float asFloat() {
|
||||
return getValue().floatValue();
|
||||
}
|
||||
|
||||
public double asDouble() {
|
||||
return getValue().doubleValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return getValue().toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class ShortTag extends NumberTag<Short> implements Comparable<ShortTag> {
|
||||
|
||||
public static final byte ID = 2;
|
||||
public static final short ZERO_VALUE = 0;
|
||||
|
||||
public ShortTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public ShortTag(short value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public void setValue(short value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && asShort() == ((ShortTag) other).asShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ShortTag other) {
|
||||
return getValue().compareTo(other.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShortTag clone() {
|
||||
return new ShortTag(getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
public class StringTag extends Tag<String> implements Comparable<StringTag> {
|
||||
|
||||
public static final byte ID = 8;
|
||||
public static final String ZERO_VALUE = "";
|
||||
|
||||
public StringTag() {
|
||||
super(ZERO_VALUE);
|
||||
}
|
||||
|
||||
public StringTag(String value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getID() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return super.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String valueToString(int maxDepth) {
|
||||
return escapeString(getValue(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return super.equals(other) && getValue().equals(((StringTag) other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(StringTag o) {
|
||||
return getValue().compareTo(o.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StringTag clone() {
|
||||
return new StringTag(getValue());
|
||||
}
|
||||
}
|
||||
219
src/main/java/com/volmit/iris/engine/data/nbt/tag/Tag.java
Normal file
219
src/main/java/com/volmit/iris/engine/data/nbt/tag/Tag.java
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.data.nbt.tag;
|
||||
|
||||
import com.volmit.iris.engine.data.io.MaxDepthReachedException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Base class for all NBT tags.
|
||||
*
|
||||
* <h1>Nesting</h1>
|
||||
* <p>All methods serializing instances or deserializing data track the nesting levels to prevent
|
||||
* circular references or malicious data which could, when deserialized, result in thousands
|
||||
* of instances causing a denial of service.</p>
|
||||
*
|
||||
* <p>These methods have a parameter for the maximum nesting depth they are allowed to traverse. A
|
||||
* value of {@code 0} means that only the object itself, but no nested objects may be processed.
|
||||
* If an instance is nested further than allowed, a {@link MaxDepthReachedException} will be thrown.
|
||||
* Providing a negative maximum nesting depth will cause an {@code IllegalArgumentException}
|
||||
* to be thrown.</p>
|
||||
*
|
||||
* <p>Some methods do not provide a parameter to specify the maximum nesting depth, but instead use
|
||||
* {@link #DEFAULT_MAX_DEPTH}, which is also the maximum used by Minecraft. This is documented for
|
||||
* the respective methods.</p>
|
||||
*
|
||||
* <p>If custom NBT tags contain objects other than NBT tags, which can be nested as well, then there
|
||||
* is no guarantee that {@code MaxDepthReachedException}s are thrown for them. The respective class
|
||||
* will document this behavior accordingly.</p>
|
||||
*
|
||||
* @param <T> The type of the contained value
|
||||
*/
|
||||
public abstract class Tag<T> implements Cloneable {
|
||||
|
||||
/**
|
||||
* The default maximum depth of the NBT structure.
|
||||
*/
|
||||
public static final int DEFAULT_MAX_DEPTH = 512;
|
||||
|
||||
private static final Map<String, String> ESCAPE_CHARACTERS;
|
||||
|
||||
static {
|
||||
final Map<String, String> temp = new HashMap<>();
|
||||
temp.put("\\", "\\\\\\\\");
|
||||
temp.put("\n", "\\\\n");
|
||||
temp.put("\t", "\\\\t");
|
||||
temp.put("\r", "\\\\r");
|
||||
temp.put("\"", "\\\\\"");
|
||||
//noinspection Java9CollectionFactory
|
||||
ESCAPE_CHARACTERS = Collections.unmodifiableMap(temp);
|
||||
}
|
||||
|
||||
private static final Pattern ESCAPE_PATTERN = Pattern.compile("[\\\\\n\t\r\"]");
|
||||
private static final Pattern NON_QUOTE_PATTERN = Pattern.compile("[a-zA-Z0-9_\\-+]+");
|
||||
|
||||
private T value;
|
||||
|
||||
/**
|
||||
* Initializes this Tag with some value. If the value is {@code null}, it will
|
||||
* throw a {@code NullPointerException}
|
||||
*
|
||||
* @param value The value to be set for this Tag.
|
||||
*/
|
||||
public Tag(T value) {
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This Tag's ID, usually used for serialization and deserialization.
|
||||
*/
|
||||
public abstract byte getID();
|
||||
|
||||
/**
|
||||
* @return The value of this Tag.
|
||||
*/
|
||||
protected T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for this Tag directly.
|
||||
*
|
||||
* @param value The value to be set.
|
||||
* @throws NullPointerException If the value is null
|
||||
*/
|
||||
protected void setValue(T value) {
|
||||
this.value = checkValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the value {@code value} is {@code null}.
|
||||
*
|
||||
* @param value The value to check
|
||||
* @return The parameter {@code value}
|
||||
* @throws NullPointerException If {@code value} was {@code null}
|
||||
*/
|
||||
protected T checkValue(T value) {
|
||||
return Objects.requireNonNull(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link Tag#toString(int)} with an initial depth of {@code 0}.
|
||||
*
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
* @see Tag#toString(int)
|
||||
*/
|
||||
@Override
|
||||
public final String toString() {
|
||||
return toString(DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a string representation of this Tag in a valid JSON format.
|
||||
*
|
||||
* @param maxDepth The maximum nesting depth.
|
||||
* @return The string representation of this Tag.
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
*/
|
||||
public String toString(int maxDepth) {
|
||||
return "{\"type\":\"" + getClass().getSimpleName() + "\"," +
|
||||
"\"value\":" + valueToString(maxDepth) + "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@link Tag#valueToString(int)} with {@link Tag#DEFAULT_MAX_DEPTH}.
|
||||
*
|
||||
* @return The string representation of the value of this Tag.
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
*/
|
||||
public String valueToString() {
|
||||
return valueToString(DEFAULT_MAX_DEPTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON representation of the value of this Tag.
|
||||
*
|
||||
* @param maxDepth The maximum nesting depth.
|
||||
* @return The string representation of the value of this Tag.
|
||||
* @throws MaxDepthReachedException If the maximum nesting depth is exceeded.
|
||||
*/
|
||||
public abstract String valueToString(int maxDepth);
|
||||
|
||||
/**
|
||||
* Returns whether this Tag and some other Tag are equal.
|
||||
* They are equal if {@code other} is not {@code null} and they are of the same class.
|
||||
* Custom Tag implementations should overwrite this but check the result
|
||||
* of this {@code super}-method while comparing.
|
||||
*
|
||||
* @param other The Tag to compare to.
|
||||
* @return {@code true} if they are equal based on the conditions mentioned above.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other != null && getClass() == other.getClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash code of this Tag. Tags which are equal according to {@link Tag#equals(Object)}
|
||||
* must return an equal hash code.
|
||||
*
|
||||
* @return The hash code of this Tag.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a clone of this Tag.
|
||||
*
|
||||
* @return A clone of this Tag.
|
||||
*/
|
||||
public abstract Tag<T> clone();
|
||||
|
||||
/**
|
||||
* Escapes a string to fit into a JSON-like string representation for Minecraft
|
||||
* or to create the JSON string representation of a Tag returned from {@link Tag#toString()}
|
||||
*
|
||||
* @param s The string to be escaped.
|
||||
* @param lenient {@code true} if it should force double quotes ({@code "}) at the start and
|
||||
* the end of the string.
|
||||
* @return The escaped string.
|
||||
*/
|
||||
@SuppressWarnings("StringBufferMayBeStringBuilder")
|
||||
protected static String escapeString(String s, @SuppressWarnings("SameParameterValue") boolean lenient) {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
Matcher m = ESCAPE_PATTERN.matcher(s);
|
||||
while (m.find()) {
|
||||
m.appendReplacement(sb, ESCAPE_CHARACTERS.get(m.group()));
|
||||
}
|
||||
m.appendTail(sb);
|
||||
m = NON_QUOTE_PATTERN.matcher(s);
|
||||
if (!lenient || !m.matches()) {
|
||||
sb.insert(0, "\"").append("\"");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.generator.decorator;
|
||||
|
||||
import com.volmit.iris.object.DecorationPart;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDecorator;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisCeilingDecorator extends IrisEngineDecorator {
|
||||
public IrisCeilingDecorator(Engine engine) {
|
||||
super(engine, "Ceiling", DecorationPart.CEILING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
IrisDecorator decorator = getDecorator(biome, realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, height, z, decorator.getBlockData100(biome, getRng(), realX, realZ, getData()));
|
||||
}
|
||||
} else {
|
||||
int stack = decorator.getHeight(getRng().nextParallelRNG(Cache.key(realX, realZ)), realX, realZ, getData());
|
||||
stack = Math.min(max + 1, stack);
|
||||
|
||||
BlockData top = decorator.getBlockDataForTop(biome, getRng(), realX, realZ, getData());
|
||||
BlockData fill = decorator.getBlockData100(biome, getRng(), realX, realZ, getData());
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
if (height - i < 0 || height - i > getEngine().getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = (((double) i) / (double) (stack - 1));
|
||||
data.set(x, height - i, z, threshold >= decorator.getTopThreshold() ? top : fill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.generator.decorator;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.object.DecorationPart;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDecorator;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.framework.EngineAssignedComponent;
|
||||
import com.volmit.iris.engine.framework.EngineDecorator;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import lombok.Getter;
|
||||
|
||||
public abstract class IrisEngineDecorator extends EngineAssignedComponent implements EngineDecorator {
|
||||
|
||||
@Getter
|
||||
private final RNG rng;
|
||||
|
||||
@Getter
|
||||
private final DecorationPart part;
|
||||
|
||||
public IrisEngineDecorator(Engine engine, String name, DecorationPart part) {
|
||||
super(engine, name + " Decorator");
|
||||
this.part = part;
|
||||
this.rng = new RNG(getSeed() + 29356788 - (part.ordinal() * 10439677L));
|
||||
}
|
||||
|
||||
protected IrisDecorator getDecorator(IrisBiome biome, double realX, double realZ) {
|
||||
KList<IrisDecorator> v = new KList<>();
|
||||
RNG rng = new RNG(Cache.key((int) realX, (int) realZ));
|
||||
|
||||
for (IrisDecorator i : biome.getDecorators()) {
|
||||
try {
|
||||
if (i.getPartOf().equals(part) && i.getBlockData(biome, this.rng, realX, realZ, getData()) != null) {
|
||||
v.add(i);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.error("PART OF: " + biome.getLoadFile().getAbsolutePath() + " HAS AN INVALID DECORATOR near 'partOf'!!!");
|
||||
}
|
||||
}
|
||||
|
||||
if (v.isNotEmpty()) {
|
||||
return v.get(rng.nextInt(v.size()));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.generator.decorator;
|
||||
|
||||
import com.volmit.iris.object.DecorationPart;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDecorator;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisSeaFloorDecorator extends IrisEngineDecorator {
|
||||
public IrisSeaFloorDecorator(Engine engine) {
|
||||
super(engine, "Sea Floor", DecorationPart.SEA_FLOOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
if (height <= getDimension().getFluidHeight()) {
|
||||
|
||||
IrisDecorator decorator = getDecorator(biome, realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, height, z, decorator.getBlockData100(biome, getRng(), realX, realZ, getData()));
|
||||
}
|
||||
} else {
|
||||
int stack = decorator.getHeight(getRng().nextParallelRNG(Cache.key(realX, realZ)), realX, realZ, getData());
|
||||
stack = Math.min(stack, getDimension().getFluidHeight() - height + 2);
|
||||
|
||||
BlockData top = decorator.getBlockDataForTop(biome, getRng(), realX, realZ, getData());
|
||||
BlockData fill = decorator.getBlockData100(biome, getRng(), realX, realZ, getData());
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
if (height - i < 0 || height - i > getEngine().getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (height + i > getDimension().getFluidHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, height + i, z, threshold >= decorator.getTopThreshold() ? top : fill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.generator.decorator;
|
||||
|
||||
import com.volmit.iris.object.DecorationPart;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDecorator;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisSeaSurfaceDecorator extends IrisEngineDecorator {
|
||||
public IrisSeaSurfaceDecorator(Engine engine) {
|
||||
super(engine, "Sea Surface", DecorationPart.SEA_SURFACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
IrisDecorator decorator = getDecorator(biome, realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
if (height >= 0 || height < getEngine().getHeight()) {
|
||||
data.set(x, getDimension().getFluidHeight() + 1, z, decorator.getBlockData100(biome, getRng(), realX, realZ, getData()));
|
||||
}
|
||||
} else {
|
||||
int stack = decorator.getHeight(getRng().nextParallelRNG(Cache.key(realX, realZ)), realX, realZ, getData());
|
||||
|
||||
BlockData top = decorator.getBlockDataForTop(biome, getRng(), realX, realZ, getData());
|
||||
BlockData fill = decorator.getBlockData100(biome, getRng(), realX, realZ, getData());
|
||||
for (int i = 0; i < stack; i++) {
|
||||
if (height - i < 0 || height - i > getEngine().getHeight()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, getDimension().getFluidHeight() + 1 + i, z, threshold >= decorator.getTopThreshold() ? top : fill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.generator.decorator;
|
||||
|
||||
import com.volmit.iris.object.DecorationPart;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDecorator;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisShoreLineDecorator extends IrisEngineDecorator {
|
||||
public IrisShoreLineDecorator(Engine engine) {
|
||||
super(engine, "Shore Line", DecorationPart.SHORE_LINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
|
||||
if (height == getDimension().getFluidHeight()) {
|
||||
if (Math.round(getComplex().getHeightStream().get(realX1, realZ)) < getComplex().getFluidHeight() ||
|
||||
Math.round(getComplex().getHeightStream().get(realX_1, realZ)) < getComplex().getFluidHeight() ||
|
||||
Math.round(getComplex().getHeightStream().get(realX, realZ1)) < getComplex().getFluidHeight() ||
|
||||
Math.round(getComplex().getHeightStream().get(realX, realZ_1)) < getComplex().getFluidHeight()
|
||||
) {
|
||||
IrisDecorator decorator = getDecorator(biome, realX, realZ);
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
data.set(x, height + 1, z, decorator.getBlockData100(biome, getRng(), realX, realZ, getData()));
|
||||
} else {
|
||||
int stack = decorator.getHeight(getRng().nextParallelRNG(Cache.key(realX, realZ)), realX, realZ, getData());
|
||||
|
||||
BlockData top = decorator.getBlockDataForTop(biome, getRng(), realX, realZ, getData());
|
||||
BlockData fill = decorator.getBlockData100(biome, getRng(), realX, realZ, getData());
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
data.set(x, height + 1 + i, z, threshold >= decorator.getTopThreshold() ? top : fill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.generator.decorator;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.object.DecorationPart;
|
||||
import com.volmit.iris.object.InferredType;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDecorator;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import org.bukkit.block.data.Bisected;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public class IrisSurfaceDecorator extends IrisEngineDecorator {
|
||||
public IrisSurfaceDecorator(Engine engine) {
|
||||
super(engine, "Surface", DecorationPart.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
if (biome.getInferredType().equals(InferredType.SHORE) && height < getDimension().getFluidHeight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockData bd, bdx;
|
||||
IrisDecorator decorator = getDecorator(biome, realX, realZ);
|
||||
bdx = data.get(x, height, z);
|
||||
boolean underwater = height < getDimension().getFluidHeight();
|
||||
|
||||
if (decorator != null) {
|
||||
if (!decorator.isStacking()) {
|
||||
bd = decorator.getBlockData100(biome, getRng(), realX, realZ, getData());
|
||||
|
||||
if (!underwater) {
|
||||
if (!canGoOn(bd, bdx)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bd instanceof Bisected) {
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.TOP);
|
||||
try {
|
||||
data.set(x, height + 2, z, bd);
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
bd = bd.clone();
|
||||
((Bisected) bd).setHalf(Bisected.Half.BOTTOM);
|
||||
}
|
||||
|
||||
data.set(x, height + 1, z, bd);
|
||||
|
||||
} else {
|
||||
if (height < getDimension().getFluidHeight()) {
|
||||
max = getDimension().getFluidHeight() - height;
|
||||
}
|
||||
|
||||
int stack = decorator.getHeight(getRng().nextParallelRNG(Cache.key(realX, realZ)), realX, realZ, getData());
|
||||
BlockData top = decorator.getBlockDataForTop(biome, getRng(), realX, realZ, getData());
|
||||
BlockData fill = decorator.getBlockData100(biome, getRng(), realX, realZ, getData());
|
||||
|
||||
for (int i = 0; i < stack; i++) {
|
||||
double threshold = ((double) i) / (stack - 1);
|
||||
bd = threshold >= decorator.getTopThreshold() ? top : fill;
|
||||
|
||||
if (bd == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == 0 && !underwater && !canGoOn(bd, bdx)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (underwater && height + 1 + i > getDimension().getFluidHeight()) {
|
||||
break;
|
||||
}
|
||||
|
||||
data.set(x, height + 1 + i, z, bd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.util.RNG;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public interface BlockUpdater {
|
||||
|
||||
void catchBlockUpdates(int x, int y, int z, BlockData data);
|
||||
|
||||
void updateChunk(Chunk c);
|
||||
|
||||
void update(int x, int y, int z, Chunk c, RNG rf);
|
||||
}
|
||||
370
src/main/java/com/volmit/iris/engine/framework/Engine.java
Normal file
370
src/main/java/com/volmit/iris/engine/framework/Engine.java
Normal file
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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.manager.IrisDataManager;
|
||||
import com.volmit.iris.gui.Renderer;
|
||||
import com.volmit.iris.gui.RenderType;
|
||||
import com.volmit.iris.object.*;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.engine.parallax.ParallaxAccess;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.*;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
|
||||
public interface Engine extends DataProvider, Fallible, GeneratorAccess, LootProvider, BlockUpdater, Renderer, Hotloadable {
|
||||
void close();
|
||||
|
||||
double getMaxBiomeObjectDensity();
|
||||
|
||||
double getMaxBiomeDecoratorDensity();
|
||||
|
||||
double getMaxBiomeLayerDensity();
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
EngineWorldManager getWorldManager();
|
||||
|
||||
void setParallelism(int parallelism);
|
||||
|
||||
int getParallelism();
|
||||
|
||||
EngineTarget getTarget();
|
||||
|
||||
EngineFramework getFramework();
|
||||
|
||||
void setMinHeight(int min);
|
||||
|
||||
void recycle();
|
||||
|
||||
int getIndex();
|
||||
|
||||
int getMinHeight();
|
||||
|
||||
double modifyX(double x);
|
||||
|
||||
double modifyZ(double z);
|
||||
|
||||
void generate(int x, int z, Hunk<BlockData> blocks, Hunk<Biome> biomes);
|
||||
|
||||
EngineMetrics getMetrics();
|
||||
|
||||
default void save() {
|
||||
getParallax().saveAll();
|
||||
}
|
||||
|
||||
default void saveNow() {
|
||||
getParallax().saveAllNOW();
|
||||
}
|
||||
|
||||
default String getName() {
|
||||
return getDimension().getName();
|
||||
}
|
||||
|
||||
default int getHeight() {
|
||||
return getTarget().getHeight();
|
||||
}
|
||||
|
||||
default IrisDataManager getData() {
|
||||
return getTarget().getData();
|
||||
}
|
||||
|
||||
default World getWorld() {
|
||||
return getTarget().getWorld();
|
||||
}
|
||||
|
||||
default IrisDimension getDimension() {
|
||||
return getTarget().getDimension();
|
||||
}
|
||||
|
||||
default ParallaxAccess getParallax() {
|
||||
return getTarget().getParallaxWorld();
|
||||
}
|
||||
|
||||
default Color draw(double x, double z) {
|
||||
IrisRegion region = getRegion((int) x, (int) z);
|
||||
IrisBiome biome = getSurfaceBiome((int) x, (int) z);
|
||||
int height = getHeight((int) x, (int) z);
|
||||
double heightFactor = M.lerpInverse(0, getHeight(), height);
|
||||
Color irc = region.getColor(this.getFramework().getComplex(), RenderType.BIOME);
|
||||
Color ibc = biome.getColor(this, RenderType.BIOME);
|
||||
Color rc = irc != null ? irc : Color.GREEN.darker();
|
||||
Color bc = ibc != null ? ibc : biome.isAquatic() ? Color.BLUE : Color.YELLOW;
|
||||
Color f = IrisColor.blend(rc, bc, bc, Color.getHSBColor(0, 0, (float) heightFactor));
|
||||
|
||||
return IrisColor.blend(rc, bc, bc, Color.getHSBColor(0, 0, (float) heightFactor));
|
||||
}
|
||||
|
||||
@Override
|
||||
default IrisRegion getRegion(int x, int z) {
|
||||
return getFramework().getComplex().getRegionStream().get(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
default ParallaxAccess getParallaxAccess() {
|
||||
return getParallax();
|
||||
}
|
||||
|
||||
@Override
|
||||
default IrisBiome getCaveBiome(int x, int z) {
|
||||
return getFramework().getComplex().getCaveBiomeStream().get(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
default IrisBiome getSurfaceBiome(int x, int z) {
|
||||
return getFramework().getComplex().getTrueBiomeStream().get(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
default int getHeight(int x, int z) {
|
||||
return getFramework().getEngineParallax().getHighest(x, z, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void catchBlockUpdates(int x, int y, int z, BlockData data) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (B.isUpdatable(data)) {
|
||||
synchronized (getParallax()) {
|
||||
getParallax().updateBlock(x, y, z);
|
||||
getParallax().getMetaRW(x >> 4, z >> 4).setUpdates(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void placeTiles(Chunk c) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default void updateChunk(Chunk c) {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
if (getParallax().getMetaR(c.getX(), c.getZ()).isUpdates()) {
|
||||
Hunk<Boolean> b = getParallax().getUpdatesR(c.getX(), c.getZ());
|
||||
|
||||
b.iterateSync((x, y, z, v) -> {
|
||||
|
||||
if (v != null && v) {
|
||||
int vx = x & 15;
|
||||
int vz = z & 15;
|
||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
||||
|
||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
||||
updateLighting(x, y, z, c);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||
}
|
||||
|
||||
default void updateLighting(int x, int y, int z, Chunk c) {
|
||||
Block block = c.getBlock(x, y, z);
|
||||
BlockData data = block.getBlockData();
|
||||
|
||||
if (B.isLit(data)) {
|
||||
try {
|
||||
block.setType(Material.AIR, false);
|
||||
block.setBlockData(data, true);
|
||||
} catch (Exception e) {
|
||||
Iris.reportError(e);
|
||||
// Issue when adding block data. Suppress massive warnings and stack-traces to console.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default void update(int x, int y, int z, Chunk c, RNG rf) {
|
||||
Block block = c.getBlock(x, y, z);
|
||||
BlockData data = block.getBlockData();
|
||||
|
||||
if (B.isStorage(data)) {
|
||||
RNG rx = rf.nextParallelRNG(BlockPosition.toLong(x, y, z));
|
||||
InventorySlotType slot = null;
|
||||
|
||||
if (B.isStorageChest(data)) {
|
||||
slot = InventorySlotType.STORAGE;
|
||||
}
|
||||
|
||||
if (slot != null) {
|
||||
KList<IrisLootTable> tables = getLootTables(rx, block);
|
||||
|
||||
try {
|
||||
InventoryHolder m = (InventoryHolder) block.getState();
|
||||
addItems(false, m.getInventory(), rx, tables, slot, x, y, z, 15);
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default void scramble(Inventory inventory, RNG rng) {
|
||||
org.bukkit.inventory.ItemStack[] items = inventory.getContents();
|
||||
org.bukkit.inventory.ItemStack[] nitems = new org.bukkit.inventory.ItemStack[inventory.getSize()];
|
||||
System.arraycopy(items, 0, nitems, 0, items.length);
|
||||
boolean packedFull = false;
|
||||
|
||||
splitting:
|
||||
for (int i = 0; i < nitems.length; i++) {
|
||||
ItemStack is = nitems[i];
|
||||
|
||||
if (is != null && is.getAmount() > 1 && !packedFull) {
|
||||
for (int j = 0; j < nitems.length; j++) {
|
||||
if (nitems[j] == null) {
|
||||
int take = rng.nextInt(is.getAmount());
|
||||
take = take == 0 ? 1 : take;
|
||||
is.setAmount(is.getAmount() - take);
|
||||
nitems[j] = is.clone();
|
||||
nitems[j].setAmount(take);
|
||||
continue splitting;
|
||||
}
|
||||
}
|
||||
|
||||
packedFull = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
Arrays.parallelSort(nitems, (a, b) -> rng.nextInt());
|
||||
break;
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setContents(nitems);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void injectTables(KList<IrisLootTable> list, IrisLootReference r) {
|
||||
if (r.getMode().equals(LootMode.CLEAR) || r.getMode().equals(LootMode.REPLACE)) {
|
||||
list.clear();
|
||||
}
|
||||
|
||||
list.addAll(r.getLootTables(getFramework().getComplex()));
|
||||
}
|
||||
|
||||
@Override
|
||||
default KList<IrisLootTable> getLootTables(RNG rng, Block b) {
|
||||
int rx = b.getX();
|
||||
int rz = b.getZ();
|
||||
double he = getFramework().getComplex().getHeightStream().get(rx, rz);
|
||||
PlacedObject po = getFramework().getEngine().getObjectPlacement(rx, b.getY(), rz);
|
||||
if (po != null && po.getPlacement() != null) {
|
||||
|
||||
if (B.isStorageChest(b.getBlockData())) {
|
||||
IrisLootTable table = po.getPlacement().getTable(b.getBlockData(), getData());
|
||||
if (table != null) {
|
||||
return new KList<>(table);
|
||||
}
|
||||
}
|
||||
}
|
||||
IrisRegion region = getFramework().getComplex().getRegionStream().get(rx, rz);
|
||||
IrisBiome biomeSurface = getFramework().getComplex().getTrueBiomeStream().get(rx, rz);
|
||||
IrisBiome biomeUnder = b.getY() < he ? getFramework().getComplex().getCaveBiomeStream().get(rx, rz) : biomeSurface;
|
||||
KList<IrisLootTable> tables = new KList<>();
|
||||
double multiplier = 1D * getDimension().getLoot().getMultiplier() * region.getLoot().getMultiplier() * biomeSurface.getLoot().getMultiplier() * biomeUnder.getLoot().getMultiplier();
|
||||
injectTables(tables, getDimension().getLoot());
|
||||
injectTables(tables, region.getLoot());
|
||||
injectTables(tables, biomeSurface.getLoot());
|
||||
injectTables(tables, biomeUnder.getLoot());
|
||||
|
||||
if (tables.isNotEmpty()) {
|
||||
int target = (int) Math.round(tables.size() * multiplier);
|
||||
|
||||
while (tables.size() < target && tables.isNotEmpty()) {
|
||||
tables.add(tables.get(rng.i(tables.size() - 1)));
|
||||
}
|
||||
|
||||
while (tables.size() > target && tables.isNotEmpty()) {
|
||||
tables.remove(rng.i(tables.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, int x, int y, int z, int mgf) {
|
||||
KList<ItemStack> items = new KList<>();
|
||||
|
||||
int b = 4;
|
||||
for (IrisLootTable i : tables) {
|
||||
b++;
|
||||
items.addAll(i.getLoot(debug, items.isEmpty(), rng, slot, x, y, z, b + b, mgf + b));
|
||||
}
|
||||
|
||||
for (ItemStack i : items) {
|
||||
inv.addItem(i);
|
||||
}
|
||||
|
||||
scramble(inv, rng);
|
||||
}
|
||||
|
||||
default int getMaxHeight() {
|
||||
return getHeight() + getMinHeight();
|
||||
}
|
||||
|
||||
EngineEffects getEffects();
|
||||
|
||||
EngineCompound getCompound();
|
||||
|
||||
default boolean isStudio() {
|
||||
return getCompound().isStudio();
|
||||
}
|
||||
|
||||
default void clean() {
|
||||
MultiBurst.burst.lazy(() -> getParallax().cleanup());
|
||||
}
|
||||
|
||||
default IrisBiome getBiome(Location l) {
|
||||
return getBiome(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
||||
}
|
||||
|
||||
default IrisRegion getRegion(Location l) {
|
||||
return getRegion(l.getBlockX(), l.getBlockZ());
|
||||
}
|
||||
|
||||
default boolean contains(Location l) {
|
||||
return l.getBlockY() >= getMinHeight() && l.getBlockY() <= getMaxHeight();
|
||||
}
|
||||
|
||||
IrisBiome getFocus();
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
|
||||
public interface EngineActuator<O> extends EngineComponent {
|
||||
void actuate(int x, int z, Hunk<O> output);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
|
||||
public abstract class EngineAssignedActuator<T> extends EngineAssignedComponent implements EngineActuator<T> {
|
||||
public EngineAssignedActuator(Engine engine, String name) {
|
||||
super(engine, name);
|
||||
}
|
||||
|
||||
public abstract void onActuate(int x, int z, Hunk<T> output);
|
||||
|
||||
@Override
|
||||
public void actuate(int x, int z, Hunk<T> output) {
|
||||
onActuate(x, z, output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
|
||||
public abstract class EngineAssignedBiModifier<A, B> extends EngineAssignedComponent implements EngineBiModifier<A, B> {
|
||||
public EngineAssignedBiModifier(Engine engine, String name) {
|
||||
super(engine, name);
|
||||
}
|
||||
|
||||
public abstract void onModify(int x, int z, Hunk<A> a, Hunk<B> b);
|
||||
|
||||
@Override
|
||||
public void modify(int x, int z, Hunk<A> a, Hunk<B> b) {
|
||||
onModify(x, z, a, b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.util.RollingSequence;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class EngineAssignedComponent implements EngineComponent {
|
||||
private final Engine engine;
|
||||
private final RollingSequence metrics;
|
||||
private final String name;
|
||||
|
||||
public EngineAssignedComponent(Engine engine, String name) {
|
||||
this.engine = engine;
|
||||
this.metrics = new RollingSequence(16);
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
|
||||
public abstract class EngineAssignedModifier<T> extends EngineAssignedComponent implements EngineModifier<T> {
|
||||
public EngineAssignedModifier(Engine engine, String name) {
|
||||
super(engine, name);
|
||||
}
|
||||
|
||||
public abstract void onModify(int x, int z, Hunk<T> output);
|
||||
|
||||
@Override
|
||||
public void modify(int x, int z, Hunk<T> output) {
|
||||
onModify(x, z, output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
import org.bukkit.event.world.WorldSaveEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
|
||||
public abstract class EngineAssignedWorldManager extends EngineAssignedComponent implements EngineWorldManager, Listener {
|
||||
private final int taskId;
|
||||
|
||||
public EngineAssignedWorldManager(Engine engine) {
|
||||
super(engine, "World");
|
||||
Iris.instance.registerListener(this);
|
||||
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, this::onTick, 0, 0);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(WorldSaveEvent e) {
|
||||
if (e.getWorld().equals(getTarget().getWorld())) {
|
||||
onSave();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(WorldUnloadEvent e) {
|
||||
if (e.getWorld().equals(getTarget().getWorld())) {
|
||||
getEngine().close();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(EntitySpawnEvent e) {
|
||||
if (e.getEntity().getWorld().equals(getTarget().getWorld())) {
|
||||
onEntitySpawn(e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(BlockBreakEvent e) {
|
||||
if (e.getPlayer().getWorld().equals(getTarget().getWorld())) {
|
||||
onBlockBreak(e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void on(BlockPlaceEvent e) {
|
||||
if (e.getPlayer().getWorld().equals(getTarget().getWorld())) {
|
||||
onBlockPlace(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
Iris.instance.unregisterListener(this);
|
||||
Bukkit.getScheduler().cancelTask(taskId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
|
||||
public interface EngineBiModifier<A, B> extends EngineComponent {
|
||||
void modify(int x, int z, Hunk<A> a, Hunk<B> b);
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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.generator.IrisComplex;
|
||||
import com.volmit.iris.manager.IrisDataManager;
|
||||
import com.volmit.iris.object.IrisDimension;
|
||||
import com.volmit.iris.engine.parallax.ParallaxAccess;
|
||||
import com.volmit.iris.util.RollingSequence;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public interface EngineComponent {
|
||||
Engine getEngine();
|
||||
|
||||
RollingSequence getMetrics();
|
||||
|
||||
String getName();
|
||||
|
||||
default void close() {
|
||||
try {
|
||||
if (this instanceof Listener) {
|
||||
Iris.instance.unregisterListener((Listener) this);
|
||||
}
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
default double modX(double x) {
|
||||
return getEngine().modifyX(x);
|
||||
}
|
||||
|
||||
default double modZ(double z) {
|
||||
return getEngine().modifyZ(z);
|
||||
}
|
||||
|
||||
default IrisDataManager getData() {
|
||||
return getEngine().getData();
|
||||
}
|
||||
|
||||
default ParallaxAccess getParallax() {
|
||||
return getEngine().getParallax();
|
||||
}
|
||||
|
||||
default EngineTarget getTarget() {
|
||||
return getEngine().getTarget();
|
||||
}
|
||||
|
||||
default IrisDimension getDimension() {
|
||||
return getEngine().getDimension();
|
||||
}
|
||||
|
||||
default long getSeed() {
|
||||
return getTarget().getWorld().getSeed();
|
||||
}
|
||||
|
||||
default EngineFramework getFramework() {
|
||||
return getEngine().getFramework();
|
||||
}
|
||||
|
||||
default int getParallelism() {
|
||||
return getEngine().getParallelism();
|
||||
}
|
||||
|
||||
default IrisComplex getComplex() {
|
||||
return getFramework().getComplex();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,967 @@
|
||||
/*
|
||||
* 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.IrisSettings;
|
||||
import com.volmit.iris.generator.IrisEngineCompound;
|
||||
import com.volmit.iris.manager.IrisDataManager;
|
||||
import com.volmit.iris.nms.BiomeBaseInjector;
|
||||
import com.volmit.iris.nms.INMS;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDimension;
|
||||
import com.volmit.iris.object.IrisPosition;
|
||||
import com.volmit.iris.engine.data.DirectWorldWriter;
|
||||
import com.volmit.iris.engine.IrisWorlds;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.engine.parallel.BurstExecutor;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.*;
|
||||
import io.netty.util.internal.ConcurrentSet;
|
||||
import io.papermc.lib.PaperLib;
|
||||
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 org.bukkit.material.MaterialData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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 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 int generated = 0;
|
||||
private int lgenerated = 0;
|
||||
private final KMap<Long, PregeneratedData> chunkCache;
|
||||
private final ChronoLatch hotloadcd;
|
||||
private final AtomicBoolean fake;
|
||||
@Getter
|
||||
private double generatedPerSecond = 0;
|
||||
private final int art;
|
||||
private ReactiveFolder hotloader = null;
|
||||
|
||||
public EngineCompositeGenerator() {
|
||||
this(null, true);
|
||||
}
|
||||
|
||||
public EngineCompositeGenerator(String query, boolean production) {
|
||||
super();
|
||||
chunkCache = new KMap<>();
|
||||
fake = new AtomicBoolean(true);
|
||||
hotloadcd = new ChronoLatch(3500);
|
||||
mst = M.ms();
|
||||
this.production = production;
|
||||
this.dimensionQuery = query;
|
||||
initialized = new AtomicBoolean(false);
|
||||
art = J.ar(this::tick, 100);
|
||||
populators = new KList<BlockPopulator>().qadd(new BlockPopulator() {
|
||||
@Override
|
||||
public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) {
|
||||
if (compound != null) {
|
||||
for (BlockPopulator i : compound.get().getPopulators()) {
|
||||
i.populate(world, random, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void hotload() {
|
||||
if (isStudio()) {
|
||||
Iris.proj.updateWorkspace();
|
||||
getData().dump();
|
||||
J.s(() -> {
|
||||
try {
|
||||
for (Player i : getTarget().getWorld().getPlayers()) {
|
||||
new MortarSender(i, Iris.instance.getTag()).sendMessage("Dimension Hotloaded");
|
||||
i.playSound(i.getLocation(), Sound.BLOCK_COPPER_PLACE, 1f, 1.25f);
|
||||
}
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
});
|
||||
initialized.lazySet(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!initialized.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (hotloader != null) {
|
||||
J.a(() -> hotloader.check());
|
||||
getComposite().clean();
|
||||
}
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
|
||||
if (M.ms() - mst > 1000) {
|
||||
generatedPerSecond = (double) (generated - lgenerated) / ((double) (M.ms() - mst) / 1000D);
|
||||
mst = M.ms();
|
||||
lgenerated = generated;
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized IrisDimension getDimension(World world) {
|
||||
String query = dimensionQuery;
|
||||
query = Iris.linkMultiverseCore.getWorldNameType(world.getName(), query);
|
||||
|
||||
IrisDimension dim = null;
|
||||
|
||||
if (query == null) {
|
||||
File iris = new File(world.getWorldFolder(), "iris");
|
||||
|
||||
if (iris.exists() && iris.isDirectory()) {
|
||||
searching:
|
||||
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.getName() + "! Assuming " + IrisSettings.get().getGenerator().getDefaultWorldType() + "!");
|
||||
query = IrisSettings.get().getGenerator().getDefaultWorldType();
|
||||
}
|
||||
|
||||
dim = IrisDataManager.loadAnyDimension(query);
|
||||
|
||||
if (dim == null) {
|
||||
Iris.proj.downloadSearch(new MortarSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), query, false);
|
||||
dim = IrisDataManager.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 IrisDataManager(getDataFolder(world)).getDimensionLoader().load(od.getLoadKey());
|
||||
|
||||
if (dim == null) {
|
||||
Iris.info("Installing Iris pack " + od.getName() + " into world " + world.getName() + "...");
|
||||
Iris.proj.installIntoWorld(new MortarSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), od.getLoadKey(), world.getWorldFolder());
|
||||
dim = new IrisDataManager(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 = IrisDataManager.loadAnyDimension(query);
|
||||
|
||||
if (dim == null) {
|
||||
Iris.proj.downloadSearch(new MortarSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), query, false);
|
||||
dim = IrisDataManager.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 IrisDataManager(getDataFolder(world)).getDimensionLoader().load(od.getLoadKey());
|
||||
|
||||
if (dim == null) {
|
||||
Iris.info("Installing Iris pack " + od.getName() + " into world " + world + "...");
|
||||
Iris.proj.installIntoWorld(new MortarSender(Bukkit.getConsoleSender(), Iris.instance.getTag()), od.getLoadKey(), new File(world));
|
||||
dim = new IrisDataManager(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(World world) {
|
||||
if (!(world instanceof FakeWorld) && fake.get() && this.compound != null) {
|
||||
fake.set(false);
|
||||
this.compound.get().updateWorld(world);
|
||||
getTarget().updateWorld(world);
|
||||
placeStrongholds(world);
|
||||
|
||||
for (int i = 0; i < getComposite().getSize(); i++) {
|
||||
getComposite().getEngine(i).getTarget().updateWorld(world);
|
||||
}
|
||||
|
||||
Iris.info("Attached Real World to Engine Target");
|
||||
}
|
||||
|
||||
if (initialized.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
initialized.set(true);
|
||||
IrisDimension dim = getDimension(world);
|
||||
IrisDataManager data = production ? new IrisDataManager(getDataFolder(world)) : dim.getLoader().copy();
|
||||
compound.set(new IrisEngineCompound(world, dim, data, Iris.getThreadCount()));
|
||||
compound.get().setStudio(!production);
|
||||
populators.clear();
|
||||
populators.addAll(compound.get().getPopulators());
|
||||
hotloader = new ReactiveFolder(data.getDataFolder(), (a, c, d) -> hotload());
|
||||
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(World world) {
|
||||
return new File(world.getWorldFolder(), "iris/pack");
|
||||
}
|
||||
|
||||
private File getDataFolder(String world) {
|
||||
return new File(world + "/iris/pack");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ChunkData generateChunkData(@NotNull World world, @NotNull Random ignored, int x, int z, @NotNull BiomeGrid biome) {
|
||||
TerrainChunk tc = TerrainChunk.create(world, biome);
|
||||
generateChunkRawData(world, x, z, tc).run();
|
||||
generated++;
|
||||
return tc.getRaw();
|
||||
}
|
||||
|
||||
public void directWriteMCA(World w, int x, int z, DirectWorldWriter writer, MultiBurst burst) {
|
||||
BurstExecutor e = burst.burst(1024);
|
||||
int mcaox = x << 5;
|
||||
int mcaoz = z << 5;
|
||||
|
||||
for (int i = 0; i < 32; i++) {
|
||||
int ii = i;
|
||||
for (int j = 0; j < 32; j++) {
|
||||
int jj = j;
|
||||
e.queue(() -> directWriteChunk(w, ii + mcaox, jj + mcaoz, writer));
|
||||
}
|
||||
}
|
||||
|
||||
e.complete();
|
||||
}
|
||||
|
||||
public void directWriteChunk(World w, int x, int z, DirectWorldWriter writer) {
|
||||
int ox = x << 4;
|
||||
int oz = z << 4;
|
||||
com.volmit.iris.engine.data.mca.Chunk cc = writer.getChunk(x, z);
|
||||
BiomeBaseInjector injector = (xx, yy, zz, biomeBase) -> cc.setBiomeAt(ox + xx, yy, oz + zz, INMS.get().getTrueBiomeBaseId(biomeBase));
|
||||
//noinspection deprecation
|
||||
generateChunkRawData(w, x, z, new TerrainChunk() {
|
||||
@Override
|
||||
public BiomeBaseInjector getBiomeBaseInjector() {
|
||||
return injector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRaw(ChunkData data) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Biome getBiome(int x, int z) {
|
||||
return Biome.THE_VOID;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Biome getBiome(int x, int y, int z) {
|
||||
return Biome.THE_VOID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int z, Biome bio) {
|
||||
setBiome(ox + x, 0, oz + z, bio);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int y, int z, Biome bio) {
|
||||
writer.setBiome(ox + x, y, oz + z, bio);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight() {
|
||||
return w.getMinHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHeight() {
|
||||
return w.getMaxHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, BlockData blockData) {
|
||||
int xx = (x + ox) & 15;
|
||||
int zz = (z + oz) & 15;
|
||||
|
||||
if (y > 255 || y < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
cc.setBlockStateAt(xx, y, zz, DirectWorldWriter.getCompound(blockData), false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BlockData getBlockData(int x, int y, int z) {
|
||||
if (y > getMaxHeight()) {
|
||||
y = getMaxHeight();
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
}
|
||||
|
||||
return DirectWorldWriter.getBlockData(cc.getBlockStateAt((x + ox) & 15, y, (z + oz) & 15));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData getRaw() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(BiomeGrid biome) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, @NotNull Material material) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, @NotNull MaterialData material) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, @NotNull Material material) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, @NotNull MaterialData material) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, @NotNull BlockData blockData) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Material getType(int x, int y, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MaterialData getTypeAndData(int x, int y, int z) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getData(int x, int y, int z) {
|
||||
return 0;
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
|
||||
public Chunk generatePaper(World world, int x, int z) {
|
||||
precache(world, x, z);
|
||||
Chunk c = PaperLib.getChunkAtAsync(world, x, z, true).join();
|
||||
chunkCache.remove(Cache.key(x, z));
|
||||
return c;
|
||||
}
|
||||
|
||||
public void precache(World world, int x, int z) {
|
||||
synchronized (this) {
|
||||
initialize(world);
|
||||
}
|
||||
|
||||
synchronized (chunkCache) {
|
||||
if (chunkCache.containsKey(Cache.key(x, z))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PregeneratedData data = new PregeneratedData(getComposite().getHeight() - 1);
|
||||
compound.get().generate(x * 16, z * 16, data.getBlocks(), data.getPost(), data.getBiomes());
|
||||
synchronized (chunkCache) {
|
||||
chunkCache.put(Cache.key(x, z), data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPrecacheSize() {
|
||||
return chunkCache.size();
|
||||
}
|
||||
|
||||
public int getCachedChunks() {
|
||||
return chunkCache.size();
|
||||
}
|
||||
|
||||
public Runnable generateChunkRawData(World world, int x, int z, TerrainChunk tc) {
|
||||
initialize(world);
|
||||
|
||||
synchronized (chunkCache) {
|
||||
long g = Cache.key(x, z);
|
||||
if (chunkCache.containsKey(g)) {
|
||||
return chunkCache.remove(g).inject(tc);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return () -> blocks.insertSoftly(0, 0, 0, post, (b) -> b == null || B.isAirOrFluid(b));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSpawn(@NotNull World world, int x, int z) {
|
||||
return super.canSpawn(world, x, z);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<BlockPopulator> getDefaultPopulators(@NotNull World world) {
|
||||
return populators;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Location getFixedSpawnLocation(@NotNull World world, @NotNull 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 IrisDataManager 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 clearRegeneratedLists(int x, int z) {
|
||||
for (int i = 0; i < getComposite().getSize(); i++) {
|
||||
getComposite().getEngine(i).getParallax().delete(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void regenerate(int x, int z) {
|
||||
|
||||
clearRegeneratedLists(x, z);
|
||||
int xx = x * 16;
|
||||
int zz = z * 16;
|
||||
BiomeBaseInjector inj = (a, b, c, d) -> {
|
||||
};
|
||||
//noinspection deprecation
|
||||
generateChunkRawData(getComposite().getWorld(), x, z, new TerrainChunk() {
|
||||
@Override
|
||||
public BiomeBaseInjector getBiomeBaseInjector() {
|
||||
return inj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRaw(ChunkData data) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Biome getBiome(int x, int z) {
|
||||
return Biome.THE_VOID;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Biome getBiome(int x, int y, int z) {
|
||||
return Biome.THE_VOID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int z, Biome bio) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBiome(int x, int y, int z, Biome bio) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinHeight() {
|
||||
return getComposite().getWorld().getMinHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxHeight() {
|
||||
return getComposite().getWorld().getMaxHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, BlockData blockData) {
|
||||
if (!getBlockData(x, y, z).matches(blockData)) {
|
||||
Iris.edit.set(compound.get().getWorld(), x + xx, y, z + zz, blockData);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BlockData getBlockData(int x, int y, int z) {
|
||||
return Iris.edit.get(compound.get().getWorld(), x + xx, y, z + zz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData getRaw() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(BiomeGrid biome) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int i, int i1, int i2, @NotNull Material material) {
|
||||
setBlock(i, i1, i2, material.createBlockData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int i, int i1, int i2, @NotNull MaterialData materialData) {
|
||||
setBlock(i, i1, i2, materialData.getItemType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int i, int i1, int i2, int i3, int i4, int i5, @NotNull Material material) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int i, int i1, int i2, int i3, int i4, int i5, @NotNull MaterialData materialData) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRegion(int i, int i1, int i2, int i3, int i4, int i5, @NotNull BlockData blockData) {
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Material getType(int i, int i1, int i2) {
|
||||
return getBlockData(i, i1, i2).getMaterial();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public MaterialData getTypeAndData(int i, int i1, int i2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getData(int i, int i1, int i2) {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
Iris.edit.flushNow();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
J.car(art);
|
||||
if (getComposite() != null) {
|
||||
getComposite().close();
|
||||
|
||||
|
||||
if (isStudio()) {
|
||||
IrisWorlds.evacuate(getComposite().getWorld());
|
||||
Bukkit.unloadWorld(getComposite().getWorld(), !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) -> IrisDataManager.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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.generator.actuator.IrisTerrainActuator;
|
||||
import com.volmit.iris.manager.IrisDataManager;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisDimension;
|
||||
import com.volmit.iris.object.IrisPosition;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.KMap;
|
||||
import org.bukkit.World;
|
||||
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);
|
||||
|
||||
World 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();
|
||||
}
|
||||
}
|
||||
|
||||
IrisDataManager getData(int height);
|
||||
|
||||
default IrisDataManager 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();
|
||||
}
|
||||
|
||||
void updateWorld(World world);
|
||||
|
||||
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 = ((IrisTerrainActuator) e.getFramework().getTerrainActuator()).getLastBedrock();
|
||||
|
||||
if (f > m) {
|
||||
f = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.google.gson.Gson;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.object.IrisPosition;
|
||||
import com.volmit.iris.util.IO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class EngineData {
|
||||
private String dimension;
|
||||
private String lastVersion;
|
||||
private List<IrisPosition> strongholdPositions;
|
||||
|
||||
public void save(File f) {
|
||||
try {
|
||||
f.getParentFile().mkdirs();
|
||||
IO.writeAll(f, new Gson().toJson(this));
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static EngineData load(File f) {
|
||||
try {
|
||||
f.getParentFile().mkdirs();
|
||||
return new Gson().fromJson(IO.readAll(f), EngineData.class);
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
|
||||
return new EngineData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.object.IrisBiome;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.util.B;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public interface EngineDecorator extends EngineComponent {
|
||||
void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk<BlockData> data, IrisBiome biome, int height, int max);
|
||||
|
||||
default void decorate(int x, int z, int realX, int realZ, Hunk<BlockData> data, IrisBiome biome, int height, int max) {
|
||||
decorate(x, z, realX, realX, realX, realZ, realZ, realZ, data, biome, height, max);
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
default boolean canGoOn(BlockData decorant, BlockData atop) {
|
||||
if (atop == null || B.isAir(atop)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return B.canPlaceOnto(decorant.getMaterial(), atop.getMaterial());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface EngineEffects extends EngineComponent {
|
||||
void updatePlayerMap();
|
||||
|
||||
void tickRandomPlayer();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.generator.IrisComplex;
|
||||
import com.volmit.iris.manager.IrisDataManager;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
public interface EngineFramework extends DataProvider {
|
||||
Engine getEngine();
|
||||
|
||||
IrisComplex getComplex();
|
||||
|
||||
EngineParallaxManager getEngineParallax();
|
||||
|
||||
default IrisDataManager getData() {
|
||||
return getComplex().getData();
|
||||
}
|
||||
|
||||
default void recycle() {
|
||||
getEngine().getParallax().cleanup();
|
||||
getData().getObjectLoader().clean();
|
||||
}
|
||||
|
||||
EngineActuator<BlockData> getTerrainActuator();
|
||||
|
||||
EngineActuator<BlockData> getDecorantActuator();
|
||||
|
||||
EngineActuator<Biome> getBiomeActuator();
|
||||
|
||||
EngineModifier<BlockData> getCaveModifier();
|
||||
|
||||
EngineModifier<BlockData> getRavineModifier();
|
||||
|
||||
EngineModifier<BlockData> getDepositModifier();
|
||||
|
||||
EngineModifier<BlockData> getPostModifier();
|
||||
|
||||
void close();
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.util.AtomicRollingSequence;
|
||||
import com.volmit.iris.util.KMap;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class EngineMetrics {
|
||||
private final AtomicRollingSequence total;
|
||||
private final AtomicRollingSequence updates;
|
||||
private final AtomicRollingSequence terrain;
|
||||
private final AtomicRollingSequence biome;
|
||||
private final AtomicRollingSequence parallax;
|
||||
private final AtomicRollingSequence parallaxInsert;
|
||||
private final AtomicRollingSequence post;
|
||||
private final AtomicRollingSequence decoration;
|
||||
private final AtomicRollingSequence cave;
|
||||
private final AtomicRollingSequence ravine;
|
||||
private final AtomicRollingSequence deposit;
|
||||
|
||||
public EngineMetrics(int mem) {
|
||||
this.total = new AtomicRollingSequence(mem);
|
||||
this.terrain = new AtomicRollingSequence(mem);
|
||||
this.biome = new AtomicRollingSequence(mem);
|
||||
this.parallax = new AtomicRollingSequence(mem);
|
||||
this.parallaxInsert = new AtomicRollingSequence(mem);
|
||||
this.post = new AtomicRollingSequence(mem);
|
||||
this.decoration = new AtomicRollingSequence(mem);
|
||||
this.updates = new AtomicRollingSequence(mem);
|
||||
this.cave = new AtomicRollingSequence(mem);
|
||||
this.ravine = new AtomicRollingSequence(mem);
|
||||
this.deposit = new AtomicRollingSequence(mem);
|
||||
}
|
||||
|
||||
public KMap<String, Double> pull() {
|
||||
KMap<String, Double> v = new KMap<>();
|
||||
v.put("terrain", terrain.getAverage());
|
||||
v.put("biome", biome.getAverage());
|
||||
v.put("parallax", parallax.getAverage());
|
||||
v.put("parallax.insert", parallaxInsert.getAverage());
|
||||
v.put("post", post.getAverage());
|
||||
v.put("decoration", decoration.getAverage());
|
||||
v.put("updates", updates.getAverage());
|
||||
v.put("cave", cave.getAverage());
|
||||
v.put("ravine", ravine.getAverage());
|
||||
v.put("deposit", deposit.getAverage());
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
|
||||
public interface EngineModifier<T> extends EngineComponent {
|
||||
void modify(int x, int z, Hunk<T> t);
|
||||
}
|
||||
@@ -0,0 +1,835 @@
|
||||
/*
|
||||
* 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.generator.IrisComplex;
|
||||
import com.volmit.iris.manager.IrisDataManager;
|
||||
import com.volmit.iris.object.*;
|
||||
import com.volmit.iris.object.tile.TileData;
|
||||
import com.volmit.iris.engine.cache.Cache;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import com.volmit.iris.engine.hunk.Hunk;
|
||||
import com.volmit.iris.engine.jigsaw.PlannedStructure;
|
||||
import com.volmit.iris.engine.parallax.ParallaxAccess;
|
||||
import com.volmit.iris.engine.parallax.ParallaxChunkMeta;
|
||||
import com.volmit.iris.engine.parallel.BurstExecutor;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.*;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.block.TileState;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.BlockVector;
|
||||
import org.bukkit.util.Consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public interface EngineParallaxManager extends DataProvider, IObjectPlacer {
|
||||
BlockData AIR = B.get("AIR");
|
||||
|
||||
Engine getEngine();
|
||||
|
||||
int getParallaxSize();
|
||||
|
||||
default EngineFramework getFramework() {
|
||||
return getEngine().getFramework();
|
||||
}
|
||||
|
||||
default ParallaxAccess getParallaxAccess() {
|
||||
return getEngine().getParallax();
|
||||
}
|
||||
|
||||
default IrisDataManager getData() {
|
||||
return getEngine().getData();
|
||||
}
|
||||
|
||||
default IrisComplex getComplex() {
|
||||
return getEngine().getFramework().getComplex();
|
||||
}
|
||||
|
||||
default KList<IrisRegion> getAllRegions() {
|
||||
KList<IrisRegion> r = new KList<>();
|
||||
|
||||
for (String i : getEngine().getDimension().getRegions()) {
|
||||
r.add(getEngine().getData().getRegionLoader().load(i));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
default KList<IrisFeaturePotential> getAllFeatures() {
|
||||
KList<IrisFeaturePotential> r = new KList<>();
|
||||
r.addAll(getEngine().getDimension().getFeatures());
|
||||
getAllRegions().forEach((i) -> r.addAll(i.getFeatures()));
|
||||
getAllBiomes().forEach((i) -> r.addAll(i.getFeatures()));
|
||||
return r;
|
||||
}
|
||||
|
||||
default KList<IrisBiome> getAllBiomes() {
|
||||
KList<IrisBiome> r = new KList<>();
|
||||
|
||||
for (IrisRegion i : getAllRegions()) {
|
||||
r.addAll(i.getAllBiomes(this));
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the chunk correctly has all the parallax objects it should.
|
||||
* This method should not have to be written but why not.
|
||||
* Thread Safe, Designed to run async
|
||||
*
|
||||
* @param c the bukkit chunk
|
||||
*/
|
||||
default int repairChunk(Chunk c) {
|
||||
ParallaxChunkMeta m = getParallaxAccess().getMetaR(c.getX(), c.getZ());
|
||||
Hunk<String> o = getParallaxAccess().getObjectsR(c.getX(), c.getZ());
|
||||
Hunk<BlockData> b = getParallaxAccess().getBlocksR(c.getX(), c.getZ());
|
||||
ChunkSnapshot snapshot = c.getChunkSnapshot(false, false, false);
|
||||
KList<Runnable> queue = new KList<>();
|
||||
|
||||
o.iterateSync((x, y, z, s) -> {
|
||||
if (s != null) {
|
||||
|
||||
BlockData bd = b.get(x, y, z);
|
||||
if (bd != null) {
|
||||
BlockData bdx = snapshot.getBlockData(x, y, z);
|
||||
|
||||
if (!bdx.getMaterial().equals(bd.getMaterial())) {
|
||||
queue.add(() -> c.getBlock(x, y, z).setBlockData(bd, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AtomicBoolean bx = new AtomicBoolean(false);
|
||||
J.s(() -> {
|
||||
queue.forEach(Runnable::run);
|
||||
bx.set(true);
|
||||
});
|
||||
|
||||
while (!bx.get()) {
|
||||
J.sleep(50);
|
||||
}
|
||||
|
||||
return queue.size();
|
||||
}
|
||||
|
||||
default void insertTileEntities(int x, int z, Consumer4<Integer, Integer, Integer, TileData<? extends TileState>> consumer) {
|
||||
ParallaxChunkMeta meta = getParallaxAccess().getMetaRW(x >> 4, z >> 4);
|
||||
|
||||
if (meta.isTilesGenerated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
meta.setTilesGenerated(true);
|
||||
|
||||
getParallaxAccess().getTilesRW(x >> 4, z >> 4).iterateSync((a, b, c, d) -> {
|
||||
if (d != null) {
|
||||
consumer.accept(a, b, c, d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
default void insertParallax(int x, int z, Hunk<BlockData> data) {
|
||||
try {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
ParallaxChunkMeta meta = getParallaxAccess().getMetaR(x >> 4, z >> 4);
|
||||
|
||||
if (!meta.isParallaxGenerated()) {
|
||||
generateParallaxLayer(x, z, true);
|
||||
meta = getParallaxAccess().getMetaR(x >> 4, z >> 4);
|
||||
}
|
||||
|
||||
if (!meta.isObjects()) {
|
||||
getEngine().getMetrics().getParallaxInsert().put(p.getMilliseconds());
|
||||
return;
|
||||
}
|
||||
|
||||
getParallaxAccess().getBlocksR(x >> 4, z >> 4).iterateSync((a, b, c, d) -> {
|
||||
if (d != null) {
|
||||
data.set(a, b, c, d);
|
||||
}
|
||||
});
|
||||
|
||||
getEngine().getMetrics().getParallaxInsert().put(p.getMilliseconds());
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
Iris.error("Failed to insert parallax at chunk " + (x >> 4) + " " + (z >> 4));
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
default void forEachFeature(double x, double z, Consumer<IrisFeaturePositional> f) {
|
||||
for (IrisFeaturePositional i : getEngine().getDimension().getSpecificFeatures()) {
|
||||
if (i.shouldFilter(x, z)) {
|
||||
f.accept(i);
|
||||
}
|
||||
}
|
||||
|
||||
int s = (int) Math.ceil(getParallaxSize() / 2D);
|
||||
int i, j;
|
||||
int cx = (int) x >> 4;
|
||||
int cz = (int) z >> 4;
|
||||
|
||||
for (i = -s; i <= s; i++) {
|
||||
for (j = -s; j <= s; j++) {
|
||||
ParallaxChunkMeta m = getParallaxAccess().getMetaR(i + cx, j + cz);
|
||||
|
||||
try {
|
||||
synchronized (m.getFeatures()) {
|
||||
for (IrisFeaturePositional k : m.getFeatures()) {
|
||||
if (k.shouldFilter(x, z)) {
|
||||
f.accept(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
Iris.warn("Failed to read positional features in chunk " + (i + cx) + " " + (j + cz) + "(" + e.getClass().getSimpleName() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
|
||||
default void generateParallaxArea(int x, int z) {
|
||||
try {
|
||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||
|
||||
int s = (int) Math.ceil(getParallaxSize() / 2D);
|
||||
int i, j;
|
||||
KList<Runnable> after = new KList<>();
|
||||
int bs = (int) Math.pow((s * 2) + 1, 2);
|
||||
BurstExecutor burst = MultiBurst.burst.burst(bs);
|
||||
for (i = -s; i <= s; i++) {
|
||||
for (j = -s; j <= s; j++) {
|
||||
int xx = i + x;
|
||||
int zz = j + z;
|
||||
int xxx = xx << 4;
|
||||
int zzz = zz << 4;
|
||||
if (!getParallaxAccess().isFeatureGenerated(xx, zz)) {
|
||||
burst.queue(() -> {
|
||||
getParallaxAccess().setFeatureGenerated(xx, zz);
|
||||
RNG rng = new RNG(Cache.key(xx, zz)).nextParallelRNG(getEngine().getTarget().getWorld().getSeed());
|
||||
IrisRegion region = getComplex().getRegionStream().get(xxx, zzz);
|
||||
IrisBiome biome = getComplex().getTrueBiomeStream().get(xxx, zzz);
|
||||
generateParallaxFeatures(rng, xx, zz, region, biome);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
burst.complete();
|
||||
|
||||
if(getEngine().getDimension().isPlaceObjects())
|
||||
{
|
||||
burst = MultiBurst.burst.burst(bs);
|
||||
|
||||
for (i = -s; i <= s; i++) {
|
||||
int ii = i;
|
||||
for (j = -s; j <= s; j++) {
|
||||
int jj = j;
|
||||
burst.queue(() -> {
|
||||
KList<Runnable> a = generateParallaxVacuumLayer(ii + x, jj + z);
|
||||
synchronized (a) {
|
||||
after.addAll(a);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
burst.complete();
|
||||
burst = MultiBurst.burst.burst(bs);
|
||||
|
||||
for (i = -s; i <= s; i++) {
|
||||
int ii = i;
|
||||
for (j = -s; j <= s; j++) {
|
||||
int jj = j;
|
||||
burst.queue(() -> generateParallaxLayer(ii + x, jj + z));
|
||||
}
|
||||
}
|
||||
|
||||
burst.complete();
|
||||
}
|
||||
|
||||
MultiBurst.burst.burst(after);
|
||||
getParallaxAccess().setChunkGenerated(x, z);
|
||||
p.end();
|
||||
getEngine().getMetrics().getParallax().put(p.getMilliseconds());
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
Iris.error("Failed to generate parallax in " + x + " " + z);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
default KList<Runnable> generateParallaxVacuumLayer(int x, int z) {
|
||||
KList<Runnable> after = new KList<>();
|
||||
if (getParallaxAccess().isParallaxGenerated(x, z)) {
|
||||
return after;
|
||||
}
|
||||
|
||||
if(getEngine().getDimension().isPlaceObjects())
|
||||
{
|
||||
int xx = x << 4;
|
||||
int zz = z << 4;
|
||||
RNG rng = new RNG(Cache.key(x, z)).nextParallelRNG(getEngine().getTarget().getWorld().getSeed());
|
||||
IrisRegion region = getComplex().getRegionStream().get(xx + 8, zz + 8);
|
||||
IrisBiome biome = getComplex().getTrueBiomeStream().get(xx + 8, zz + 8);
|
||||
after.addAll(generateParallaxJigsaw(rng, x, z, biome, region));
|
||||
generateParallaxSurface(rng, x, z, biome, region, true);
|
||||
generateParallaxMutations(rng, x, z, true);
|
||||
}
|
||||
|
||||
return after;
|
||||
}
|
||||
|
||||
default void generateParallaxLayer(int x, int z, boolean force) {
|
||||
if (!force && getParallaxAccess().isParallaxGenerated(x, z)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int xx = x << 4;
|
||||
int zz = z << 4;
|
||||
getParallaxAccess().setParallaxGenerated(x, z);
|
||||
RNG rng = new RNG(Cache.key(x, z)).nextParallelRNG(getEngine().getTarget().getWorld().getSeed());
|
||||
IrisBiome biome = getComplex().getTrueBiomeStream().get(xx + 8, zz + 8);
|
||||
IrisRegion region = getComplex().getRegionStream().get(xx + 8, zz + 8);
|
||||
generateParallaxSurface(rng, x, z, biome, region, false);
|
||||
generateParallaxMutations(rng, x, z, false);
|
||||
}
|
||||
|
||||
default void generateParallaxFeatures(RNG rng, int cx, int cz, IrisRegion region, IrisBiome biome) {
|
||||
for (IrisFeaturePotential i : getEngine().getDimension().getFeatures()) {
|
||||
placeZone(rng, cx, cz, i);
|
||||
}
|
||||
|
||||
for (IrisFeaturePotential i : region.getFeatures()) {
|
||||
placeZone(rng, cx, cz, i);
|
||||
}
|
||||
|
||||
for (IrisFeaturePotential i : biome.getFeatures()) {
|
||||
placeZone(rng, cx, cz, i);
|
||||
}
|
||||
}
|
||||
|
||||
default void placeZone(RNG rng, int cx, int cz, IrisFeaturePotential i) {
|
||||
if (i.hasZone(rng, cx, cz)) {
|
||||
getParallaxAccess().getMetaRW(cx, cz).getFeatures().add(new IrisFeaturePositional((cx << 4) + rng.nextInt(16), (cz << 4) + rng.nextInt(16), i.getZone()));
|
||||
}
|
||||
}
|
||||
|
||||
default void generateParallaxLayer(int x, int z) {
|
||||
generateParallaxLayer(x, z, false);
|
||||
}
|
||||
|
||||
default KList<Runnable> placeStructure(IrisPosition position, IrisJigsawStructure structure, RNG rng) {
|
||||
KList<Runnable> placeAfter = new KList<>();
|
||||
|
||||
if (structure == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (structure.getFeature() != null) {
|
||||
if (structure.getFeature().getBlockRadius() == 32) {
|
||||
structure.getFeature().setBlockRadius((double) structure.getMaxDimension() / 3);
|
||||
}
|
||||
|
||||
getParallaxAccess().getMetaRW(position.getX() >> 4, position.getZ() >> 4).getFeatures()
|
||||
.add(new IrisFeaturePositional(position.getX(), position.getZ(), structure.getFeature()));
|
||||
}
|
||||
|
||||
placeAfter.addAll(new PlannedStructure(structure, position, rng).place(this, this));
|
||||
return placeAfter;
|
||||
}
|
||||
|
||||
default KList<Runnable> generateParallaxJigsaw(RNG rng, int x, int z, IrisBiome biome, IrisRegion region) {
|
||||
KList<Runnable> placeAfter = new KList<>();
|
||||
|
||||
if(getEngine().getDimension().isPlaceObjects())
|
||||
{
|
||||
boolean placed = false;
|
||||
|
||||
if (getEngine().getDimension().getStronghold() != null) {
|
||||
List<IrisPosition> poss = getEngine().getCompound().getStrongholdPositions();
|
||||
|
||||
if (poss != null) {
|
||||
for (IrisPosition pos : poss) {
|
||||
if (x == pos.getX() >> 4 && z == pos.getZ() >> 4) {
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(getEngine().getDimension().getStronghold());
|
||||
placeAfter.addAll(placeStructure(pos, structure, rng));
|
||||
placed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed) {
|
||||
for (IrisJigsawStructurePlacement i : biome.getJigsawStructures()) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
placeAfter.addAll(placeStructure(position, structure, rng));
|
||||
placed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed) {
|
||||
for (IrisJigsawStructurePlacement i : region.getJigsawStructures()) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
placeAfter.addAll(placeStructure(position, structure, rng));
|
||||
placed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!placed) {
|
||||
for (IrisJigsawStructurePlacement i : getEngine().getDimension().getJigsawStructures()) {
|
||||
if (rng.nextInt(i.getRarity()) == 0) {
|
||||
IrisPosition position = new IrisPosition((x << 4) + rng.nextInt(15), 0, (z << 4) + rng.nextInt(15));
|
||||
IrisJigsawStructure structure = getData().getJigsawStructureLoader().load(i.getStructure());
|
||||
placeAfter.addAll(placeStructure(position, structure, rng));
|
||||
placed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return placeAfter;
|
||||
}
|
||||
|
||||
default void generateParallaxSurface(RNG rng, int x, int z, IrisBiome biome, IrisRegion region, boolean vacuum) {
|
||||
|
||||
for (IrisObjectPlacement i : biome.getSurfaceObjects()) {
|
||||
if (i.isVacuum() != vacuum) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rng.chance(i.getChance() + rng.d(-0.005, 0.005)) && rng.chance(getComplex().getObjectChanceStream().get(x << 4, z << 4))) {
|
||||
try {
|
||||
place(rng, x << 4, z << 4, i);
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
Iris.error("Failed to place objects in the following biome: " + biome.getName());
|
||||
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||
Iris.error("Are these objects missing?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisObjectPlacement i : region.getSurfaceObjects()) {
|
||||
if (i.isVacuum() != vacuum) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rng.chance(i.getChance() + rng.d(-0.005, 0.005)) && rng.chance(getComplex().getObjectChanceStream().get(x << 4, z << 4))) {
|
||||
try {
|
||||
place(rng, x << 4, z << 4, i);
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
Iris.error("Failed to place objects in the following biome: " + biome.getName());
|
||||
Iris.error("Object(s) " + i.getPlace().toString(", ") + " (" + e.getClass().getSimpleName() + ").");
|
||||
Iris.error("Are these objects missing?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void generateParallaxMutations(RNG rng, int x, int z, boolean vacuum) {
|
||||
if (getEngine().getDimension().getMutations().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
searching:
|
||||
for (IrisBiomeMutation k : getEngine().getDimension().getMutations()) {
|
||||
for (int l = 0; l < k.getChecks(); l++) {
|
||||
IrisBiome sa = getComplex().getTrueBiomeStream().get(((x * 16) + rng.nextInt(16)) + rng.i(-k.getRadius(), k.getRadius()), ((z * 16) + rng.nextInt(16)) + rng.i(-k.getRadius(), k.getRadius()));
|
||||
IrisBiome sb = getComplex().getTrueBiomeStream().get(((x * 16) + rng.nextInt(16)) + rng.i(-k.getRadius(), k.getRadius()), ((z * 16) + rng.nextInt(16)) + rng.i(-k.getRadius(), k.getRadius()));
|
||||
|
||||
if (sa.getLoadKey().equals(sb.getLoadKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (k.getRealSideA(this).contains(sa.getLoadKey()) && k.getRealSideB(this).contains(sb.getLoadKey())) {
|
||||
for (IrisObjectPlacement m : k.getObjects()) {
|
||||
if (m.isVacuum() != vacuum) {
|
||||
continue;
|
||||
}
|
||||
|
||||
place(rng.nextParallelRNG((34 * ((x * 30) + (z * 30)) * x * z) + x - z + 1569962), x, z, m);
|
||||
}
|
||||
|
||||
continue searching;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void place(RNG rng, int x, int z, IrisObjectPlacement objectPlacement) {
|
||||
place(rng, x, -1, z, objectPlacement);
|
||||
}
|
||||
|
||||
default void placePiece(RNG rng, int xx, int forceY, int zz, IrisObject v, IrisObjectPlacement p) {
|
||||
int id = rng.i(0, Integer.MAX_VALUE);
|
||||
int maxf = 10000;
|
||||
AtomicBoolean pl = new AtomicBoolean(false);
|
||||
AtomicInteger max = new AtomicInteger(-1);
|
||||
AtomicInteger min = new AtomicInteger(maxf);
|
||||
int h = v.place(xx, forceY, zz, this, p, rng, (b) -> {
|
||||
int xf = b.getX();
|
||||
int yf = b.getY();
|
||||
int zf = b.getZ();
|
||||
getParallaxAccess().setObject(xf, yf, zf, v.getLoadKey() + "@" + id);
|
||||
ParallaxChunkMeta meta = getParallaxAccess().getMetaRW(xf >> 4, zf >> 4);
|
||||
meta.setObjects(true);
|
||||
meta.setMinObject(Math.min(Math.max(meta.getMinObject(), 0), yf));
|
||||
meta.setMaxObject(Math.max(Math.max(meta.getMaxObject(), 0), yf));
|
||||
|
||||
}, null, getData());
|
||||
|
||||
if (p.isVacuum()) {
|
||||
double a = Math.max(v.getW(), v.getD());
|
||||
IrisFeature f = new IrisFeature();
|
||||
f.setConvergeToHeight(h - (v.getH() >> 1));
|
||||
f.setBlockRadius(a);
|
||||
f.setInterpolationRadius(a / 4);
|
||||
f.setInterpolator(InterpolationMethod.BILINEAR_STARCAST_9);
|
||||
f.setStrength(1D);
|
||||
getParallaxAccess().getMetaRW(xx >> 4, zz >> 4).getFeatures().add(new IrisFeaturePositional(xx, zz, f));
|
||||
}
|
||||
}
|
||||
|
||||
default void place(RNG rng, int x, int forceY, int z, IrisObjectPlacement objectPlacement) {
|
||||
for (int i = 0; i < objectPlacement.getDensity(); i++) {
|
||||
IrisObject v = objectPlacement.getScale().get(rng, objectPlacement.getObject(getComplex(), rng));
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
int xx = rng.i(x, x + 16);
|
||||
int zz = rng.i(z, z + 16);
|
||||
int id = rng.i(0, Integer.MAX_VALUE);
|
||||
int maxf = 10000;
|
||||
AtomicBoolean pl = new AtomicBoolean(false);
|
||||
AtomicInteger max = new AtomicInteger(-1);
|
||||
AtomicInteger min = new AtomicInteger(maxf);
|
||||
int h = v.place(xx, forceY, zz, this, objectPlacement, rng, (b) -> {
|
||||
int xf = b.getX();
|
||||
int yf = b.getY();
|
||||
int zf = b.getZ();
|
||||
getParallaxAccess().setObject(xf, yf, zf, v.getLoadKey() + "@" + id);
|
||||
ParallaxChunkMeta meta = getParallaxAccess().getMetaRW(xf >> 4, zf >> 4);
|
||||
meta.setObjects(true);
|
||||
meta.setMinObject(Math.min(Math.max(meta.getMinObject(), 0), yf));
|
||||
meta.setMaxObject(Math.max(Math.max(meta.getMaxObject(), 0), yf));
|
||||
|
||||
}, null, getData());
|
||||
|
||||
if (objectPlacement.isVacuum()) {
|
||||
double a = Math.max(v.getW(), v.getD());
|
||||
IrisFeature f = new IrisFeature();
|
||||
f.setConvergeToHeight(h - (v.getH() >> 1));
|
||||
f.setBlockRadius(a);
|
||||
f.setInterpolationRadius(a / 4);
|
||||
f.setInterpolator(InterpolationMethod.BILINEAR_STARCAST_9);
|
||||
f.setStrength(1D);
|
||||
getParallaxAccess().getMetaRW(xx >> 4, zz >> 4).getFeatures().add(new IrisFeaturePositional(xx, zz, f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default void updateParallaxChunkObjectData(int minY, int maxY, int x, int z, IrisObject v) {
|
||||
ParallaxChunkMeta meta = getParallaxAccess().getMetaRW(x >> 4, z >> 4);
|
||||
meta.setObjects(true);
|
||||
meta.setMaxObject(Math.max(maxY, meta.getMaxObject()));
|
||||
meta.setMinObject(Math.min(minY, Math.max(meta.getMinObject(), 0)));
|
||||
}
|
||||
|
||||
default int computeParallaxSize() {
|
||||
Iris.verbose("Calculating the Parallax Size in Parallel");
|
||||
AtomicInteger xg = new AtomicInteger(0);
|
||||
AtomicInteger zg = new AtomicInteger();
|
||||
xg.set(0);
|
||||
zg.set(0);
|
||||
int jig = 0;
|
||||
KSet<String> objects = new KSet<>();
|
||||
KMap<IrisObjectScale, KList<String>> scalars = new KMap<>();
|
||||
int x = xg.get();
|
||||
int z = zg.get();
|
||||
|
||||
if(getEngine().getDimension().isPlaceObjects())
|
||||
{
|
||||
KList<IrisRegion> r = getAllRegions();
|
||||
KList<IrisBiome> b = getAllBiomes();
|
||||
|
||||
for (IrisBiome i : b) {
|
||||
for (IrisObjectPlacement j : i.getObjects()) {
|
||||
if (j.getScale().canScaleBeyond()) {
|
||||
scalars.put(j.getScale(), j.getPlace());
|
||||
} else {
|
||||
objects.addAll(j.getPlace());
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisJigsawStructurePlacement j : i.getJigsawStructures()) {
|
||||
jig = Math.max(jig, getData().getJigsawStructureLoader().load(j.getStructure()).getMaxDimension());
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisRegion i : r) {
|
||||
for (IrisObjectPlacement j : i.getObjects()) {
|
||||
if (j.getScale().canScaleBeyond()) {
|
||||
scalars.put(j.getScale(), j.getPlace());
|
||||
} else {
|
||||
objects.addAll(j.getPlace());
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisJigsawStructurePlacement j : i.getJigsawStructures()) {
|
||||
jig = Math.max(jig, getData().getJigsawStructureLoader().load(j.getStructure()).getMaxDimension());
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisJigsawStructurePlacement j : getEngine().getDimension().getJigsawStructures()) {
|
||||
jig = Math.max(jig, getData().getJigsawStructureLoader().load(j.getStructure()).getMaxDimension());
|
||||
}
|
||||
|
||||
if (getEngine().getDimension().getStronghold() != null) {
|
||||
try {
|
||||
jig = Math.max(jig, getData().getJigsawStructureLoader().load(getEngine().getDimension().getStronghold()).getMaxDimension());
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
Iris.error("THIS IS THE ONE");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Iris.verbose("Checking sizes for " + Form.f(objects.size()) + " referenced objects.");
|
||||
BurstExecutor e = MultiBurst.burst.burst(objects.size());
|
||||
KMap<String, BlockVector> sizeCache = new KMap<>();
|
||||
for (String i : objects) {
|
||||
e.queue(() -> {
|
||||
try {
|
||||
BlockVector bv = sizeCache.compute(i, (k, v) -> {
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
|
||||
try {
|
||||
return IrisObject.sampleSize(getData().getObjectLoader().findFile(i));
|
||||
} catch (IOException ex) {Iris.reportError(ex);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (bv == null) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
warn(i, bv);
|
||||
|
||||
synchronized (xg) {
|
||||
xg.getAndSet(Math.max(bv.getBlockX(), xg.get()));
|
||||
}
|
||||
|
||||
synchronized (zg) {
|
||||
zg.getAndSet(Math.max(bv.getBlockZ(), zg.get()));
|
||||
}
|
||||
} catch (Throwable ed) {Iris.reportError(ed);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (Map.Entry<IrisObjectScale, KList<String>> entry : scalars.entrySet()) {
|
||||
double ms = entry.getKey().getMaximumScale();
|
||||
for (String j : entry.getValue()) {
|
||||
e.queue(() -> {
|
||||
try {
|
||||
BlockVector bv = sizeCache.compute(j, (k, v) -> {
|
||||
if (v != null) {
|
||||
return v;
|
||||
}
|
||||
|
||||
try {
|
||||
return IrisObject.sampleSize(getData().getObjectLoader().findFile(j));
|
||||
} catch (IOException ioException) {Iris.reportError(ioException);
|
||||
ioException.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (bv == null) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
warnScaled(j, bv, ms);
|
||||
|
||||
synchronized (xg) {
|
||||
xg.getAndSet((int) Math.max(Math.ceil(bv.getBlockX() * ms), xg.get()));
|
||||
}
|
||||
|
||||
synchronized (zg) {
|
||||
zg.getAndSet((int) Math.max(Math.ceil(bv.getBlockZ() * ms), zg.get()));
|
||||
}
|
||||
} catch (Throwable ee) {Iris.reportError(ee);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
e.complete();
|
||||
|
||||
x = xg.get();
|
||||
z = zg.get();
|
||||
|
||||
for (IrisDepositGenerator i : getEngine().getDimension().getDeposits()) {
|
||||
int max = i.getMaxDimension();
|
||||
x = Math.max(max, x);
|
||||
z = Math.max(max, z);
|
||||
}
|
||||
|
||||
for (IrisRegion v : r) {
|
||||
for (IrisDepositGenerator i : v.getDeposits()) {
|
||||
int max = i.getMaxDimension();
|
||||
x = Math.max(max, x);
|
||||
z = Math.max(max, z);
|
||||
}
|
||||
}
|
||||
|
||||
for (IrisBiome v : b) {
|
||||
for (IrisDepositGenerator i : v.getDeposits()) {
|
||||
int max = i.getMaxDimension();
|
||||
x = Math.max(max, x);
|
||||
z = Math.max(max, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x = Math.max(z, x);
|
||||
int u = x;
|
||||
int v = computeFeatureRange();
|
||||
x = Math.max(jig, x);
|
||||
x = Math.max(x, v);
|
||||
x = (Math.max(x, 16) + 16) >> 4;
|
||||
x = x % 2 == 0 ? x + 1 : x;
|
||||
Iris.info("Parallax Size: " + x + " Chunks");
|
||||
Iris.info(" Object Parallax Size: " + u + " (" + ((Math.max(u, 16) + 16) >> 4) + ")");
|
||||
Iris.info(" Jigsaw Parallax Size: " + jig + " (" + ((Math.max(jig, 16) + 16) >> 4) + ")");
|
||||
Iris.info(" Feature Parallax Size: " + v + " (" + ((Math.max(v, 16) + 16) >> 4) + ")");
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
default int computeFeatureRange() {
|
||||
int m = 0;
|
||||
|
||||
for (IrisFeaturePotential i : getAllFeatures()) {
|
||||
m = Math.max(m, i.getZone().getRealSize());
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
default void warn(String ob, BlockVector bv) {
|
||||
if (Math.max(bv.getBlockX(), bv.getBlockZ()) > 128) {
|
||||
Iris.warn("Object " + ob + " has a large size (" + bv + ") and may increase memory usage!");
|
||||
}
|
||||
}
|
||||
|
||||
default void warnScaled(String ob, BlockVector bv, double ms) {
|
||||
if (Math.max(bv.getBlockX(), bv.getBlockZ()) > 128) {
|
||||
Iris.warn("Object " + ob + " has a large size (" + bv + ") and may increase memory usage! (Object scaled up to " + Form.pc(ms, 2) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
default int getHighest(int x, int z) {
|
||||
return getHighest(x, z, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
default int getHighest(int x, int z, boolean ignoreFluid) {
|
||||
return ignoreFluid ? trueHeight(x, z) : Math.max(trueHeight(x, z), getEngine().getDimension().getFluidHeight());
|
||||
}
|
||||
|
||||
default int trueHeight(int x, int z) {
|
||||
return getComplex().getTrueHeightStream().get(x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void set(int x, int y, int z, BlockData d) {
|
||||
getParallaxAccess().setBlock(x, y, z, d);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void setTile(int x, int y, int z, TileData<? extends TileState> d) {
|
||||
getParallaxAccess().setTile(x, y, z, d);
|
||||
}
|
||||
|
||||
@Override
|
||||
default BlockData get(int x, int y, int z) {
|
||||
BlockData block = getParallaxAccess().getBlock(x, y, z);
|
||||
|
||||
if (block == null) {
|
||||
return AIR;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isPreventingDecay() {
|
||||
return getEngine().getDimension().isPreventLeafDecay();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isSolid(int x, int y, int z) {
|
||||
return B.isSolid(get(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isUnderwater(int x, int z) {
|
||||
return getHighest(x, z, true) <= getFluidHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
default int getFluidHeight() {
|
||||
return getEngine().getDimension().getFluidHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isDebugSmartBore() {
|
||||
return getEngine().getDimension().isDebugSmartBore();
|
||||
}
|
||||
|
||||
default void close() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisEffect;
|
||||
import com.volmit.iris.object.IrisRegion;
|
||||
import com.volmit.iris.util.J;
|
||||
import com.volmit.iris.util.M;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
@Data
|
||||
public class EnginePlayer {
|
||||
private final Engine engine;
|
||||
private final Player player;
|
||||
private IrisBiome biome;
|
||||
private IrisRegion region;
|
||||
private Location lastLocation;
|
||||
private long lastSample;
|
||||
|
||||
public EnginePlayer(Engine engine, Player player) {
|
||||
this.engine = engine;
|
||||
this.player = player;
|
||||
lastLocation = player.getLocation().clone();
|
||||
lastSample = -1;
|
||||
sample();
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
sample();
|
||||
|
||||
J.a(() -> {
|
||||
if (region != null) {
|
||||
for (IrisEffect j : region.getEffects()) {
|
||||
try {
|
||||
j.apply(player, getEngine());
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (biome != null) {
|
||||
for (IrisEffect j : biome.getEffects()) {
|
||||
try {
|
||||
j.apply(player, getEngine());
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public long ticksSinceLastSample() {
|
||||
return M.ms() - lastSample;
|
||||
}
|
||||
|
||||
public void sample() {
|
||||
try {
|
||||
if (ticksSinceLastSample() > 55 && player.getLocation().distanceSquared(lastLocation) > 9 * 9) {
|
||||
lastLocation = player.getLocation().clone();
|
||||
lastSample = M.ms();
|
||||
sampleBiomeRegion();
|
||||
}
|
||||
} catch (Throwable e) {Iris.reportError(e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void sampleBiomeRegion() {
|
||||
Location l = player.getLocation();
|
||||
biome = engine.getBiome(l);
|
||||
region = engine.getRegion(l);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.manager.IrisDataManager;
|
||||
import com.volmit.iris.object.IrisDimension;
|
||||
import com.volmit.iris.engine.parallax.ParallaxWorld;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import lombok.Data;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@Data
|
||||
public class EngineTarget {
|
||||
private final MultiBurst burster;
|
||||
private final IrisDimension dimension;
|
||||
private World world;
|
||||
private final int height;
|
||||
private final IrisDataManager data;
|
||||
private final ParallaxWorld parallaxWorld;
|
||||
private final boolean inverted;
|
||||
|
||||
public EngineTarget(World world, IrisDimension dimension, IrisDataManager data, int height, boolean inverted, int threads) {
|
||||
this.world = world;
|
||||
this.height = height;
|
||||
this.dimension = dimension;
|
||||
this.data = data;
|
||||
// TODO: WARNING HEIGHT
|
||||
this.parallaxWorld = new ParallaxWorld(256, new File(world.getWorldFolder(), "iris/" + dimension.getLoadKey() + "/parallax"));
|
||||
this.inverted = inverted;
|
||||
this.burster = new MultiBurst(threads);
|
||||
}
|
||||
|
||||
public void updateWorld(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
public EngineTarget(World world, IrisDimension dimension, IrisDataManager data, int height, int threads) {
|
||||
this(world, dimension, data, height, false, threads);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 org.bukkit.Chunk;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntitySpawnEvent;
|
||||
|
||||
@SuppressWarnings("EmptyMethod")
|
||||
public interface EngineWorldManager {
|
||||
void close();
|
||||
|
||||
void onEntitySpawn(EntitySpawnEvent e);
|
||||
|
||||
void onTick();
|
||||
|
||||
void onSave();
|
||||
|
||||
void spawnInitialEntities(Chunk chunk);
|
||||
|
||||
void onBlockBreak(BlockBreakEvent e);
|
||||
|
||||
void onBlockPlace(BlockPlaceEvent e);
|
||||
}
|
||||
40
src/main/java/com/volmit/iris/engine/framework/Fallible.java
Normal file
40
src/main/java/com/volmit/iris/engine/framework/Fallible.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface Fallible {
|
||||
default void fail(String error) {
|
||||
try {
|
||||
throw new RuntimeException();
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
fail(error, e);
|
||||
}
|
||||
}
|
||||
|
||||
default void fail(Throwable e) {
|
||||
fail("Failed to generate", e);
|
||||
}
|
||||
|
||||
void fail(String error, Throwable e);
|
||||
|
||||
boolean hasFailed();
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.manager.IrisDataManager;
|
||||
import com.volmit.iris.gui.Renderer;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisObjectPlacement;
|
||||
import com.volmit.iris.object.IrisRegion;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import com.volmit.iris.engine.parallax.ParallaxAccess;
|
||||
|
||||
public interface GeneratorAccess extends DataProvider, Renderer {
|
||||
IrisRegion getRegion(int x, int z);
|
||||
|
||||
ParallaxAccess getParallaxAccess();
|
||||
|
||||
IrisDataManager getData();
|
||||
|
||||
IrisBiome getCaveBiome(int x, int z);
|
||||
|
||||
IrisBiome getSurfaceBiome(int x, int z);
|
||||
|
||||
int getHeight(int x, int z);
|
||||
|
||||
default IrisBiome getBiome(int x, int y, int z) {
|
||||
if (y <= getHeight(x, z) - 2) {
|
||||
return getCaveBiome(x, z);
|
||||
}
|
||||
|
||||
return getSurfaceBiome(x, z);
|
||||
}
|
||||
|
||||
default PlacedObject getObjectPlacement(int x, int y, int z) {
|
||||
String objectAt = getParallaxAccess().getObject(x, y, z);
|
||||
|
||||
if (objectAt == null || objectAt.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] v = objectAt.split("\\Q@\\E");
|
||||
String object = v[0];
|
||||
int id = Integer.parseInt(v[1]);
|
||||
IrisRegion region = getRegion(x, z);
|
||||
|
||||
for (IrisObjectPlacement i : region.getObjects()) {
|
||||
if (i.getPlace().contains(object)) {
|
||||
return new PlacedObject(i, getData().getObjectLoader().load(object), id, x, z);
|
||||
}
|
||||
}
|
||||
|
||||
IrisBiome biome = getBiome(x, y, z);
|
||||
|
||||
for (IrisObjectPlacement i : biome.getObjects()) {
|
||||
if (i.getPlace().contains(object)) {
|
||||
return new PlacedObject(i, getData().getObjectLoader().load(object), id, x, z);
|
||||
}
|
||||
}
|
||||
|
||||
return new PlacedObject(null, getData().getObjectLoader().load(object), id, x, z);
|
||||
}
|
||||
|
||||
int getCacheID();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface Hotloadable {
|
||||
void hotload();
|
||||
}
|
||||
240
src/main/java/com/volmit/iris/engine/framework/IrisAccess.java
Normal file
240
src/main/java/com/volmit/iris/engine/framework/IrisAccess.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.manager.IrisDataManager;
|
||||
import com.volmit.iris.object.IrisBiome;
|
||||
import com.volmit.iris.object.IrisRegion;
|
||||
import com.volmit.iris.engine.data.DirectWorldWriter;
|
||||
import com.volmit.iris.engine.data.DataProvider;
|
||||
import com.volmit.iris.engine.parallel.MultiBurst;
|
||||
import com.volmit.iris.util.*;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
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 {
|
||||
|
||||
void directWriteMCA(World w, int x, int z, DirectWorldWriter writer, MultiBurst burst);
|
||||
|
||||
void directWriteChunk(World w, int x, int z, DirectWorldWriter writer);
|
||||
|
||||
int getGenerated();
|
||||
|
||||
double getGeneratedPerSecond();
|
||||
|
||||
void printMetrics(CommandSender sender);
|
||||
|
||||
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);
|
||||
|
||||
IrisDataManager getData();
|
||||
|
||||
int getHeight(int x, int y, int z);
|
||||
|
||||
int getThreadCount();
|
||||
|
||||
void changeThreadCount(int m);
|
||||
|
||||
void regenerate(int x, int z);
|
||||
|
||||
void close();
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
EngineTarget getTarget();
|
||||
|
||||
EngineCompound getCompound();
|
||||
|
||||
boolean isFailing();
|
||||
|
||||
boolean isStudio();
|
||||
|
||||
default Location lookForBiome(IrisBiome biome, long timeout, Consumer<Integer> triesc) {
|
||||
ChronoLatch cl = new ChronoLatch(250, false);
|
||||
long s = M.ms();
|
||||
int cpus = 2 + (Runtime.getRuntime().availableProcessors() / 2);
|
||||
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);
|
||||
AtomicReference<Location> location = new AtomicReference<>();
|
||||
|
||||
for (int i = 0; i < cpus; i++) {
|
||||
J.a(() -> {
|
||||
try {
|
||||
Engine e;
|
||||
IrisBiome b;
|
||||
int x, y, z;
|
||||
|
||||
while (!found.get()) {
|
||||
try {
|
||||
synchronized (engines) {
|
||||
e = engines.getRandom();
|
||||
x = RNG.r.i(-29999970, 29999970);
|
||||
y = RNG.r.i(0, e.getHeight() - 1);
|
||||
z = RNG.r.i(-29999970, 29999970);
|
||||
|
||||
b = e.getBiome(x, y, 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(), x, y, 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) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return location.get();
|
||||
}
|
||||
|
||||
default Location lookForRegion(IrisRegion reg, long timeout, Consumer<Integer> triesc) {
|
||||
ChronoLatch cl = new ChronoLatch(3000, false);
|
||||
long s = M.ms();
|
||||
int cpus = 2 + (Runtime.getRuntime().availableProcessors() / 2);
|
||||
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);
|
||||
AtomicReference<Location> location = new AtomicReference<>();
|
||||
|
||||
for (int i = 0; i < cpus; i++) {
|
||||
J.a(() -> {
|
||||
Engine e;
|
||||
IrisRegion b;
|
||||
int x, z;
|
||||
|
||||
while (!found.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(), 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());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
triesc.accept(tries.get());
|
||||
return location.get();
|
||||
}
|
||||
|
||||
void clearRegeneratedLists(int x, int z);
|
||||
|
||||
void precache(World world, int x, int z);
|
||||
|
||||
int getPrecacheSize();
|
||||
|
||||
Chunk generatePaper(World world, int cx, int cz);
|
||||
|
||||
default int getParallaxChunkCount() {
|
||||
int v = 0;
|
||||
|
||||
for (int i = 0; i < getCompound().getSize(); i++) {
|
||||
v += getCompound().getEngine(i).getParallax().getChunkCount();
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public interface IrisAccessProvider {
|
||||
IrisAccess getAccess();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.object.InventorySlotType;
|
||||
import com.volmit.iris.object.IrisLootReference;
|
||||
import com.volmit.iris.object.IrisLootTable;
|
||||
import com.volmit.iris.util.KList;
|
||||
import com.volmit.iris.util.RNG;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
|
||||
public interface LootProvider {
|
||||
void scramble(Inventory inventory, RNG rng);
|
||||
|
||||
void injectTables(KList<IrisLootTable> list, IrisLootReference r);
|
||||
|
||||
KList<IrisLootTable> getLootTables(RNG rng, Block b);
|
||||
|
||||
void addItems(boolean debug, Inventory inv, RNG rng, KList<IrisLootTable> tables, InventorySlotType slot, int x, int y, int z, int mgf);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.object.IrisObject;
|
||||
import com.volmit.iris.object.IrisObjectPlacement;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class PlacedObject {
|
||||
@Nullable
|
||||
private IrisObjectPlacement placement;
|
||||
@Nullable
|
||||
private IrisObject object;
|
||||
private int id;
|
||||
private int xx;
|
||||
private int zz;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.engine.hunk.Hunk;
|
||||
import com.volmit.iris.util.B;
|
||||
import com.volmit.iris.util.TerrainChunk;
|
||||
import lombok.Data;
|
||||
import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@Data
|
||||
public class PregeneratedData {
|
||||
private final Hunk<BlockData> blocks;
|
||||
private final Hunk<BlockData> post;
|
||||
private final Hunk<Biome> biomes;
|
||||
private final AtomicBoolean postMod;
|
||||
|
||||
public PregeneratedData(int height) {
|
||||
postMod = new AtomicBoolean(false);
|
||||
blocks = Hunk.newAtomicHunk(16, height, 16);
|
||||
biomes = Hunk.newAtomicHunk(16, height, 16);
|
||||
Hunk<BlockData> p = Hunk.newMappedHunkSynced(16, height, 16);
|
||||
post = p.trackWrite(postMod);
|
||||
}
|
||||
|
||||
public Runnable inject(TerrainChunk tc) {
|
||||
blocks.iterateSync((x, y, z, b) -> {
|
||||
if (b != null) {
|
||||
tc.setBlock(x, y, z, b);
|
||||
}
|
||||
|
||||
Biome bf = biomes.get(x, y, z);
|
||||
if (bf != null) {
|
||||
tc.setBiome(x, y, z, bf);
|
||||
}
|
||||
});
|
||||
|
||||
if (postMod.get()) {
|
||||
return () -> Hunk.view((ChunkGenerator.ChunkData) tc).insertSoftly(0, 0, 0, post, (b) -> b == null || B.isAirOrFluid(b));
|
||||
}
|
||||
|
||||
return () -> {
|
||||
};
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user