Look ma, no Bukkit API in the core package

This commit is contained in:
dfsek
2020-12-11 17:30:17 -07:00
parent 7ee1d2c391
commit 5bf699cba9
345 changed files with 1352 additions and 2642 deletions

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra;
import com.dfsek.terra.api.gaea.profiler.DataType;
import com.dfsek.terra.api.gaea.profiler.Measurement;
import com.dfsek.terra.api.gaea.profiler.WorldProfiler;
import com.dfsek.terra.api.generic.world.World;
public class TerraProfiler extends WorldProfiler {
public TerraProfiler(World w) {
super(w);
this
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "FloraTime")
.addMeasurement(new Measurement(10000000, DataType.PERIOD_MILLISECONDS), "TreeTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "OreTime")
.addMeasurement(new Measurement(5000000, DataType.PERIOD_MILLISECONDS), "CaveTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "StructureTime")
.addMeasurement(new Measurement(1500000, DataType.PERIOD_MILLISECONDS), "ElevationTime");
}
}

View File

@@ -0,0 +1,80 @@
package com.dfsek.terra;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.biome.BiomeZone;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.biome.grid.master.TerraRadialBiomeGrid;
import com.dfsek.terra.biome.grid.master.TerraStandardBiomeGrid;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.base.ConfigPackTemplate;
import com.dfsek.terra.config.builder.biomegrid.BiomeGridBuilder;
import com.dfsek.terra.debug.Debug;
import com.dfsek.terra.generation.TerraChunkGenerator;
public class TerraWorld {
private final TerraBiomeGrid grid;
private final BiomeZone zone;
private final ConfigPack config;
private boolean safe;
private final TerraProfiler profiler;
public TerraWorld(World w, ConfigPack c, TerraPlugin main) {
safe = true;
config = c;
profiler = new TerraProfiler(w);
ConfigPackTemplate template = config.getTemplate();
int zoneSize = template.getGrids().size();
BiomeGrid[] definedGrids = new BiomeGrid[zoneSize];
for(int i = 0; i < zoneSize; i++) {
String partName = template.getGrids().get(i);
try {
BiomeGridBuilder g = config.getBiomeGrid(partName);
BiomeGrid b = g.build(w, c);
definedGrids[i] = b;
} catch(NullPointerException e) {
safe = false;
Debug.stack(e);
main.getLogger().severe("No such BiomeGrid " + partName);
main.getLogger().severe("Please check configuration files for errors. Configuration errors will have been reported during initialization.");
main.getLogger().severe("ONLY report this to Terra if you are SURE your config is error-free.");
main.getLogger().severe("Terrain will NOT generate properly at this point. Correct your config before using your server!");
}
}
zone = new BiomeZone(w, c, definedGrids);
if(template.getGridType().equals(TerraBiomeGrid.Type.RADIAL)) {
BiomeGrid internal = config.getBiomeGrid(template.getRadialInternalGrid()).build(w, c);
grid = new TerraRadialBiomeGrid(w, template.getGridFreqX(), template.getGridFreqZ(), zone, config, template.getRadialGridRadius(), internal);
} else grid = new TerraStandardBiomeGrid(w, template.getGridFreqX(), template.getGridFreqZ(), zone, config);
}
public static boolean isTerraWorld(World w) {
return w.getGenerator() instanceof TerraChunkGenerator;
}
public TerraBiomeGrid getGrid() {
return grid;
}
public ConfigPack getConfig() {
return config;
}
public BiomeZone getZone() {
return zone;
}
public boolean isSafe() {
return safe;
}
public TerraProfiler getProfiler() {
return profiler;
}
}

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api;
import com.dfsek.terra.api.generic.Handle;
import com.dfsek.terra.api.generic.world.vector.Location;
public interface Entity extends Handle {
Location getLocation();
}

View File

@@ -0,0 +1,63 @@
package com.dfsek.terra.api;
import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.math.Range;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.biome.palette.PaletteHolder;
import com.dfsek.terra.biome.palette.PaletteLayer;
import com.dfsek.terra.carving.CarverPalette;
import com.dfsek.terra.config.loaders.ImageLoaderLoader;
import com.dfsek.terra.config.loaders.MaterialSetLoader;
import com.dfsek.terra.config.loaders.ProbabilityCollectionLoader;
import com.dfsek.terra.config.loaders.RangeLoader;
import com.dfsek.terra.config.loaders.config.FloraLayerLoader;
import com.dfsek.terra.config.loaders.config.GridSpawnLoader;
import com.dfsek.terra.config.loaders.config.NoiseBuilderLoader;
import com.dfsek.terra.config.loaders.config.OreConfigLoader;
import com.dfsek.terra.config.loaders.config.OreHolderLoader;
import com.dfsek.terra.config.loaders.config.TreeLayerLoader;
import com.dfsek.terra.config.loaders.palette.CarverPaletteLoader;
import com.dfsek.terra.config.loaders.palette.PaletteHolderLoader;
import com.dfsek.terra.config.loaders.palette.PaletteLayerLoader;
import com.dfsek.terra.generation.config.NoiseBuilder;
import com.dfsek.terra.generation.items.flora.FloraLayer;
import com.dfsek.terra.generation.items.flora.TerraFlora;
import com.dfsek.terra.generation.items.ores.Ore;
import com.dfsek.terra.generation.items.ores.OreConfig;
import com.dfsek.terra.generation.items.ores.OreHolder;
import com.dfsek.terra.generation.items.tree.TreeLayer;
import com.dfsek.terra.image.ImageLoader;
import com.dfsek.terra.procgen.GridSpawn;
import com.dfsek.terra.util.MaterialSet;
public class GenericLoaders implements LoaderRegistrar {
private final TerraPlugin main;
public GenericLoaders(TerraPlugin main) {
this.main = main;
}
@Override
public void register(TypeRegistry registry) {
registry.registerLoader(ProbabilityCollection.class, new ProbabilityCollectionLoader())
.registerLoader(Range.class, new RangeLoader())
.registerLoader(CarverPalette.class, new CarverPaletteLoader())
.registerLoader(GridSpawn.class, new GridSpawnLoader())
.registerLoader(PaletteHolder.class, new PaletteHolderLoader())
.registerLoader(PaletteLayer.class, new PaletteLayerLoader())
.registerLoader(FloraLayer.class, new FloraLayerLoader())
.registerLoader(Ore.Type.class, (t, o, l) -> Ore.Type.valueOf((String) o))
.registerLoader(OreConfig.class, new OreConfigLoader())
.registerLoader(NoiseBuilder.class, new NoiseBuilderLoader())
.registerLoader(TreeLayer.class, new TreeLayerLoader(main))
.registerLoader(MaterialSet.class, new MaterialSetLoader())
.registerLoader(OreHolder.class, new OreHolderLoader())
.registerLoader(ImageLoader.class, new ImageLoaderLoader())
.registerLoader(TerraBiomeGrid.Type.class, (t, o, l) -> TerraBiomeGrid.Type.valueOf((String) o))
.registerLoader(ImageLoader.Channel.class, (t, o, l) -> ImageLoader.Channel.valueOf((String) o))
.registerLoader(ImageLoader.Align.class, (t, o, l) -> ImageLoader.Align.valueOf((String) o))
.registerLoader(TerraFlora.Search.class, (t, o, l) -> TerraFlora.Search.valueOf((String) o));
}
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api;
import com.dfsek.tectonic.loading.TypeRegistry;
public interface LoaderRegistrar {
void register(TypeRegistry registry);
}

View File

@@ -0,0 +1,4 @@
package com.dfsek.terra.api;
public interface Player extends Entity {
}

View File

@@ -0,0 +1,26 @@
package com.dfsek.terra.api.gaea;
import com.dfsek.terra.api.generic.TerraPlugin;
public class Debug {
public static TerraPlugin main;
public static void setMain(TerraPlugin main) {
Debug.main = main;
}
public static void info(String message) {
if(Gaea.isDebug()) main.getLogger().info(message);
}
public static void warn(String message) {
if(Gaea.isDebug()) main.getLogger().warning(message);
}
public static void error(String message) {
if(Gaea.isDebug()) main.getLogger().severe(message);
}
public static void stack(Exception e) {
if(Gaea.isDebug()) e.printStackTrace();
}
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.gaea;
import com.dfsek.terra.api.generic.world.World;
import java.io.File;
public class Gaea {
private static boolean debug;
public static File getGaeaFolder(World w) {
File f = new File(w.getWorldFolder(), "gaea");
f.mkdirs();
return f;
}
public static boolean isDebug() {
return debug;
}
}

View File

@@ -0,0 +1,41 @@
package com.dfsek.terra.api.gaea.biome;
import com.dfsek.terra.api.generic.world.World;
/**
* Interface to be implemented by a custom generator's Biome enum.<br>
* Represents a custom biome, and contains methods to retrieve information about each type.
*/
public interface Biome {
/**
* Gets the Vanilla biome to represent the custom biome.
*
* @return Biome - The Vanilla biome.
*/
com.dfsek.terra.api.generic.world.Biome getVanillaBiome();
/**
* Gets the BiomeTerrain instance used to generate the biome.
*
* @return BiomeTerrain - The terrain generation instance.
*/
Generator getGenerator();
/**
* Returns the Decorator instance containing information about the population in the biome.
*
* @return Decorator - the Decorator instance.
*/
Decorator getDecorator();
/**
* Gets the BiomeTerrain instance used to generate the biome in this world.
*
* @return BiomeTerrain - The terrain generation instance.
*/
default Generator getGenerator(World w) {
return getGenerator();
}
}

View File

@@ -0,0 +1,121 @@
package com.dfsek.terra.api.gaea.biome;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.api.generic.world.vector.Location;
public abstract class BiomeGrid {
private final FastNoiseLite noiseX;
private final FastNoiseLite noiseZ;
private final World world;
private final int sizeX;
private final int sizeZ;
private Biome[][] grid;
public BiomeGrid(World w, double freq1, double freq2, int sizeX, int sizeZ) {
this.sizeX = sizeX;
this.sizeZ = sizeZ;
this.world = w;
this.noiseX = new FastNoiseLite((int) w.getSeed());
this.noiseZ = new FastNoiseLite((int) w.getSeed() + 1);
this.noiseX.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
this.noiseX.setFractalType(FastNoiseLite.FractalType.FBm);
this.noiseX.setFractalOctaves(4);
this.noiseZ.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
this.noiseZ.setFractalType(FastNoiseLite.FractalType.FBm);
this.noiseZ.setFractalOctaves(4);
this.noiseX.setFrequency(freq1);
this.noiseZ.setFrequency(freq2);
}
/**
* Gets the biome at a pair of coordinates.
*
* @param x - X-coordinate at which to fetch biome
* @param z - Z-coordinate at which to fetch biome
* @return Biome - Biome at the given coordinates.
*/
public Biome getBiome(int x, int z, GenerationPhase phase) {
return grid[getBiomeNoiseX(x, z)][getBiomeNoiseZ(x, z)];
}
/**
* Gets the biome at a location.
*
* @param l - The location at which to fetch the biome.
* @return Biome - Biome at the given coordinates.
*/
public Biome getBiome(Location l) {
return getBiome(l, GenerationPhase.POST_GEN);
}
public double[] getRawNoise(int x, int z) {
return new double[] {noiseX.getNoise(x, z), noiseZ.getNoise(x, z)};
}
/**
* Get the raw X-noise for coordinates in the Grid.
*
* @param x X coordinate
* @param z Z coordinate
* @return Normalized noise
*/
public int getBiomeNoiseX(int x, int z) {
return normalize(noiseX.getNoise(x, z), sizeX);
}
/**
* Get the raw Z-noise for coordinates in the Grid.
*
* @param x X coordinate
* @param z Z coordinate
* @return Normalized noise
*/
public int getBiomeNoiseZ(int x, int z) {
return normalize(noiseZ.getNoise(x, z), sizeZ);
}
public Biome[][] getGrid() {
return grid;
}
public void setGrid(Biome[][] grid) {
if(grid.length != sizeX) throw new IllegalArgumentException("Invalid length for grid, expected " + sizeX + ", got " + grid.length);
for(Biome[] gridLayer : grid) {
if(gridLayer.length != sizeZ) throw new IllegalArgumentException("Invalid length for grid layer, expected " + sizeZ + ", got " + gridLayer.length);
}
this.grid = grid;
}
public Biome getBiome(Location l, GenerationPhase phase) {
double biomeNoise = noiseX.getNoise(l.getBlockX(), l.getBlockZ());
double climateNoise = noiseZ.getNoise(l.getBlockX(), l.getBlockZ());
return grid[normalize(biomeNoise, sizeX)][normalize(climateNoise, sizeZ)];
}
public World getWorld() {
return world;
}
public int getSizeX() {
return sizeX;
}
public int getSizeZ() {
return sizeZ;
}
/**
* Takes a noise input and normalizes it to a value between 0 and 15 inclusive.
*
* @param i - The noise value to normalize.
* @return int - The normalized value.
*/
protected int normalize(double i, int range) {
return NormalizationUtil.normalize(i, range, 4);
}
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.gaea.biome;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.tree.Tree;
import com.dfsek.terra.api.gaea.world.Flora;
public abstract class Decorator {
public abstract ProbabilityCollection<Tree> getTrees();
public abstract int getTreeDensity();
public abstract boolean overrideStructureChance();
public abstract ProbabilityCollection<Flora> getFlora();
public abstract int getFloraChance();
}

View File

@@ -0,0 +1,40 @@
package com.dfsek.terra.api.gaea.biome;
import com.dfsek.terra.api.gaea.math.Interpolator;
import com.dfsek.terra.api.gaea.world.palette.Palette;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.api.generic.world.block.BlockData;
public abstract class Generator {
/**
* Gets the 3D noise at a pair of coordinates using the provided FastNoiseLite instance.
*
* @param x - The x coordinate.
* @param y - The y coordinate.
* @param z - The z coordinate.
* @return double - Noise value at the specified coordinates.
*/
public abstract double getNoise(World w, int x, int y, int z);
/**
* Gets the BlocPalette to generate the biome with.
*
* @return BlocPalette - The biome's palette.
*/
public abstract Palette<BlockData> getPalette(int y);
/**
* Returns true if the biome should be interpolated just once, false to use advanced interpolation + blending.
* @return Whether biome should use minimal interpolation
*/
public abstract boolean useMinimalInterpolation();
/**
* Get the type of interpolation to use in this biome.
* @return Interpolation type
*/
public Interpolator.Type getInterpolationType() {
return Interpolator.Type.LINEAR;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api.gaea.generation;
/**
* The phase of terrain generation. Used for modifying values based on the phase of generation.
*/
public enum GenerationPhase {
BASE, POPULATE, GENERATION_POPULATE, PALETTE_APPLY, POST_GEN
}

View File

@@ -0,0 +1,37 @@
package com.dfsek.terra.api.gaea.lang;
import com.dfsek.terra.api.generic.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Language {
public Language(File file) throws IOException {
load(file);
}
public void load(@NotNull File file) throws IOException {
}
@SuppressWarnings("unchecked")
public Message getMessage(String id) {
Object m = null;
Message temp;
if(m instanceof List) {
temp = new MultiLineMessage((List<String>) m);
} else if(m instanceof String) {
temp = new SingleLineMessage((String) m);
} else return new SingleLineMessage("message:" + id + ":translation_undefined");
if(temp.isEmpty()) return new SingleLineMessage("message:" + id + ":translation_undefined");
return temp;
}
public void log(String messageID, Level level, Logger logger, String... args) {
getMessage(messageID).log(logger, level, args);
}
public void send(String messageID, CommandSender sender, String... args) {
getMessage(messageID).send(sender, args);
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.gaea.lang;
import com.dfsek.terra.api.generic.CommandSender;
import java.util.logging.Level;
import java.util.logging.Logger;
public interface Message {
void log(Logger logger, Level level, String... args);
void send(CommandSender sender, String... args);
boolean isEmpty();
}

View File

@@ -0,0 +1,33 @@
package com.dfsek.terra.api.gaea.lang;
import com.dfsek.terra.api.generic.CommandSender;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
public class MultiLineMessage implements Message {
private final List<String> message;
public MultiLineMessage(List<String> message) {
this.message = message;
}
@Override
public void log(Logger logger, Level level, String... args) {
for(String line: message) {
logger.log(level, String.format(line, Arrays.asList(args).toArray()));
}
}
@Override
public void send(CommandSender sender, String... args) {
for(String line: message) {
sender.sendMessage(String.format(line, Arrays.asList(args).toArray()));
}
}
@Override
public boolean isEmpty() {
return message == null || message.isEmpty();
}
}

View File

@@ -0,0 +1,29 @@
package com.dfsek.terra.api.gaea.lang;
import com.dfsek.terra.api.generic.CommandSender;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SingleLineMessage implements Message {
private final String message;
public SingleLineMessage(String message) {
this.message = message;
}
@Override
public void log(Logger logger, Level level, String... args) {
logger.log(level, String.format(message, Arrays.asList(args).toArray()));
}
@Override
public void send(CommandSender sender, String... args) {
sender.sendMessage(String.format(message, Arrays.asList(args).toArray()));
}
@Override
public boolean isEmpty() {
return message == null || message.equals("");
}
}

View File

@@ -0,0 +1,128 @@
package com.dfsek.terra.api.gaea.math;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.api.gaea.biome.Generator;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.api.generic.world.World;
import net.jafama.FastMath;
/**
* Class to abstract away the 16 Interpolators needed to generate a chunk.<br>
* Contains method to get interpolated noise at a coordinate within the chunk.
*/
public class ChunkInterpolator3 {
private final Interpolator3[][][] interpGrid = new Interpolator3[4][64][4];
private final Generator[][] gens = new Generator[7][7];
private final boolean[][] needsBiomeInterp = new boolean[5][5];
private final double[][][] noiseStorage = new double[7][7][65];
private final int xOrigin;
private final int zOrigin;
private final World w;
/**
* Instantiates a 3D ChunkInterpolator at a pair of chunk coordinates, with a BiomeGrid and FastNoiseLite instance.
*
* @param chunkX X coordinate of the chunk.
* @param chunkZ Z coordinate of the chunk.
* @param grid BiomeGrid to use for noise fetching.
*/
public ChunkInterpolator3(World w, int chunkX, int chunkZ, BiomeGrid grid) {
this.xOrigin = chunkX << 4;
this.zOrigin = chunkZ << 4;
this.w = w;
for(int x = -1; x < 6; x++) {
for(int z = -1; z < 6; z++) {
gens[x + 1][z + 1] = grid.getBiome(xOrigin + (x << 2), zOrigin + (z << 2), GenerationPhase.BASE).getGenerator();
}
}
for(int x = 0; x < 5; x++) {
for(int z = 0; z < 5; z++) {
needsBiomeInterp[x][z] = compareGens(x+1, z+1);
}
}
storeNoise();
for(byte x = 0; x < 4; x++) {
for(byte z = 0; z < 4; z++) {
for(int y = 0; y < 64; y++) {
interpGrid[x][y][z] = new Interpolator3(
biomeAvg(x, y, z),
biomeAvg(x + 1, y, z),
biomeAvg(x, y + 1, z),
biomeAvg(x + 1, y + 1, z),
biomeAvg(x, y, z + 1),
biomeAvg(x + 1, y, z + 1),
biomeAvg(x, y + 1, z + 1),
biomeAvg(x + 1, y + 1, z + 1), gens[x+1][z+1].getInterpolationType());
}
}
}
}
private boolean compareGens(int x, int z) {
Generator comp = gens[x][z];
if(!comp.equals(gens[x+1][z])) return true;
if(!comp.equals(gens[x][z+1])) return true;
if(!comp.equals(gens[x-1][z])) return true;
if(!comp.equals(gens[x][z-1])) return true;
if(!comp.equals(gens[x+1][z+1])) return true;
if(!comp.equals(gens[x-1][z-1])) return true;
if(!comp.equals(gens[x+1][z-1])) return true;
return !comp.equals(gens[x - 1][z + 1]);
}
private void storeNoise() {
for(byte x = - 1; x < 6; x++) {
for(byte z = - 1; z < 6; z++) {
for(int y = 0; y < 64; y++) {
noiseStorage[x + 1][z + 1][y] = gens[x + 1][z + 1].getNoise(w, (x << 2) + xOrigin, y << 2, (z << 2) + zOrigin);
}
}
}
}
private double biomeAvg(int x, int y, int z) {
if(needsBiomeInterp[x][z]) return (noiseStorage[x + 2][z + 1][y]
+ noiseStorage[x][z + 1][y]
+ noiseStorage[x + 1][z + 2][y]
+ noiseStorage[x + 1][z][y]
+ noiseStorage[x][z][y]
+ noiseStorage[x + 2][z + 2][y]
+ noiseStorage[x + 2][z][y]
+ noiseStorage[x][z + 2][y]
+ noiseStorage[x + 1][z + 1][y]
) / 9D;
else {
if(gens[x+1][z+1].useMinimalInterpolation()) return noiseStorage[x+1][z+1][y];
else return (noiseStorage[x + 2][z + 1][y]
+ noiseStorage[x][z + 1][y]
+ noiseStorage[x + 1][z + 2][y]
+ noiseStorage[x + 1][z][y]
+ noiseStorage[x+1][z+1][y]) / 5D;
}
}
/**
* Gets the noise at a pair of internal chunk coordinates.
*
* @param x The internal X coordinate (0-15).
* @param z The internal Z coordinate (0-15).
* @return double - The interpolated noise at the coordinates.
*/
public double getNoise(double x, double y, double z) {
return interpGrid[reRange(((int) x) / 4, 3)][reRange(((int) y) / 4, 63)][reRange(((int) z) / 4, 3)].trilerp((x % 4) / 4, (y % 4) / 4, (z % 4) / 4);
}
private static int reRange(int value, int high) {
return FastMath.max(FastMath.min(value, high), 0);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,61 @@
package com.dfsek.terra.api.gaea.math;
import net.jafama.FastMath;
/**
* Class for bilinear interpolation of values arranged on a unit square.
*/
public class Interpolator {
private final double v0, v1, v2, v3;
private final Type type;
/**
* Constructs an interpolator with given values as vertices of a unit square.
*
* @param v0 - (0,0)
* @param v1 - (1,0)
* @param v2 - (0,1)
* @param v3 - (1,1)
*/
public Interpolator(double v0, double v1, double v2, double v3, Type type) {
this.v0 = v0;
this.v1 = v1;
this.v2 = v2;
this.v3 = v3;
this.type = type;
}
/**
* 1D Linear interpolation between 2 points 1 unit apart.
*
* @param t - Distance from v0. Total distance between v0 and v1 is 1 unit.
* @param v0 - Value at v0.
* @param v1 - Value at v1.
* @return double - The interpolated value.
*/
public static double lerp(double t, double v0, double v1, Type type) {
switch(type) {
case LINEAR: return v0 + t * (v1 - v0);
case NEAREST_NEIGHBOR: return FastMath.abs(v0-t) > FastMath.abs(v1-t) ? v1 : v0;
default: throw new IllegalStateException();
}
}
/**
* 2D Bilinear interpolation between 4 points on a unit square.
*
* @param s - X value
* @param t - Z value
* @return double - The interpolated value.
*/
public double bilerp(double s, double t) {
double v01 = lerp(s, v0, v1, type);
double v23 = lerp(s, v2, v3, type);
double v = lerp(t, v01, v23, type);
return v;
}
public enum Type {
LINEAR, NEAREST_NEIGHBOR
}
}

View File

@@ -0,0 +1,40 @@
package com.dfsek.terra.api.gaea.math;
/**
* Class for bilinear interpolation of values arranged on a unit square.
*/
public class Interpolator3 {
private final double _000, _100, _010, _110, _001, _101, _011, _111;
private final Interpolator.Type type;
/**
* Constructs an interpolator with given values as vertices of a unit cube.
* * @param _000 The value at <code>(t, u, v) = (0, 0, 0)</code>.
* * @param _100 The value at <code>(t, u, v) = (1, 0, 0)</code>.
* * @param _010 The value at <code>(t, u, v) = (0, 1, 0)</code>.
* * @param _110 The value at <code>(t, u, v) = (1, 1, 0)</code>.
* * @param _001 The value at <code>(t, u, v) = (0, 0, 1)</code>.
* * @param _101 The value at <code>(t, u, v) = (1, 0, 1)</code>.
* * @param _011 The value at <code>(t, u, v) = (0, 1, 1)</code>.
* * @param _111 The value at <code>(t, u, v) = (1, 1, 1)</code>.
*/
public Interpolator3(double _000, double _100,
double _010, double _110, double _001, double _101,
double _011, double _111, Interpolator.Type type) {
this._000 = _000;
this._001 = _001;
this._010 = _010;
this._011 = _011;
this._100 = _100;
this._101 = _101;
this._110 = _110;
this._111 = _111;
this.type = type;
}
public double trilerp(double x, double y, double z) {
Interpolator top = new Interpolator(_000, _010, _001, _011, type);
Interpolator bottom = new Interpolator(_100, _110, _101, _111, type);
return Interpolator.lerp(x, top.bilerp(y, z), bottom.bilerp(y, z), type);
}
}

View File

@@ -0,0 +1,58 @@
package com.dfsek.terra.api.gaea.math;
import com.dfsek.terra.api.gaea.util.FastRandom;
import net.jafama.FastMath;
import java.util.Random;
/**
* Utility class for mathematical functions.
*/
public class MathUtil {
/**
* Gets the standard deviation of an array of doubles.
*
* @param numArray The array of numbers to calculate the standard deviation of.
* @return double - The standard deviation.
*/
public static double standardDeviation(double[] numArray) {
double sum = 0.0, standardDeviation = 0.0;
int length = numArray.length;
for(double num : numArray) {
sum += num;
}
double mean = sum / length;
for(double num : numArray) {
standardDeviation += FastMath.pow(num - mean, 2);
}
return FastMath.sqrt(standardDeviation / length);
}
/**
* Gets the carver seed for a chunk.
*
* @param chunkX Chunk's X coordinate
* @param chunkZ Chunk's Z coordinate
* @param seed World seed
* @return long - The carver seed.
*/
public static long getCarverChunkSeed(int chunkX, int chunkZ, long seed) {
Random r = new FastRandom(seed);
return chunkX * r.nextLong() ^ chunkZ * r.nextLong() ^ seed;
}
public static long hashToLong(String s) {
if(s == null) {
return 0;
}
long hash = 0;
for(char c : s.toCharArray()) {
hash = 31L * hash + c;
}
return hash;
}
}

View File

@@ -0,0 +1,49 @@
package com.dfsek.terra.api.gaea.math;
import com.dfsek.terra.api.gaea.biome.NormalizationUtil;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
@SuppressWarnings("unchecked")
public class ProbabilityCollection<E> {
private final Set<Object> cont = new HashSet<>();
private Object[] array = new Object[0];
private int size;
public com.dfsek.terra.api.gaea.math.ProbabilityCollection<E> add(E item, int probability) {
if(!cont.contains(item)) size++;
cont.add(item);
int oldLength = array.length;
Object[] newArray = new Object[array.length + probability];
System.arraycopy(array, 0, newArray, 0, array.length); // Expand array.
array = newArray;
for(int i = oldLength; i < array.length; i++) array[i] = item;
return this;
}
public E get() {
if(array.length == 0) return null;
return (E) array[ThreadLocalRandom.current().nextInt(array.length)];
}
public E get(Random r) {
if(array.length == 0) return null;
return (E) array[r.nextInt(array.length)];
}
public E get(FastNoiseLite n, double x, double z) {
if(array.length == 0) return null;
return (E) array[NormalizationUtil.normalize(n.getNoise(x, z), array.length, 1)];
}
public int getTotalProbability() {
return array.length;
}
public int size() {
return size;
}
}

View File

@@ -0,0 +1,123 @@
package com.dfsek.terra.api.gaea.math;
import net.jafama.FastMath;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
public class Range implements Iterable<Integer> {
private int min;
private int max;
public Range(int min, int max) {
if(min > max) throw new IllegalArgumentException("Minimum must not be grater than maximum!");
this.max = max;
this.min = min;
}
public boolean isInRange(int test) {
return test >= min && test < max;
}
public int getMax() {
return max;
}
public com.dfsek.terra.api.gaea.math.Range setMax(int max) {
this.max = max;
return this;
}
public int getMin() {
return min;
}
public com.dfsek.terra.api.gaea.math.Range setMin(int min) {
this.min = min;
return this;
}
public int getRange() {
return max - min;
}
public com.dfsek.terra.api.gaea.math.Range multiply(int mult) {
min *= mult;
max *= mult;
return this;
}
public com.dfsek.terra.api.gaea.math.Range reflect(int pt) {
return new com.dfsek.terra.api.gaea.math.Range(2 * pt - this.getMax(), 2 * pt - this.getMin());
}
public int get(Random r) {
return r.nextInt((max - min) + 1) + min;
}
public com.dfsek.terra.api.gaea.math.Range intersects(com.dfsek.terra.api.gaea.math.Range other) {
try {
return new com.dfsek.terra.api.gaea.math.Range(FastMath.max(this.getMin(), other.getMin()), FastMath.min(this.getMax(), other.getMax()));
} catch(IllegalArgumentException e) {
return null;
}
}
public com.dfsek.terra.api.gaea.math.Range add(int add) {
this.min += add;
this.max += add;
return this;
}
public com.dfsek.terra.api.gaea.math.Range sub(int sub) {
this.min -= sub;
this.max -= sub;
return this;
}
@Override
public String toString() {
return "Min: " + getMin() + ", Max:" + getMax();
}
@Override
public int hashCode() {
return min * 31 + max;
}
@Override
public boolean equals(Object obj) {
if(! (obj instanceof com.dfsek.terra.api.gaea.math.Range)) return false;
com.dfsek.terra.api.gaea.math.Range other = (com.dfsek.terra.api.gaea.math.Range) obj;
return other.getMin() == this.getMin() && other.getMax() == this.getMax();
}
@NotNull
@Override
public Iterator<Integer> iterator() {
return new RangeIterator(this);
}
private static class RangeIterator implements Iterator<Integer> {
private final com.dfsek.terra.api.gaea.math.Range m;
private Integer current;
public RangeIterator(com.dfsek.terra.api.gaea.math.Range m) {
this.m = m;
current = m.getMin();
}
@Override
public boolean hasNext() {
return current < m.getMax();
}
@Override
public Integer next() {
current++;
return current - 1;
}
}
}

View File

@@ -0,0 +1,50 @@
package com.dfsek.terra.api.gaea.population;
import com.dfsek.terra.api.generic.world.Chunk;
import java.io.Serializable;
import java.util.UUID;
public class ChunkCoordinate implements Serializable {
public static final long serialVersionUID = 7102462856296750285L;
private final int x;
private final int z;
private final UUID worldID;
public ChunkCoordinate(int x, int z, UUID worldID) {
this.x = x;
this.z = z;
this.worldID = worldID;
}
public ChunkCoordinate(Chunk c) {
this.x = c.getX();
this.z = c.getZ();
this.worldID = c.getWorld().getUID();
}
public UUID getWorldID() {
return worldID;
}
public int getX() {
return x;
}
public int getZ() {
return z;
}
@Override
public int hashCode() {
return x * 31 + z;
}
@Override
public boolean equals(Object obj) {
if(! (obj instanceof com.dfsek.terra.api.gaea.population.ChunkCoordinate)) return false;
com.dfsek.terra.api.gaea.population.ChunkCoordinate other = (com.dfsek.terra.api.gaea.population.ChunkCoordinate) obj;
return other.getX() == x && other.getZ() == z;
}
}

View File

@@ -0,0 +1,94 @@
package com.dfsek.terra.api.gaea.population;
import com.dfsek.terra.api.gaea.Gaea;
import com.dfsek.terra.api.gaea.profiler.ProfileFuture;
import com.dfsek.terra.api.gaea.profiler.WorldProfiler;
import com.dfsek.terra.api.gaea.util.FastRandom;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.dfsek.terra.api.gaea.util.SerializationUtil;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.generator.TerraBlockPopulator;
import com.dfsek.terra.api.generic.world.Chunk;
import com.dfsek.terra.api.generic.world.World;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
public class PopulationManager implements TerraBlockPopulator {
private final List<TerraBlockPopulator> attachedPopulators = new GlueList<>();
private final HashSet<ChunkCoordinate> needsPop = new HashSet<>();
private final TerraPlugin main;
private WorldProfiler profiler;
public PopulationManager(TerraPlugin main) {
this.main = main;
}
public void attach(TerraBlockPopulator populator) {
this.attachedPopulators.add(populator);
}
@Override
@SuppressWarnings("try")
public void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk) {
try(ProfileFuture ignored = measure()) {
needsPop.add(new ChunkCoordinate(chunk));
int x = chunk.getX();
int z = chunk.getZ();
if(main.isEnabled()) {
for(int xi = - 1; xi <= 1; xi++) {
for(int zi = - 1; zi <= 1; zi++) {
if(xi == 0 && zi == 0) continue;
if(world.isChunkGenerated(xi + x, zi + z)) checkNeighbors(xi + x, zi + z, world);
}
}
}
}
}
private ProfileFuture measure() {
if(profiler != null) return profiler.measure("PopulationManagerTime");
return null;
}
public void attachProfiler(WorldProfiler p) {
this.profiler = p;
}
@SuppressWarnings("unchecked")
public synchronized void saveBlocks(World w) throws IOException {
File f = new File(Gaea.getGaeaFolder(w), "chunks.bin");
f.createNewFile();
SerializationUtil.toFile((HashSet<ChunkCoordinate>) needsPop.clone(), f);
}
@SuppressWarnings("unchecked")
public synchronized void loadBlocks(World w) throws IOException, ClassNotFoundException {
File f = new File(Gaea.getGaeaFolder(w), "chunks.bin");
needsPop.addAll((HashSet<ChunkCoordinate>) SerializationUtil.fromFile(f));
}
// Synchronize to prevent chunks from being queued for population multiple times.
public synchronized void checkNeighbors(int x, int z, World w) {
ChunkCoordinate c = new ChunkCoordinate(x, z, w.getUID());
if(w.isChunkGenerated(x + 1, z)
&& w.isChunkGenerated(x - 1, z)
&& w.isChunkGenerated(x, z + 1)
&& w.isChunkGenerated(x, z - 1) && needsPop.contains(c)) {
Random random = new FastRandom(w.getSeed());
long xRand = (random.nextLong() / 2L << 1L) + 1L;
long zRand = (random.nextLong() / 2L << 1L) + 1L;
random.setSeed((long) x * xRand + (long) z * zRand ^ w.getSeed());
Chunk currentChunk = w.getChunkAt(x, z);
for(TerraBlockPopulator r : attachedPopulators) {
r.populate(w, random, currentChunk);
}
needsPop.remove(c);
}
}
}

View File

@@ -0,0 +1,36 @@
package com.dfsek.terra.api.gaea.profiler;
/**
* Class to hold a profiler data value. Contains formatting method to highlight value based on desired range.
*/
public class DataHolder {
private final long desired;
private final DataType type;
private final double desiredRangePercent;
/**
* Constructs a DataHolder with a DataType and a desired value, including a percentage around the desired value considered acceptable
*
* @param type The type of data held in this instance.
* @param desired The desired value. This should be the average value of whatever is being measured.
* @param desiredRangePercent The percentage around the desired value to be considered acceptable.
*/
public DataHolder(DataType type, long desired, double desiredRangePercent) {
this.desired = desired;
this.type = type;
this.desiredRangePercent = desiredRangePercent;
}
/**
* Returns a String, formatted with Bungee ChatColors.<br>
* GREEN if the value is better than desired and outside of acceptable range.<br>
* YELLOW if the value is better or worse than desired, and within acceptable range.<br>
* RED if the value is worse than desired and outside of acceptable range.<br>
*
* @param data The data to format.
* @return String - The formatted data.
*/
public String getFormattedData(long data) {
return type.getFormatted(data);
}
}

View File

@@ -0,0 +1,24 @@
package com.dfsek.terra.api.gaea.profiler;
import net.jafama.FastMath;
public enum DataType {
PERIOD_MILLISECONDS(Desire.LOW, 1000000, "ms"), PERIOD_NANOSECONDS(Desire.LOW, 1, "ns");
private final Desire desire;
private final long divisor;
private final String unit;
DataType(Desire d, long divisor, String unit) {
this.desire = d;
this.divisor = divisor;
this.unit = unit;
}
public String getFormatted(long value) {
return (double) FastMath.round(((double) value / divisor) * 100D) / 100D + unit;
}
public Desire getDesire() {
return desire;
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.gaea.profiler;
/**
* Enum to represent the "goal" of a value, whether it is desirable for the value to be high (e.g. Frequency), or low (e.g. Period)
*/
public enum Desire {
LOW, HIGH
}

View File

@@ -0,0 +1,92 @@
package com.dfsek.terra.api.gaea.profiler;
import com.dfsek.terra.api.gaea.math.MathUtil;
import com.dfsek.terra.api.gaea.util.GlueList;
import net.jafama.FastMath;
import java.math.BigInteger;
import java.util.List;
/**
* Class to record and hold all data for a single type of measurement performed by the profiler.
*/
public class Measurement {
private final List<Long> measurements;
private final long desirable;
private final DataType type;
private long min = Long.MAX_VALUE;
private long max = Long.MIN_VALUE;
/**
* Constructs a new Measurement with a desired value and DataType.
*
* @param desirable The desired value of the measurement.
* @param type The type of data the measurement is holding.
*/
public Measurement(long desirable, DataType type) {
this.desirable = desirable;
this.type = type;
measurements = new GlueList<>();
}
public void record(long value) {
max = FastMath.max(value, max);
min = FastMath.min(value, min);
measurements.add(value);
}
public int size() {
return measurements.size();
}
public ProfileFuture beginMeasurement() {
ProfileFuture future = new ProfileFuture();
long current = System.nanoTime();
future.thenRun(() -> record(System.nanoTime() - current));
return future;
}
public void reset() {
min = Long.MAX_VALUE;
max = Long.MIN_VALUE;
measurements.clear();
}
public DataHolder getDataHolder() {
return new DataHolder(type, desirable, 0.25);
}
public long getMin() {
if(min == Long.MAX_VALUE) return 0;
return min;
}
public long getMax() {
if(max == Long.MIN_VALUE) return 0;
return max;
}
public long average() {
BigInteger running = new BigInteger("0");
List<Long> mTemp = new GlueList<>(measurements);
for(Long l : mTemp) {
running = running.add(BigInteger.valueOf(l));
}
if(measurements.size() == 0) return 0;
return running.divide(BigInteger.valueOf(measurements.size())).longValue();
}
public double getStdDev() {
List<Long> mTemp = new GlueList<>(measurements);
double[] vals = new double[mTemp.size()];
for(int i = 0; i < mTemp.size(); i++) {
vals[i] = mTemp.get(i);
}
return MathUtil.standardDeviation(vals);
}
public int entries() {
return measurements.size();
}
}

View File

@@ -0,0 +1,18 @@
package com.dfsek.terra.api.gaea.profiler;
import java.util.concurrent.CompletableFuture;
public class ProfileFuture extends CompletableFuture<Boolean> implements AutoCloseable {
public ProfileFuture() {
super();
}
public boolean complete() {
return super.complete(true);
}
@Override
public void close() {
this.complete();
}
}

View File

@@ -0,0 +1,82 @@
package com.dfsek.terra.api.gaea.profiler;
import com.dfsek.terra.api.generic.world.World;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.jafama.FastMath;
import java.util.Map;
public class WorldProfiler {
private final BiMap<String, Measurement> measures = HashBiMap.create();
private final World world;
private boolean isProfiling;
public WorldProfiler(World w) {
if(w.getGenerator().getTerraGenerator() == null)
throw new IllegalArgumentException("Attempted to instantiate profiler on non-Gaea managed world!");
this.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "TotalChunkGenTime")
.addMeasurement(new Measurement(2500000, DataType.PERIOD_MILLISECONDS), "ChunkBaseGenTime")
.addMeasurement(new Measurement(2000000, DataType.PERIOD_MILLISECONDS), "BiomeApplyTime")
.addMeasurement(new Measurement(2000000, DataType.PERIOD_MILLISECONDS), "PopulationManagerTime");
isProfiling = false;
this.world = w;
w.getGenerator().getTerraGenerator().attachProfiler(this);
}
public String getResultsFormatted() {
if(! isProfiling) return "Profiler is not currently running.";
StringBuilder result = new StringBuilder("Gaea World Profiler Results (Min / Avg / Max / Std Dev): \n");
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
result
.append(e.getKey())
.append(": ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMin()))
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().average()))
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMax()))
.append(" / ")
.append((double) FastMath.round((e.getValue().getStdDev() / 1000000) * 100D) / 100D)
.append("ms")
.append(" (x").append(e.getValue().size()).append(")\n");
}
return result.toString();
}
public void reset() {
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
e.getValue().reset();
}
}
public com.dfsek.terra.api.gaea.profiler.WorldProfiler addMeasurement(Measurement m, String name) {
measures.put(name, m);
return this;
}
public void setMeasurement(String id, long value) {
if(isProfiling) measures.get(id).record(value);
}
public ProfileFuture measure(String id) {
if(isProfiling) return measures.get(id).beginMeasurement();
else return null;
}
public String getID(Measurement m) {
return measures.inverse().get(m);
}
public boolean isProfiling() {
return isProfiling;
}
public void setProfiling(boolean enabled) {
this.isProfiling = enabled;
}
public World getWorld() {
return world;
}
}

View File

@@ -0,0 +1,50 @@
package com.dfsek.terra.api.gaea.serial;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Field;
public class MovedObjectInputStream extends ObjectInputStream {
private final String oldNameSpace;
private final String newNameSpace;
public MovedObjectInputStream(InputStream in, String oldNameSpace, String newNameSpace) throws IOException {
super(in);
this.oldNameSpace = oldNameSpace;
this.newNameSpace = newNameSpace;
}
@Override
protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
ObjectStreamClass result = super.readClassDescriptor();
try {
if (result.getName().contains(oldNameSpace)) {
String newClassName = result.getName().replace(oldNameSpace, newNameSpace);
Class localClass = Class.forName(newClassName);
Field nameField = ObjectStreamClass.class.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(result, newClassName);
ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
Field suidField = ObjectStreamClass.class.getDeclaredField("suid");
suidField.setAccessible(true);
suidField.set(result, localClassDescriptor.getSerialVersionUID());
}
} catch(Exception e) {
throw new IOException("Exception when trying to replace namespace", e);
}
return result;
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
if (desc.getName().contains(oldNameSpace)) {
String newClassName = desc.getName().replace(oldNameSpace, newNameSpace);
return Class.forName(newClassName);
}
return super.resolveClass(desc);
}
}

View File

@@ -0,0 +1,93 @@
package com.dfsek.terra.api.gaea.structures.loot;
import com.dfsek.terra.api.gaea.structures.loot.functions.AmountFunction;
import com.dfsek.terra.api.gaea.structures.loot.functions.DamageFunction;
import com.dfsek.terra.api.gaea.structures.loot.functions.Function;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.inventory.ItemStack;
import com.dfsek.terra.api.generic.world.block.MaterialData;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.List;
import java.util.Random;
/**
* Representation of a single item entry within a Loot Table pool.
*/
public class Entry {
private final MaterialData item;
private final long weight;
private final List<Function> functions = new GlueList<>();
private final TerraPlugin main;
/**
* Instantiates an Entry from a JSON representation.
*
* @param entry The JSON Object to instantiate from.
*/
public Entry(JSONObject entry, TerraPlugin main) {
this.main = main;
String id = entry.get("name").toString();
this.item = main.getWorldHandle().createMaterialData(id);
long weight1;
try {
weight1 = (long) entry.get("weight");
} catch(NullPointerException e) {
weight1 = 1;
}
this.weight = weight1;
if(entry.containsKey("functions")) {
for(Object function : (JSONArray) entry.get("functions")) {
switch(((String) ((JSONObject) function).get("function"))) {
case "minecraft:set_count":
case "set_count":
Object loot = ((JSONObject) function).get("count");
long max, min;
if(loot instanceof Long) {
max = (Long) loot;
min = (Long) loot;
} else {
max = (long) ((JSONObject) loot).get("max");
min = (long) ((JSONObject) loot).get("min");
}
functions.add(new AmountFunction(FastMath.toIntExact(min), FastMath.toIntExact(max)));
break;
case "minecraft:set_damage":
case "set_damage":
long maxDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("max");
long minDamage = (long) ((JSONObject) ((JSONObject) function).get("damage")).get("min");
functions.add(new DamageFunction(FastMath.toIntExact(minDamage), FastMath.toIntExact(maxDamage)));
break;
}
}
}
}
/**
* Fetches a single ItemStack from the Entry, applying all functions to it.
*
* @param r The Random instance to apply functions with
* @return ItemStack - The ItemStack with all functions applied.
*/
public ItemStack getItem(Random r) {
ItemStack item = main.getItemHandle().newItemStack(this.item, 1);
for(Function f : functions) {
item = f.apply(item, r);
}
return item;
}
/**
* Gets the weight attribute of the Entry.
*
* @return long - The weight of the Entry.
*/
public long getWeight() {
return this.weight;
}
}

View File

@@ -0,0 +1,78 @@
package com.dfsek.terra.api.gaea.structures.loot;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.inventory.Inventory;
import com.dfsek.terra.api.generic.inventory.ItemStack;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.List;
import java.util.Random;
/**
* Class representation of a Loot Table to populate chest loot.
*/
public class LootTable {
private final List<Pool> pools = new GlueList<>();
/**
* Instantiates a LootTable from a JSON String.
*
* @param json The JSON String representing the loot table.
* @throws ParseException if malformed JSON is passed.
*/
public LootTable(String json, TerraPlugin main) throws ParseException {
JSONParser jsonParser = new JSONParser();
Object tableJSON = jsonParser.parse(json);
JSONArray poolArray = (JSONArray) ((JSONObject) tableJSON).get("pools");
for(Object pool : poolArray) {
pools.add(new Pool((JSONObject) pool, main));
}
}
/**
* Fetches a list of ItemStacks from the loot table using the given Random instance.
*
* @param r The Random instance to use.
* @return List&lt;ItemStack&gt; - The list of loot fetched.
*/
public List<ItemStack> getLoot(Random r) {
List<ItemStack> itemList = new GlueList<>();
for(Pool pool : pools) {
itemList.addAll(pool.getItems(r));
}
return itemList;
}
/**
* Fills an Inventory with loot.
*
* @param i The Inventory to fill.
* @param r The The Random instance to use.
*/
public void fillInventory(Inventory i, Random r) {
List<ItemStack> loot = getLoot(r);
for(ItemStack stack : loot) {
int attempts = 0;
while(stack.getAmount() != 0 && attempts < 10) {
ItemStack newStack = stack.clone();
newStack.setAmount(1);
int slot = r.nextInt(i.getSize());
ItemStack slotItem = i.getItem(slot);
if(slotItem == null) {
i.setItem(slot, newStack);
stack.setAmount(stack.getAmount() - 1);
} else if(slotItem.getType().equals(newStack.getType())) {
ItemStack dep = newStack.clone();
dep.setAmount(newStack.getAmount() + slotItem.getAmount());
i.setItem(slot, dep);
stack.setAmount(stack.getAmount() - 1);
}
attempts++;
}
}
}
}

View File

@@ -0,0 +1,59 @@
package com.dfsek.terra.api.gaea.structures.loot;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.inventory.ItemStack;
import net.jafama.FastMath;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.List;
import java.util.Random;
/**
* Representation of a Loot Table pool, or a set of items to be fetched independently.
*/
public class Pool {
private final int max;
private final int min;
private final ProbabilityCollection<Entry> entries;
/**
* Instantiates a Pool from a JSON representation.
*
* @param pool The JSON Object to instantiate from.
*/
public Pool(JSONObject pool, TerraPlugin main) {
entries = new ProbabilityCollection<>();
Object amount = pool.get("rolls");
if(amount instanceof Long) {
max = FastMath.toIntExact((Long) amount);
min = FastMath.toIntExact((Long) amount);
} else {
max = FastMath.toIntExact((Long) ((JSONObject) amount).get("max"));
min = FastMath.toIntExact((Long) ((JSONObject) amount).get("min"));
}
for(Object entryJSON : (JSONArray) pool.get("entries")) {
Entry entry = new Entry((JSONObject) entryJSON, main);
entries.add(entry, FastMath.toIntExact(entry.getWeight()));
}
}
/**
* Fetches a list of items from the pool using the provided Random instance.
*
* @param r The Random instance to use.
* @return List&lt;ItemStack&gt; - The list of items fetched.
*/
public List<ItemStack> getItems(Random r) {
int rolls = r.nextInt(max - min + 1) + min;
List<ItemStack> items = new GlueList<>();
for(int i = 0; i < rolls; i++) {
items.add(entries.get(r).getItem(r));
}
return items;
}
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.gaea.structures.loot.functions;
import com.dfsek.terra.api.generic.inventory.ItemStack;
import java.util.Random;
/**
* Loot Function fot setting the amount of an item.
*/
public class AmountFunction implements Function {
private final int max;
private final int min;
/**
* Instantiates an AmountFunction.
*
* @param min Minimum amount.
* @param max Maximum amount.
*/
public AmountFunction(int min, int max) {
this.min = min;
this.max = max;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
original.setAmount(r.nextInt(max - min + 1) + min);
return original;
}
}

View File

@@ -0,0 +1,42 @@
package com.dfsek.terra.api.gaea.structures.loot.functions;
import com.dfsek.terra.api.generic.inventory.ItemStack;
import com.dfsek.terra.api.generic.inventory.item.Damageable;
import com.dfsek.terra.api.generic.inventory.item.ItemMeta;
import java.util.Random;
/**
* Loot Function for setting the damage on items in Loot Tables
*/
public class DamageFunction implements Function {
private final int max;
private final int min;
/**
* Instantiates a DamageFunction.
*
* @param min Minimum amount of damage (percentage, out of 100)
* @param max Maximum amount of damage (percentage, out of 100)
*/
public DamageFunction(int min, int max) {
this.min = min;
this.max = max;
}
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
@Override
public ItemStack apply(ItemStack original, Random r) {
double itemDurability = (r.nextDouble() * (max - min)) + min;
Damageable damage = (Damageable) original.getItemMeta();
damage.setDamage((int) (original.getType().getMaxDurability() - (itemDurability / 100) * original.getType().getMaxDurability()));
original.setItemMeta((ItemMeta) damage);
return original;
}
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.gaea.structures.loot.functions;
import com.dfsek.terra.api.generic.inventory.ItemStack;
import java.util.Random;
/**
* Interface for mutating items in Loot Tables.
*/
public interface Function {
/**
* Applies the function to an ItemStack.
*
* @param original The ItemStack on which to apply the function.
* @param r The Random instance to use.
* @return - ItemStack - The mutated ItemStack.
*/
ItemStack apply(ItemStack original, Random r);
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.gaea.tree;
import com.dfsek.terra.api.generic.world.block.MaterialData;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.Random;
import java.util.Set;
public interface Tree {
boolean plant(Location l, Random r);
Set<MaterialData> getSpawnable();
}

View File

@@ -0,0 +1,32 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import com.dfsek.terra.api.generic.Entity;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.function.Consumer;
public class EntitySpawnHolder {
private final Location l;
private final Class<? extends Entity> e;
private final Consumer<Entity> c;
public EntitySpawnHolder(Location l, Class<? extends Entity> e, Consumer<Entity> c) {
this.l = l;
this.e = e;
this.c = c;
}
@SuppressWarnings("rawtypes")
public Class getEntity() {
return e;
}
public Consumer<Entity> getConsumer() {
return c;
}
public Location getLocation() {
return l;
}
}

View File

@@ -0,0 +1,114 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.dfsek.terra.api.generic.Entity;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.MaterialData;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.function.Consumer;
public abstract class FractalTree {
private final Map<Location, BlockData> treeAssembler = new HashMap<>();
private final List<EntitySpawnHolder> entities = new GlueList<>();
private final Location origin;
private final Random random;
private final TerraPlugin main;
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public FractalTree(Location origin, Random random, TerraPlugin plugin) {
this.origin = origin.add(0, 1, 0);
this.random = random;
this.main = plugin;
}
/**
* Gets the raw tree map.
*
* @return HashMap&lt;Location, BlockData&gt; - The raw dictionary representation of the tree.
*/
public Map<Location, BlockData> getTree() {
return treeAssembler;
}
/**
* Fetches the Random object used to generate the tree.
*
* @return Random - The Random object.
*/
public Random getRandom() {
return random;
}
/**
* Fetches the origin location.
*
* @return Location - The origin location specified upon instantiation.
*/
public Location getOrigin() {
return origin;
}
/**
* Sets a block in the tree's storage map to a material.
*
* @param l - The location to set.
* @param m - The material to which it will be set.
*/
public void setBlock(Location l, BlockData m) {
treeAssembler.put(l, m);
}
public TerraPlugin getMain() {
return main;
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
public abstract void grow();
/**
* Pastes the tree in the world. Must be invoked from main thread.
*/
@SuppressWarnings("unchecked")
public void plant() {
for(Map.Entry<Location, BlockData> entry : treeAssembler.entrySet()) {
entry.getKey().getBlock().setBlockData(entry.getValue(), false);
}
for(EntitySpawnHolder e : entities) {
Objects.requireNonNull(e.getLocation().getWorld()).spawn(e.getLocation(), e.getEntity(), e.getConsumer());
}
}
@SuppressWarnings("rawtypes, unchecked")
public void spawnEntity(Location spawn, Class clazz, Consumer<Entity> consumer) {
entities.add(new EntitySpawnHolder(spawn, clazz, consumer));
}
/**
* Gets the material at the specified block.
* Returns air if no material has been set.
*
* @param l - The location at which to check.
* @return Material - The material at the specified block.
*/
public MaterialData getMaterial(Location l) {
return treeAssembler.getOrDefault(l, main.getWorldHandle().createBlockData("minecraft:air")).getMaterial();
}
}

View File

@@ -0,0 +1,66 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import com.dfsek.terra.api.generic.world.vector.Vector3;
public class TreeGeometry {
private final FractalTree tree;
public TreeGeometry(FractalTree tree) {
this.tree = tree;
}
public static Vector3 getPerpendicular(Vector3 v) {
return v.getZ() < v.getX() ? new Vector3(v.getY(), - v.getX(), 0) : new Vector3(0, - v.getZ(), v.getY());
}
public FractalTree getTree() {
return tree;
}
public void generateSphere(Location l, BlockData m, int radius, boolean overwrite) {
generateSphere(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, overwrite);
}
public void generateCylinder(Location l, BlockData m, int radius, int height, boolean overwrite) {
generateCylinder(l, new ProbabilityCollection<BlockData>().add(m, 1), radius, height, overwrite);
}
public void generateSphere(Location l, ProbabilityCollection<BlockData> m, int radius, boolean overwrite) {
for(int x = - radius; x <= radius; x++) {
for(int y = - radius; y <= radius; y++) {
for(int z = - radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, y, z));
if(l.toVector().distance(position) <= radius + 0.5 && (overwrite || tree.getMaterial(position.toLocation(l.getWorld())).isAir()))
tree.setBlock(position.toLocation(l.getWorld()), m.get());
}
}
}
}
public void generateSponge(Location l, ProbabilityCollection<BlockData> m, int radius, boolean overwrite, int sponginess) {
for(int x = - radius; x <= radius; x++) {
for(int y = - radius; y <= radius; y++) {
for(int z = - radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, y, z));
if(tree.getRandom().nextInt(100) < sponginess && l.toVector().distance(position) <= radius + 0.5 && (overwrite || tree.getMaterial(position.toLocation(l.getWorld())).isAir()))
tree.setBlock(position.toLocation(l.getWorld()), m.get());
}
}
}
}
public void generateCylinder(Location l, ProbabilityCollection<BlockData> m, int radius, int height, boolean overwrite) {
for(int x = - radius; x <= radius; x++) {
for(int y = 0; y <= height; y++) {
for(int z = - radius; z <= radius; z++) {
Vector3 position = l.toVector().clone().add(new Vector3(x, 0, z));
if(l.toVector().distance(position) <= radius + 0.5 && (overwrite || tree.getMaterial(position.toLocation(l.getWorld())).isAir()))
tree.setBlock(position.toLocation(l.getWorld()), m.get());
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.Random;
public class Cactus extends FractalTree {
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public Cactus(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
BlockData cactus = getMain().getWorldHandle().createBlockData("minecraft:cactus");
int h = super.getRandom().nextInt(4) + 1;
for(int i = 0; i < h; i++) setBlock(super.getOrigin().clone().add(0, i, 0), cactus);
}
}

View File

@@ -0,0 +1,51 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.gaea.tree.fractal.TreeGeometry;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.WorldHandle;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import java.util.Random;
public class IceSpike extends FractalTree {
private final TreeGeometry geo;
private final ProbabilityCollection<BlockData> ice;
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public IceSpike(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
geo = new TreeGeometry(this);
WorldHandle handle = main.getWorldHandle();
ice = new ProbabilityCollection<BlockData>().add(handle.createBlockData("minecraft:packed_ice"), 95).add(handle.createBlockData("minecraft:blue_ice"), 5);
}
private double getOffset() {
return (getRandom().nextDouble() - 0.5D);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
Vector3 direction = new Vector3(getOffset(), 0, getOffset());
Location l1 = super.getOrigin().clone();
int h = super.getRandom().nextInt(16) + 8;
for(int i = 0; i < h; i++) {
geo.generateSponge(l1.clone().add(0, i, 0).add(direction.clone().multiply(i)), ice, (int) ((1 - ((double) i / h)) * 2 + 1), true, 80);
}
for(int i = 0; i < h/3; i++) {
setBlock(l1.clone().add(0, h + i, 0).add(direction.clone().multiply(h + i)), ice.get(super.getRandom()));
}
}
}

View File

@@ -0,0 +1,62 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.gaea.tree.fractal.TreeGeometry;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import net.jafama.FastMath;
import java.util.Random;
public class OakTree extends FractalTree {
private final TreeGeometry geo;
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public OakTree(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
geo = new TreeGeometry(this);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
growBranch(super.getOrigin().clone(), new Vector3(super.getRandom().nextInt(5) - 2, super.getRandom().nextInt(4) + 6, super.getRandom().nextInt(5) - 2), 2, 0);
}
private void growBranch(Location l1, Vector3 diff, double d1, int recursions) {
BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:oak_wood");
BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:oak_leave");
if(recursions > 1) {
geo.generateSphere(l1, leaves, 1 + super.getRandom().nextInt(2) + (3 - recursions), false);
if(recursions > 2) return;
}
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), wood, FastMath.max((int) d1, 0), true);
}
double runningAngle = (double) 45 / (recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.75).rotateAroundX(FastMath.toRadians(runningAngle + getNoise())).rotateAroundZ(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.75).rotateAroundX(FastMath.toRadians(- runningAngle + getNoise())).rotateAroundZ(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.75).rotateAroundZ(FastMath.toRadians(runningAngle + getNoise())).rotateAroundX(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.75).rotateAroundZ(FastMath.toRadians(- runningAngle + getNoise())).rotateAroundX(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
}
private int getNoise() {
return super.getRandom().nextInt(60) - 30;
}
}

View File

@@ -0,0 +1,50 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.Random;
public class ShatteredPillar extends FractalTree {
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public ShatteredPillar(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
BlockData obsidian = getMain().getWorldHandle().createBlockData("minecraft:obsidian");
int h = super.getRandom().nextInt(5) + 8;
int max = h;
for(int i = - h; i < h; i++) setBlock(super.getOrigin().clone().add(0, i, 0), obsidian);
h = h + (getRandom().nextBoolean() ? getRandom().nextInt(3) + 1 : - (getRandom().nextInt(3) + 1));
int[] crystalLoc = new int[] {0, 0};
if(h > max) {
max = h;
crystalLoc = new int[] {1, 0};
}
for(int i = - h; i < h; i++) setBlock(super.getOrigin().clone().add(1, i, 0), obsidian);
h = h + (getRandom().nextBoolean() ? getRandom().nextInt(3) + 1 : - (getRandom().nextInt(3) + 1));
if(h > max) {
max = h;
crystalLoc = new int[] {0, 1};
}
for(int i = - h; i < h; i++) setBlock(super.getOrigin().clone().add(0, i, 1), obsidian);
h = h + (getRandom().nextBoolean() ? getRandom().nextInt(3) + 1 : - (getRandom().nextInt(3) + 1));
if(h > max) {
max = h;
crystalLoc = new int[] {1, 1};
}
for(int i = - h; i < h; i++) setBlock(super.getOrigin().clone().add(1, i, 1), obsidian);
}
}

View File

@@ -0,0 +1,72 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.gaea.tree.fractal.TreeGeometry;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.WorldHandle;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import net.jafama.FastMath;
import java.util.Random;
public class ShatteredTree extends FractalTree {
private final TreeGeometry geo;
private final ProbabilityCollection<BlockData> bark;
private final ProbabilityCollection<BlockData> leaves;
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public ShatteredTree(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
geo = new TreeGeometry(this);
WorldHandle handle = main.getWorldHandle();
bark = new ProbabilityCollection<BlockData>()
.add(handle.createBlockData("minecraft:obsidian"), 1)
.add(handle.createBlockData("minecraft:black_concrete"), 1);
leaves = new ProbabilityCollection<BlockData>()
.add(handle.createBlockData("minecraft:purple_stained_glass"), 1)
.add(handle.createBlockData("minecraft:magenta_stained_glass"), 1);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
growBranch(super.getOrigin().clone(), new Vector3(super.getRandom().nextInt(5) - 2, super.getRandom().nextInt(4) + 6, super.getRandom().nextInt(5) - 2), 1, 0);
}
private void growBranch(Location l1, Vector3 diff, double d1, int recursions) {
if(recursions > 2) {
geo.generateSphere(l1, leaves, 1 + super.getRandom().nextInt(2), false);
return;
}
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), bark, FastMath.max((int) d1, 0), true);
}
double runningAngle = (double) 45 / (recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.9).rotateAroundX(FastMath.toRadians(runningAngle + getNoise())).rotateAroundZ(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.9).rotateAroundX(FastMath.toRadians(- runningAngle + getNoise())).rotateAroundZ(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.9).rotateAroundZ(FastMath.toRadians(runningAngle + getNoise())).rotateAroundX(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.9).rotateAroundZ(FastMath.toRadians(- runningAngle + getNoise())).rotateAroundX(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
}
private int getNoise() {
return super.getRandom().nextInt(90) - 45;
}
}

View File

@@ -0,0 +1,30 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.Random;
public class SmallShatteredPillar extends FractalTree {
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public SmallShatteredPillar(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
int h = super.getRandom().nextInt(5) + 5;
BlockData obsidian = getMain().getWorldHandle().createBlockData("minecraft:obsidian");
for(int i = - h; i < h; i++) setBlock(super.getOrigin().clone().add(0, i, 0), obsidian);
}
}

View File

@@ -0,0 +1,72 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.gaea.tree.fractal.TreeGeometry;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.WorldHandle;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import net.jafama.FastMath;
import java.util.Random;
public class SmallShatteredTree extends FractalTree {
private final TreeGeometry geo;
private final ProbabilityCollection<BlockData> bark;
private final ProbabilityCollection<BlockData> leaves;
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public SmallShatteredTree(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
geo = new TreeGeometry(this);
WorldHandle handle = main.getWorldHandle();
bark = new ProbabilityCollection<BlockData>()
.add(handle.createBlockData("minecraft:obsidian"), 1)
.add(handle.createBlockData("minecraft:black_concrete"), 1);
leaves = new ProbabilityCollection<BlockData>()
.add(handle.createBlockData("minecraft:purple_stained_glass"), 1)
.add(handle.createBlockData("minecraft:magenta_stained_glass"), 1);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
growBranch(super.getOrigin().clone(), new Vector3(super.getRandom().nextInt(5) - 2, super.getRandom().nextInt(3) + 4, super.getRandom().nextInt(5) - 2), 1.5, 0);
}
private void growBranch(Location l1, Vector3 diff, double d1, int recursions) {
if(recursions > 2) {
geo.generateSphere(l1, leaves, 1 + super.getRandom().nextInt(2) + (3 - recursions), false);
return;
}
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), bark, FastMath.max((int) d1, 0), true);
}
double runningAngle = (double) 45 / (recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.7).rotateAroundX(FastMath.toRadians(runningAngle + getNoise())).rotateAroundZ(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.7).rotateAroundX(FastMath.toRadians(- runningAngle + getNoise())).rotateAroundZ(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.7).rotateAroundZ(FastMath.toRadians(runningAngle + getNoise())).rotateAroundX(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
growBranch(l1.clone().add(diff), diff.clone().multiply(0.7).rotateAroundZ(FastMath.toRadians(- runningAngle + getNoise())).rotateAroundX(FastMath.toRadians(getNoise())),
d1 - 1, recursions + 1);
}
private int getNoise() {
return super.getRandom().nextInt(90) - 45;
}
}

View File

@@ -0,0 +1,49 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.gaea.tree.fractal.TreeGeometry;
import com.dfsek.terra.api.generic.TerraPlugin;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.vector.Location;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import net.jafama.FastMath;
import java.util.Random;
public class SpruceTree extends FractalTree {
private final TreeGeometry geo;
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public SpruceTree(Location origin, Random random, TerraPlugin main) {
super(origin, random, main);
geo = new TreeGeometry(this);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
growTrunk(super.getOrigin().clone(), new Vector3(0, 16 + super.getRandom().nextInt(5), 0));
}
private void growTrunk(Location l1, Vector3 diff) {
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
int rad = 7;
BlockData wood = getMain().getWorldHandle().createBlockData("minecraft:spruce_wood");
BlockData leaves = getMain().getWorldHandle().createBlockData("minecraft:spruce_leave");
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), wood, (int) ((i > d * 0.65) ? 0.5 : 1.5), true);
if(i > 3)
geo.generateCylinder(l1.clone().add(diff.clone().multiply((double) i / d)), leaves, (int) (((6 - (i % 4))) * (1.25 - ((double) i / d))), 1, false);
}
setBlock(l1.clone().add(diff), leaves);
setBlock(l1.clone().add(diff).add(0, 1, 0), leaves);
}
}

View File

@@ -0,0 +1,65 @@
package com.dfsek.terra.api.gaea.util;
import org.apache.commons.rng.core.source64.XoRoShiRo128PlusPlus;
import java.util.Random;
import java.util.SplittableRandom;
public class FastRandom extends Random {
private XoRoShiRo128PlusPlus random;
public FastRandom() {
super();
SplittableRandom randomseed = new SplittableRandom();
this.random = new XoRoShiRo128PlusPlus(randomseed.nextLong(), randomseed.nextLong());
}
public FastRandom(long seed) {
super(seed);
SplittableRandom randomseed = new SplittableRandom(seed);
this.random = new XoRoShiRo128PlusPlus(randomseed.nextLong(), randomseed.nextLong());
}
@Override
public boolean nextBoolean() {
return random.nextBoolean();
}
@Override
public int nextInt() {
return random.nextInt();
}
@Override
public float nextFloat() {
return (float) random.nextDouble();
}
@Override
public double nextDouble() {
return random.nextDouble();
}
@Override
public synchronized void setSeed(long seed) {
SplittableRandom randomseed = new SplittableRandom(seed);
this.random = new XoRoShiRo128PlusPlus(randomseed.nextLong(), randomseed.nextLong());
}
@Override
public void nextBytes(byte[] bytes) {
random.nextBytes(bytes);
}
@Override
public int nextInt(int bound) {
return random.nextInt(bound);
}
@Override
public long nextLong() {
return random.nextLong();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.gaea.util;
import com.dfsek.terra.api.gaea.Debug;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class JarUtil {
public static void copyResourcesToDirectory(JarFile fromJar, String sourceDir, String destDir) throws IOException {
for(Enumeration<JarEntry> entries = fromJar.entries(); entries.hasMoreElements(); ) {
JarEntry entry = entries.nextElement();
if(entry.getName().startsWith(sourceDir + "/") && ! entry.isDirectory()) {
File dest = new File(destDir + File.separator + entry.getName().substring(sourceDir.length() + 1));
if(dest.exists()) continue;
File parent = dest.getParentFile();
if(parent != null) {
parent.mkdirs();
}
Debug.info("Output does not already exist. Creating... ");
try(FileOutputStream out = new FileOutputStream(dest); InputStream in = fromJar.getInputStream(entry)) {
byte[] buffer = new byte[(8192)];
int s;
while((s = in.read(buffer)) > 0) {
out.write(buffer, 0, s);
}
} catch(IOException e) {
throw new IOException("Could not copy asset from jar file", e);
}
}
}
}
}

View File

@@ -0,0 +1,26 @@
package com.dfsek.terra.api.gaea.util;
import com.dfsek.terra.api.gaea.serial.MovedObjectInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializationUtil {
public static Object fromFile(File f) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new MovedObjectInputStream(new FileInputStream(f), "org.polydev.gaea", "com.dfsek.terra.api.gaea"); // Backwards compat with old Gaea location
Object o = ois.readObject();
ois.close();
return o;
}
public static void toFile(Serializable o, File f) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(f));
oos.writeObject(o);
oos.close();
}
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.gaea.world;
import com.dfsek.terra.api.gaea.math.Range;
import com.dfsek.terra.api.generic.world.Chunk;
import com.dfsek.terra.api.generic.world.block.Block;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.util.List;
public interface Flora {
List<Block> getValidSpawnsAt(Chunk chunk, int x, int z, Range check);
boolean plant(Location l);
}

View File

@@ -0,0 +1,59 @@
package com.dfsek.terra.api.gaea.world.carving;
import com.dfsek.terra.api.gaea.math.MathUtil;
import com.dfsek.terra.api.gaea.util.FastRandom;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import net.jafama.FastMath;
import java.util.Random;
import java.util.function.BiConsumer;
public abstract class Carver {
private final int minY;
private final int maxY;
private final double sixtyFourSq = FastMath.pow(64, 2);
private int carvingRadius = 4;
public Carver(int minY, int maxY) {
this.minY = minY;
this.maxY = maxY;
}
public void carve(int chunkX, int chunkZ, World w, BiConsumer<Vector3, CarvingType> consumer) {
for(int x = chunkX - carvingRadius; x <= chunkX + carvingRadius; x++) {
for(int z = chunkZ - carvingRadius; z <= chunkZ + carvingRadius; z++) {
if(isChunkCarved(w, x, z, new FastRandom(MathUtil.hashToLong(this.getClass().getName() + "_" + x + "&" + z)))) {
long seed = MathUtil.getCarverChunkSeed(x, z, w.getSeed());
Random r = new FastRandom(seed);
Worm carving = getWorm(seed, new Vector3((x << 4) + r.nextInt(16), r.nextInt(maxY - minY + 1) + minY, (z << 4) + r.nextInt(16)));
Vector3 origin = carving.getOrigin();
for(int i = 0; i < carving.getLength(); i++) {
carving.step();
if(carving.getRunning().clone().setY(0).distanceSquared(origin.clone().setY(0)) > sixtyFourSq)
break;
if(FastMath.floorDiv(origin.getBlockX(), 16) != chunkX && FastMath.floorDiv(origin.getBlockZ(), 16) != chunkZ)
continue;
carving.getPoint().carve(chunkX, chunkZ, consumer);
}
}
}
}
}
public int getCarvingRadius() {
return carvingRadius;
}
public void setCarvingRadius(int carvingRadius) {
this.carvingRadius = carvingRadius;
}
public abstract Worm getWorm(long seed, Vector3 l);
public abstract boolean isChunkCarved(World w, int chunkX, int chunkZ, Random r);
public enum CarvingType {
CENTER, WALL, TOP, BOTTOM
}
}

View File

@@ -0,0 +1,119 @@
package com.dfsek.terra.api.gaea.world.carving;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import net.jafama.FastMath;
import java.util.Random;
import java.util.function.BiConsumer;
public abstract class Worm {
private final Random r;
private final Vector3 origin;
private final Vector3 running;
private final int length;
private int topCut = 0;
private int bottomCut = 0;
private int[] radius = new int[] {0, 0, 0};
public Worm(int length, Random r, Vector3 origin) {
this.r = r;
this.length = length;
this.origin = origin;
this.running = origin;
}
public void setBottomCut(int bottomCut) {
this.bottomCut = bottomCut;
}
public void setTopCut(int topCut) {
this.topCut = topCut;
}
public Vector3 getOrigin() {
return origin;
}
public int getLength() {
return length;
}
public Vector3 getRunning() {
return running;
}
public WormPoint getPoint() {
return new WormPoint(running, radius, topCut, bottomCut);
}
public int[] getRadius() {
return radius;
}
public void setRadius(int[] radius) {
this.radius = radius;
}
public Random getRandom() {
return r;
}
public abstract void step();
public static class WormPoint {
private final Vector3 origin;
private final int topCut;
private final int bottomCut;
private final int[] rad;
public WormPoint(Vector3 origin, int[] rad, int topCut, int bottomCut) {
this.origin = origin;
this.rad = rad;
this.topCut = topCut;
this.bottomCut = bottomCut;
}
private static double ellipseEquation(int x, int y, int z, double xr, double yr, double zr) {
return (FastMath.pow2(x) / FastMath.pow2(xr + 0.5D)) + (FastMath.pow2(y) / FastMath.pow2(yr + 0.5D)) + (FastMath.pow2(z) / FastMath.pow2(zr + 0.5D));
}
public Vector3 getOrigin() {
return origin;
}
public int getRadius(int index) {
return rad[index];
}
public void carve(int chunkX, int chunkZ, BiConsumer<Vector3, Carver.CarvingType> consumer) {
int xRad = getRadius(0);
int yRad = getRadius(1);
int zRad = getRadius(2);
int originX = (chunkX << 4);
int originZ = (chunkZ << 4);
for(int x = -xRad - 1; x <= xRad + 1; x++) {
if(!(FastMath.floorDiv(origin.getBlockX() + x, 16) == chunkX)) continue;
for(int z = -zRad - 1; z <= zRad + 1; z++) {
if(!(FastMath.floorDiv(origin.getBlockZ() + z, 16) == chunkZ)) continue;
for(int y = -yRad - 1; y <= yRad + 1; y++) {
Vector3 position = origin.clone().add(new Vector3(x, y, z));
if(position.getY() < 0 || position.getY() > 255) continue;
double eq = ellipseEquation(x, y, z, xRad, yRad, zRad);
if(eq <= 1 &&
y >= -yRad - 1 + bottomCut && y <= yRad + 1 - topCut) {
consumer.accept(new Vector3(position.getBlockX() - originX, position.getBlockY(), position.getBlockZ() - originZ), Carver.CarvingType.CENTER);
} else if(eq <= 1.5) {
Carver.CarvingType type = Carver.CarvingType.WALL;
if(y <= -yRad - 1 + bottomCut) {
type = Carver.CarvingType.BOTTOM;
} else if(y >= yRad + 1 - topCut) {
type = Carver.CarvingType.TOP;
}
consumer.accept(new Vector3(position.getBlockX() - originX, position.getBlockY(), position.getBlockZ() - originZ), type);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,116 @@
package com.dfsek.terra.api.gaea.world.palette;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.api.gaea.util.GlueList;
import java.util.List;
import java.util.Random;
/**
* A class representation of a "slice" of the world.
* Used to get a section of blocks, based on the depth at which they are found.
*/
public abstract class Palette<E> {
private final List<PaletteLayer<E>> pallet = new GlueList<>();
/**
* Constructs a blank palette.
*/
public Palette() {
}
/**
* Adds a material to the palette, for a number of layers.
*
* @param m - The material to add to the palette.
* @param layers - The number of layers the material occupies.
* @return - BlockPalette instance for chaining.
*/
public com.dfsek.terra.api.gaea.world.palette.Palette<E> add(E m, int layers) {
for(int i = 0; i < layers; i++) {
pallet.add(new PaletteLayer<>(m));
}
return this;
}
/**
* Adds a ProbabilityCollection to the palette, for a number of layers.
*
* @param m - The ProbabilityCollection to add to the palette.
* @param layers - The number of layers the material occupies.
* @return - BlockPalette instance for chaining.
*/
public com.dfsek.terra.api.gaea.world.palette.Palette<E> add(ProbabilityCollection<E> m, int layers) {
for(int i = 0; i < layers; i++) {
pallet.add(new PaletteLayer<>(m));
}
return this;
}
/**
* Fetches a material from the palette, at a given layer.
*
* @param layer - The layer at which to fetch the material.
* @return BlockData - The material fetched.
*/
public abstract E get(int layer, int x, int z);
public int getSize() {
return pallet.size();
}
public List<PaletteLayer<E>> getLayers() {
return pallet;
}
/**
* Class representation of a layer of a BlockPalette.
*/
public static class PaletteLayer<E> {
private final boolean col; // Is layer using a collection?
private ProbabilityCollection<E> collection;
private E m;
/**
* Constructs a PaletteLayer with a ProbabilityCollection of materials and a number of layers.
*
* @param type - The collection of materials to choose from.
*/
public PaletteLayer(ProbabilityCollection<E> type) {
this.col = true;
this.collection = type;
}
/**
* Constructs a PaletteLayer with a single Material and a number of layers.
*
* @param type - The material to use.
*/
public PaletteLayer(E type) {
this.col = false;
this.m = type;
}
/**
* Gets a material from the layer.
*
* @return Material - the material..
*/
public E get(Random random) {
if(col) return this.collection.get(random);
return m;
}
public E get(FastNoiseLite random, int x, int z) {
if(col) return this.collection.get(random, x, z);
return m;
}
public ProbabilityCollection<E> getCollection() {
return collection;
}
}
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.gaea.world.palette;
import java.util.List;
import java.util.Random;
public class RandomPalette<E> extends Palette<E> {
private final Random r;
public RandomPalette(Random r) {
this.r = r;
}
@Override
public E get(int layer, int x, int z) {
if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r);
List<PaletteLayer<E>> pl = getLayers();
if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r);
return pl.get(layer).get(r);
}
}

View File

@@ -0,0 +1,21 @@
package com.dfsek.terra.api.gaea.world.palette;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import java.util.List;
public class SimplexPalette<E> extends Palette<E> {
private final FastNoiseLite r;
public SimplexPalette(FastNoiseLite r) {
this.r = r;
}
@Override
public E get(int layer, int x, int z) {
if(layer > this.getSize()) return this.getLayers().get(this.getLayers().size() - 1).get(r, x, z);
List<PaletteLayer<E>> pl = getLayers();
if(layer >= pl.size()) return pl.get(pl.size() - 1).get(r, x, z);
return pl.get(layer).get(r, x, z);
}
}

View File

@@ -0,0 +1,5 @@
package com.dfsek.terra.api.generic;
public interface CommandSender extends Handle {
void sendMessage(String message);
}

View File

@@ -0,0 +1,4 @@
package com.dfsek.terra.api.generic;
public interface Entity extends Handle {
}

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api.generic;
/**
* An interface that contains a platform delegate.
*/
public interface Handle {
Object getHandle();
}

View File

@@ -0,0 +1,37 @@
package com.dfsek.terra.api.generic;
import com.dfsek.terra.TerraWorld;
import com.dfsek.terra.api.LoaderRegistrar;
import com.dfsek.terra.api.gaea.lang.Language;
import com.dfsek.terra.api.generic.inventory.ItemHandle;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.api.generic.world.WorldHandle;
import com.dfsek.terra.config.base.PluginConfig;
import com.dfsek.terra.registry.ConfigRegistry;
import java.io.File;
import java.util.logging.Logger;
public interface TerraPlugin extends LoaderRegistrar {
WorldHandle getWorldHandle();
boolean isEnabled();
TerraWorld getWorld(World world);
Logger getLogger();
PluginConfig getTerraConfig();
File getDataFolder();
boolean isDebug();
Language getLanguage();
ConfigRegistry getRegistry();
void reload();
ItemHandle getItemHandle();
}

View File

@@ -0,0 +1,4 @@
package com.dfsek.terra.api.generic;
public interface Tree extends Handle {
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.generic.generator;
import com.dfsek.terra.api.generic.Handle;
import com.dfsek.terra.api.generic.world.Chunk;
import com.dfsek.terra.api.generic.world.World;
import java.util.Random;
public interface BlockPopulator extends Handle {
void populate(World world, Random random, Chunk chunk);
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.generic.generator;
import com.dfsek.terra.api.generic.world.Chunk;
import com.dfsek.terra.api.generic.world.World;
import java.util.Random;
public interface TerraBlockPopulator {
void populate(World world, Random random, Chunk chunk);
}

View File

@@ -0,0 +1,31 @@
package com.dfsek.terra.api.generic.generator;
import com.dfsek.terra.api.gaea.profiler.WorldProfiler;
import com.dfsek.terra.api.generic.world.BiomeGrid;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.config.base.ConfigPack;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Random;
public interface TerraChunkGenerator {
ChunkGenerator.ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int x, int z, @NotNull BiomeGrid biome, ChunkGenerator.ChunkData original);
void attachProfiler(WorldProfiler profiler);
boolean isParallelCapable();
boolean shouldGenerateCaves();
boolean shouldGenerateDecorations();
boolean shouldGenerateMobs();
boolean shouldGenerateStructures();
ConfigPack getConfigPack();
List<TerraBlockPopulator> getPopulators();
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.generic.inventory;
import com.dfsek.terra.api.generic.Handle;
public interface Inventory extends Handle {
int getSize();
ItemStack getItem(int slot);
void setItem(int slot, ItemStack newStack);
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.generic.inventory;
import com.dfsek.terra.api.generic.world.block.MaterialData;
public interface ItemHandle {
ItemStack newItemStack(MaterialData material, int amount);
}

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra.api.generic.inventory;
import com.dfsek.terra.api.generic.Handle;
import com.dfsek.terra.api.generic.inventory.item.ItemMeta;
import com.dfsek.terra.api.generic.world.block.MaterialData;
public interface ItemStack extends Handle, Cloneable {
int getAmount();
void setAmount(int i);
MaterialData getType();
ItemStack clone();
ItemMeta getItemMeta();
void setItemMeta(ItemMeta meta);
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.generic.inventory.item;
import com.dfsek.terra.api.generic.Handle;
public interface Damageable extends Handle, Cloneable {
Damageable clone();
int getDamage();
void setDamage(int damage);
boolean hasDamage();
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.generic.inventory.item;
import com.dfsek.terra.api.generic.Handle;
public interface ItemMeta extends Handle {
}

View File

@@ -0,0 +1,6 @@
package com.dfsek.terra.api.generic.world;
import com.dfsek.terra.api.generic.Handle;
public interface Biome extends Handle {
}

View File

@@ -0,0 +1,46 @@
package com.dfsek.terra.api.generic.world;
import com.dfsek.terra.api.generic.Handle;
import org.jetbrains.annotations.NotNull;
public interface BiomeGrid extends Handle {
/**
* Get biome at x, z within chunk being generated
*
* @param x - 0-15
* @param z - 0-15
* @return Biome value
*/
@NotNull
Biome getBiome(int x, int z);
/**
* Get biome at x, z within chunk being generated
*
* @param x - 0-15
* @param y - 0-255
* @param z - 0-15
* @return Biome value
*/
@NotNull
Biome getBiome(int x, int y, int z);
/**
* Set biome at x, z within chunk being generated
*
* @param x - 0-15
* @param z - 0-15
* @param bio - Biome value
*/
void setBiome(int x, int z, @NotNull Biome bio);
/**
* Set biome at x, z within chunk being generated
*
* @param x - 0-15
* @param y - 0-255
* @param z - 0-15
* @param bio - Biome value
*/
void setBiome(int x, int y, int z, @NotNull Biome bio);
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.generic.world;
import com.dfsek.terra.api.generic.Handle;
import com.dfsek.terra.api.generic.world.block.Block;
public interface Chunk extends Handle {
int getX();
int getZ();
World getWorld();
Block getBlock(int x, int y, int z);
}

View File

@@ -0,0 +1,38 @@
package com.dfsek.terra.api.generic.world;
import com.dfsek.terra.api.generic.Entity;
import com.dfsek.terra.api.generic.Handle;
import com.dfsek.terra.api.generic.Tree;
import com.dfsek.terra.api.generic.generator.ChunkGenerator;
import com.dfsek.terra.api.generic.world.block.Block;
import com.dfsek.terra.api.generic.world.vector.Location;
import java.io.File;
import java.util.UUID;
import java.util.function.Consumer;
public interface World extends Handle {
long getSeed();
int getMaxHeight();
ChunkGenerator getGenerator();
String getName();
UUID getUID();
boolean isChunkGenerated(int x, int z);
Chunk getChunkAt(int x, int z);
File getWorldFolder();
Block getBlockAt(int x, int y, int z);
Block getBlockAt(Location l);
boolean generateTree(Location l, Tree vanillaTreeType); // TODO: Bukkit treetype is bad
void spawn(Location location, Class<Entity> entity, Consumer<Entity> consumer); // TODO: Bukkit Entity is bad
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.generic.world;
import com.dfsek.terra.api.generic.world.block.Block;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.MaterialData;
/**
* Interface to be implemented for world manipulation.
*/
public interface WorldHandle {
void setBlockData(Block block, BlockData data, boolean physics);
BlockData getBlockData(Block block);
MaterialData getType(Block block);
BlockData createBlockData(String data);
MaterialData createMaterialData(String data);
}

View File

@@ -0,0 +1,5 @@
package com.dfsek.terra.api.generic.world.block;
public enum Axis {
X, Y, Z
}

View File

@@ -0,0 +1,28 @@
package com.dfsek.terra.api.generic.world.block;
import com.dfsek.terra.api.generic.Handle;
import com.dfsek.terra.api.generic.world.vector.Location;
public interface Block extends Handle {
void setBlockData(BlockData data, boolean physics);
BlockData getBlockData();
Block getRelative(BlockFace face);
Block getRelative(BlockFace face, int len);
boolean isEmpty();
Location getLocation();
MaterialData getType();
int getX();
int getZ();
int getY();
boolean isPassable();
}

View File

@@ -0,0 +1,11 @@
package com.dfsek.terra.api.generic.world.block;
import com.dfsek.terra.api.generic.Handle;
public interface BlockData extends Cloneable, Handle {
MaterialData getMaterial();
boolean matches(MaterialData materialData);
BlockData clone();
}

View File

@@ -0,0 +1,148 @@
package com.dfsek.terra.api.generic.world.block;
import com.dfsek.terra.api.generic.world.vector.Vector3;
import org.jetbrains.annotations.NotNull;
public enum BlockFace {
NORTH(0, 0, -1),
EAST(1, 0, 0),
SOUTH(0, 0, 1),
WEST(-1, 0, 0),
UP(0, 1, 0),
DOWN(0, -1, 0),
NORTH_EAST(NORTH, EAST),
NORTH_WEST(NORTH, WEST),
SOUTH_EAST(SOUTH, EAST),
SOUTH_WEST(SOUTH, WEST),
WEST_NORTH_WEST(WEST, NORTH_WEST),
NORTH_NORTH_WEST(NORTH, NORTH_WEST),
NORTH_NORTH_EAST(NORTH, NORTH_EAST),
EAST_NORTH_EAST(EAST, NORTH_EAST),
EAST_SOUTH_EAST(EAST, SOUTH_EAST),
SOUTH_SOUTH_EAST(SOUTH, SOUTH_EAST),
SOUTH_SOUTH_WEST(SOUTH, SOUTH_WEST),
WEST_SOUTH_WEST(WEST, SOUTH_WEST),
SELF(0, 0, 0);
private final int modX;
private final int modY;
private final int modZ;
BlockFace(final int modX, final int modY, final int modZ) {
this.modX = modX;
this.modY = modY;
this.modZ = modZ;
}
BlockFace(final BlockFace face1, final BlockFace face2) {
this.modX = face1.getModX() + face2.getModX();
this.modY = face1.getModY() + face2.getModY();
this.modZ = face1.getModZ() + face2.getModZ();
}
/**
* Get the amount of X-coordinates to modify to get the represented block
*
* @return Amount of X-coordinates to modify
*/
public int getModX() {
return modX;
}
/**
* Get the amount of Y-coordinates to modify to get the represented block
*
* @return Amount of Y-coordinates to modify
*/
public int getModY() {
return modY;
}
/**
* Get the amount of Z-coordinates to modify to get the represented block
*
* @return Amount of Z-coordinates to modify
*/
public int getModZ() {
return modZ;
}
/**
* Gets the normal vector corresponding to this block face.
*
* @return the normal vector
*/
@NotNull
public Vector3 getDirection() {
Vector3 direction = new Vector3(modX, modY, modZ);
if(modX != 0 || modY != 0 || modZ != 0) {
direction.normalize();
}
return direction;
}
@NotNull
public BlockFace getOppositeFace() {
switch(this) {
case NORTH:
return BlockFace.SOUTH;
case SOUTH:
return BlockFace.NORTH;
case EAST:
return BlockFace.WEST;
case WEST:
return BlockFace.EAST;
case UP:
return BlockFace.DOWN;
case DOWN:
return BlockFace.UP;
case NORTH_EAST:
return BlockFace.SOUTH_WEST;
case NORTH_WEST:
return BlockFace.SOUTH_EAST;
case SOUTH_EAST:
return BlockFace.NORTH_WEST;
case SOUTH_WEST:
return BlockFace.NORTH_EAST;
case WEST_NORTH_WEST:
return BlockFace.EAST_SOUTH_EAST;
case NORTH_NORTH_WEST:
return BlockFace.SOUTH_SOUTH_EAST;
case NORTH_NORTH_EAST:
return BlockFace.SOUTH_SOUTH_WEST;
case EAST_NORTH_EAST:
return BlockFace.WEST_SOUTH_WEST;
case EAST_SOUTH_EAST:
return BlockFace.WEST_NORTH_WEST;
case SOUTH_SOUTH_EAST:
return BlockFace.NORTH_NORTH_WEST;
case SOUTH_SOUTH_WEST:
return BlockFace.NORTH_NORTH_EAST;
case WEST_SOUTH_WEST:
return BlockFace.EAST_NORTH_EAST;
case SELF:
return BlockFace.SELF;
}
return BlockFace.SELF;
}
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.generic.world.block;
import com.dfsek.terra.api.generic.Handle;
public interface MaterialData extends Handle {
boolean matches(MaterialData other);
boolean matches(BlockData other);
boolean isSolid();
boolean isAir();
double getMaxDurability();
}

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
public interface AnaloguePowerable extends BlockData {
int getMaximumPower();
int getPower();
void setPower(int power);
}

View File

@@ -0,0 +1,20 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
public interface Bisected extends BlockData {
Half getHalf();
void setHalf(Half half);
enum Half {
/**
* The top half of the block, normally with the higher y coordinate.
*/
TOP,
/**
* The bottom half of the block, normally with the lower y coordinate.
*/
BOTTOM
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.BlockFace;
public interface Directional extends BlockData {
BlockFace getFacing();
void setFacing(BlockFace facing);
}

View File

@@ -0,0 +1,16 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.BlockFace;
import java.util.Set;
public interface MultipleFacing extends BlockData {
Set<BlockFace> getFaces();
void setFace(BlockFace face, boolean facing);
Set<BlockFace> getAllowedFaces();
boolean hasFace(BlockFace f);
}

View File

@@ -0,0 +1,12 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.Axis;
import com.dfsek.terra.api.generic.world.block.BlockData;
import java.util.Set;
public interface Orientable extends BlockData {
Set<Axis> getAxes();
void setAxis(Axis axis);
Axis getAxis();
}

View File

@@ -0,0 +1,22 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
public interface Rail extends BlockData {
Shape getShape();
void setShape(Shape newShape);
enum Shape {
ASCENDING_EAST,
ASCENDING_NORTH,
ASCENDING_SOUTH,
ASCENDING_WEST,
EAST_WEST,
NORTH_EAST,
NORTH_SOUTH,
NORTH_WEST,
SOUTH_EAST,
SOUTH_WEST
}
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.BlockFace;
import java.util.Set;
public interface RedstoneWire extends BlockData, AnaloguePowerable {
Set<BlockFace> getAllowedFaces();
Connection getFace(BlockFace face);
void setFace(BlockFace face, Connection connection);
enum Connection {
NONE, SIDE, UP
}
}

View File

@@ -0,0 +1,10 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.BlockFace;
public interface Rotatable extends BlockData {
BlockFace getRotation();
void setRotation(BlockFace face);
}

View File

@@ -0,0 +1,4 @@
package com.dfsek.terra.api.generic.world.block.data;
public interface Slab extends Bisected, Waterlogged {
}

View File

@@ -0,0 +1,30 @@
package com.dfsek.terra.api.generic.world.block.data;
public interface Stairs extends Waterlogged, Directional, Bisected {
Shape getShape();
void setShape(Shape shape);
enum Shape {
/**
* Regular stair block.
*/
STRAIGHT,
/**
* Inner corner stair block with higher left side.
*/
INNER_LEFT,
/**
* Inner corner stair block with higher right side.
*/
INNER_RIGHT,
/**
* Outer corner stair block with higher left side.
*/
OUTER_LEFT,
/**
* Outer corner stair block with higher right side.
*/
OUTER_RIGHT
}
}

View File

@@ -0,0 +1,15 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
import com.dfsek.terra.api.generic.world.block.BlockFace;
public interface Wall extends BlockData, Waterlogged {
boolean isUp();
void setHeight(BlockFace face, Height height);
Height getHeight(BlockFace face);
void setUp(boolean up);
enum Height {
LOW, NONE, TALL
}
}

View File

@@ -0,0 +1,9 @@
package com.dfsek.terra.api.generic.world.block.data;
import com.dfsek.terra.api.generic.world.block.BlockData;
public interface Waterlogged extends BlockData {
boolean isWaterlogged();
void setWaterlogged(boolean waterlogged);
}

View File

@@ -0,0 +1,4 @@
package com.dfsek.terra.api.generic.world.block.state;
public interface BlockState {
}

View File

@@ -0,0 +1,108 @@
package com.dfsek.terra.api.generic.world.vector;
import com.dfsek.terra.api.generic.world.World;
import com.dfsek.terra.api.generic.world.block.Block;
public class Location implements Cloneable {
private World world;
private Vector3 vector;
public Location(World w, double x, double y, double z) {
this.world = w;
this.vector = new Vector3(x, y, z);
}
public Location(World w, Vector3 vector) {
this.world = w;
this.vector = vector;
}
public void setWorld(World world) {
this.world = world;
}
public Vector3 getVector() {
return vector;
}
public void setVector(Vector3 vector) {
this.vector = vector;
}
@Override
public Location clone() {
try {
Location other = (Location) super.clone();
other.setVector(other.getVector().clone());
return other;
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
public int getBlockX() {
return vector.getBlockX();
}
public int getBlockY() {
return vector.getBlockY();
}
public int getBlockZ() {
return vector.getBlockZ();
}
public double getY() {
return vector.getY();
}
public Location setY(double y) {
vector.setY(y);
return this;
}
public double getX() {
return vector.getX();
}
public Location setX(double x) {
vector.setX(x);
return this;
}
public double getZ() {
return vector.getZ();
}
public Location setZ(double z) {
vector.setZ(z);
return this;
}
public World getWorld() {
return world;
}
public Location add(double x, double y, double z) {
vector.add(x, y, z);
return this;
}
public Block getBlock() {
return world.getBlockAt(this);
}
public Location subtract(int x, int y, int z) {
vector.subtract(x, y, z);
return this;
}
public Location add(Vector3 add) {
vector.add(add);
return this;
}
public Vector3 toVector() {
return vector.clone();
}
}

View File

@@ -0,0 +1,180 @@
package com.dfsek.terra.api.generic.world.vector;
import net.jafama.FastMath;
/**
* oh yeah
*/
@SuppressWarnings("unused")
public class Vector2 implements Cloneable {
private double x;
private double z;
/**
* Create a vector with a given X and Z component
*
* @param x X component
* @param z Z component
*/
public Vector2(double x, double z) {
this.x = x;
this.z = z;
}
/**
* Get X component
*
* @return X component
*/
public double getX() {
return x;
}
public Vector2 setX(double x) {
this.x = x;
return this;
}
/**
* Get Z component
*
* @return Z component
*/
public double getZ() {
return z;
}
public Vector2 setZ(double z) {
this.z = z;
return this;
}
/**
* Multiply X and Z components by a value.
*
* @param m Value to multiply
* @return Mutated vector, for chaining.
*/
public Vector2 multiply(double m) {
x *= m;
z *= m;
return this;
}
/**
* Add this vector to another.
*
* @param other Vector to add
* @return Mutated vector, for chaining.
*/
public Vector2 add(Vector2 other) {
x += other.x;
z += other.z;
return this;
}
/**
* Subtract a vector from this vector,
*
* @param other Vector to subtract
* @return Mutated vector, for chaining.
*/
public Vector2 subtract(Vector2 other) {
x -= other.x;
z -= other.z;
return this;
}
/**
* Normalize this vector to length 1
*
* @return Mutated vector, for chaining.
*/
public Vector2 normalize() {
divide(length());
return this;
}
/**
* Divide X and Z components by a value.
*
* @param d Divisor
* @return Mutated vector, for chaining.
*/
public Vector2 divide(double d) {
x /= d;
z /= d;
return this;
}
/**
* Get the length of this Vector
*
* @return length
*/
public double length() {
return FastMath.sqrt(lengthSquared());
}
/**
* Get the squared length of this Vector
*
* @return squared length
*/
public double lengthSquared() {
return x * x + z * z;
}
/**
* Get the distance from this vector to another.
*
* @param other Another vector
* @return Distance between vectors
*/
public double distance(Vector2 other) {
return FastMath.sqrt(distanceSquared(other));
}
/**
* Get the squared distance between 2 vectors.
*
* @param other Another vector
* @return Squared distance
*/
public double distanceSquared(Vector2 other) {
double dx = other.x - x;
double dz = other.z - z;
return dx * dx + dz * dz;
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + Double.hashCode(x);
hash = 31 * hash + Double.hashCode(z);
return hash;
}
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Vector2)) {
return false;
}
Vector2 other = (Vector2) obj;
return other.x == this.x && other.z == this.z;
}
@Override
public Vector2 clone() {
try {
return (Vector2) super.clone();
} catch(CloneNotSupportedException e) {
throw new Error(e);
}
}
@Override
public String toString() {
return "(" + x + ", " + z + ")";
}
}

Some files were not shown because too many files have changed in this diff Show More