Dump Gaea into Terra.

This commit is contained in:
dfsek
2020-12-10 10:46:56 -07:00
parent 392ba59741
commit 95e39324c7
207 changed files with 7719 additions and 330 deletions

View File

@@ -18,6 +18,7 @@ repositories {
maven { url = uri("http://maven.enginehub.org/repo/") }
maven { url = uri("https://repo.codemc.org/repository/maven-public") }
maven { url = uri("https://papermc.io/repo/repository/maven-public/") }
maven { url = uri("https://repo.aikar.co/content/groups/aikar/") }
}
java {
@@ -30,9 +31,9 @@ val versionObj = Version("2", "2", "0", true)
version = versionObj
dependencies {
val gaeaVersion = "1.15.0"
compileOnly("org.polydev.gaea:Gaea:${gaeaVersion}")
testImplementation("org.polydev.gaea:Gaea:${gaeaVersion}")
implementation("org.apache.commons:commons-rng-core:1.3")
implementation("net.jafama:jafama:2.3.2")
implementation("co.aikar:taskchain-bukkit:3.7.2")
compileOnly("org.jetbrains:annotations:20.1.0")
@@ -100,6 +101,8 @@ tasks.named<ShadowJar>("shadowJar") {
relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib")
relocate("net.jafama", "com.dfsek.terra.lib.jafama")
relocate("com.dfsek.tectonic", "com.dfsek.terra.lib.tectonic")
relocate("net.jafama", "com.dfsek.terra.lib.jafama")
relocate("co.aikar.taskchain", "com.dfsek.terra.lib.taskchain")
minimize()
}

View File

@@ -2,6 +2,11 @@ package com.dfsek.terra;
import com.dfsek.tectonic.loading.TypeRegistry;
import com.dfsek.terra.api.bukkit.BukkitWorldHandle;
import com.dfsek.terra.api.gaea.GaeaPlugin;
import com.dfsek.terra.api.gaea.generation.GaeaChunkGenerator;
import com.dfsek.terra.api.gaea.lang.Language;
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.api.generic.world.WorldHandle;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
@@ -56,11 +61,6 @@ import org.bukkit.entity.EntityType;
import org.bukkit.generator.ChunkGenerator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.polydev.gaea.GaeaPlugin;
import org.polydev.gaea.generation.GaeaChunkGenerator;
import org.polydev.gaea.lang.Language;
import org.polydev.gaea.math.ProbabilityCollection;
import org.polydev.gaea.math.Range;
import java.util.HashMap;
import java.util.Map;

View File

@@ -1,9 +1,9 @@
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 org.bukkit.World;
import org.polydev.gaea.profiler.DataType;
import org.polydev.gaea.profiler.Measurement;
import org.polydev.gaea.profiler.WorldProfiler;
public class TerraProfiler extends WorldProfiler {
public TerraProfiler(World w) {

View File

@@ -1,5 +1,6 @@
package com.dfsek.terra;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.biome.BiomeZone;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.biome.grid.master.TerraRadialBiomeGrid;
@@ -11,7 +12,6 @@ import com.dfsek.terra.debug.Debug;
import com.dfsek.terra.generation.TerraChunkGenerator;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.polydev.gaea.biome.BiomeGrid;
public class TerraWorld {
private final TerraBiomeGrid grid;

View File

@@ -0,0 +1,26 @@
package com.dfsek.terra.api.gaea;
import org.bukkit.plugin.java.JavaPlugin;
public class Debug {
public static JavaPlugin main;
public static void setMain(JavaPlugin 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,19 @@
package com.dfsek.terra.api.gaea;
import org.bukkit.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,11 @@
package com.dfsek.terra.api.gaea;
import com.dfsek.terra.api.gaea.generation.GaeaChunkGenerator;
import com.dfsek.terra.api.gaea.lang.Language;
import org.bukkit.plugin.java.JavaPlugin;
public abstract class GaeaPlugin extends JavaPlugin {
public abstract boolean isDebug();
public abstract Class<? extends GaeaChunkGenerator> getGeneratorClass();
public abstract Language getLanguage();
}

View File

@@ -0,0 +1,40 @@
package com.dfsek.terra.api.gaea.biome;
import org.bukkit.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.
*/
org.bukkit.block.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 org.bukkit.Location;
import org.bukkit.World;
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,22 @@
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 org.bukkit.block.Biome getVanillaBiome();
public abstract ProbabilityCollection<Flora> getFlora();
public abstract int getFloraChance();
}

View File

@@ -0,0 +1,52 @@
package com.dfsek.terra.api.gaea.biome;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.gaea.math.Interpolator;
import com.dfsek.terra.api.gaea.world.palette.Palette;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
public abstract class Generator {
/**
* Gets the 2D noise at a pair of coordinates using the provided FastNoiseLite instance.
*
* @param gen - The FastNoiseLite instance to use.
* @param x - The x coordinate.
* @param z - The z coordinate.
* @return double - Noise value at the specified coordinates.
*/
public abstract double getNoise(FastNoiseLite gen, World w, int x, int z);
/**
* Gets the 3D noise at a pair of coordinates using the provided FastNoiseLite instance.
*
* @param gen - The FastNoiseLite instance to use.
* @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(FastNoiseLite gen, 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,111 @@
package com.dfsek.terra.api.gaea.command;
import com.dfsek.terra.api.gaea.GaeaPlugin;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Represents a command or subcommand, can be nested via getSubCommands.
*/
public abstract class Command implements CommandExecutor, TabCompleter {
private final GaeaPlugin main;
public Command(GaeaPlugin main) {
this.main = main;
}
public Command(com.dfsek.terra.api.gaea.command.Command parent) {
main = parent.getMain();
}
public GaeaPlugin getMain() {
return main;
}
/**
* Gets the name of the command/subcommand
* @return Name of command
*/
public abstract String getName();
/**
* Gets a list of subcommands
* @return List of subcommands
*/
public abstract List<com.dfsek.terra.api.gaea.command.Command> getSubCommands();
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
public abstract boolean execute(@NotNull CommandSender sender, @NotNull org.bukkit.command.Command command, @NotNull String label, @NotNull String[] args);
/**
* Gets the number of arguments
* @return Number of arguments
*/
public abstract int arguments();
/**
* Executes the given command, invoking subcommands if applicable and returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
@Override
public final boolean onCommand(@NotNull CommandSender sender, @NotNull org.bukkit.command.Command command, @NotNull String label, @NotNull String[] args) {
if(this instanceof DebugCommand && ! main.isDebug()) {
main.getLanguage().send("command.debug-only", sender);
return true;
}
if(args.length > 0) {
for(com.dfsek.terra.api.gaea.command.Command c : getSubCommands()) {
if(c.getName().equals(args[0])) {
return c.onCommand(sender, command, label, Arrays.stream(args, 1, args.length).toArray(String[]::new));
}
}
if(args.length != arguments()) {
main.getLanguage().send("command.invalid", sender, String.valueOf(arguments()), String.valueOf(args.length));
return true;
}
return execute(sender, command, label, args);
}
if(args.length != arguments()) {
main.getLanguage().send("command.invalid", sender, String.valueOf(arguments()), String.valueOf(args.length));
return true;
}
return execute(sender, command, label, new String[] {});
}
public abstract List<String> getTabCompletions(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args);
@Override
public final @Nullable List<String> onTabComplete(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, @NotNull String alias, @NotNull String[] args) {
List<String> complete = new ArrayList<>();
if(args.length > 0) for(com.dfsek.terra.api.gaea.command.Command c : getSubCommands()) {
if(c.getName().startsWith(args[0])) complete.add(c.getName());
if(c.getName().equals(args[0])) return c.onTabComplete(sender, command, alias, Arrays.stream(args, 1, args.length).toArray(String[]::new));
}
complete.addAll(getTabCompletions(sender, alias, args));
return complete;
}
}

View File

@@ -0,0 +1,8 @@
package com.dfsek.terra.api.gaea.command;
/**
* Implementing this interface marks a command as debug-only.
* If a parent command implements this interface, all child commands will be considered debug commands, regardless of whether they implement DebugCommand as well.
*/
public interface DebugCommand {
}

View File

@@ -0,0 +1,49 @@
package com.dfsek.terra.api.gaea.command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A command that may only be executed by a player. If executor is not a player, a message will be displayed and no action will be performed.
*/
public abstract class PlayerCommand extends Command {
public PlayerCommand(Command parent) {
super(parent);
}
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
@Override
public final boolean execute(@NotNull CommandSender sender, org.bukkit.command.@NotNull Command command, @NotNull String label, @NotNull String[] args) {
if(!(sender instanceof Player)) {
getMain().getLanguage().send("command.players-only", sender);
return true;
}
Player p = (Player) sender;
return execute(p, command, label, args);
}
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Player that executed command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
public abstract boolean execute(@NotNull Player sender, org.bukkit.command.@NotNull Command command, @NotNull String label, @NotNull String[] args);
}

View File

@@ -0,0 +1,54 @@
package com.dfsek.terra.api.gaea.command;
import com.dfsek.terra.api.gaea.generation.GaeaChunkGenerator;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* A command that must be executed by a player, in a Terra world.
*/
public abstract class WorldCommand extends PlayerCommand {
public WorldCommand(com.dfsek.terra.api.gaea.command.Command parent) {
super(parent);
}
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Source of the command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @return true if a valid command, otherwise false
*/
@Override
public final boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
Class<? extends GaeaChunkGenerator> clazz = getMain().getGeneratorClass();
if(clazz.isInstance(sender.getWorld().getGenerator())) {
return execute(sender, command, label, args, sender.getWorld());
} else {
getMain().getLanguage().send("command.world", sender);
}
return true;
}
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command
* (if defined) will be sent to the player.
*
* @param sender Player that executed command
* @param command Command which was executed
* @param label Alias of the command which was used
* @param args Passed command arguments
* @param world World in which command was executed
* @return true if a valid command, otherwise false
*/
public abstract boolean execute(@NotNull Player sender, @NotNull Command command, @NotNull String label, @NotNull String[] args, World world);
}

View File

@@ -0,0 +1,86 @@
package com.dfsek.terra.api.gaea.generation;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.math.ChunkInterpolator;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.gaea.profiler.ProfileFuture;
import com.dfsek.terra.api.gaea.profiler.WorldProfiler;
import org.bukkit.World;
import org.bukkit.generator.ChunkGenerator;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Random;
public abstract class GaeaChunkGenerator extends ChunkGenerator {
private final ChunkInterpolator.InterpolationType interpolationType;
private FastNoiseLite gen;
private WorldProfiler profiler;
public GaeaChunkGenerator(ChunkInterpolator.InterpolationType type) {
interpolationType = type;
}
@Override
public @NotNull ChunkData generateChunkData(@NotNull World world, @NotNull Random random, int chunkX, int chunkZ, @NotNull BiomeGrid biome) {
try(ProfileFuture ignore = measure("TotalChunkGenTime")) {
if(gen == null) {
gen = new FastNoiseLite((int) world.getSeed());
gen.setNoiseType(FastNoiseLite.NoiseType.OpenSimplex2);
gen.setFractalType(FastNoiseLite.FractalType.FBm);
gen.setFractalOctaves(getNoiseOctaves(world));
gen.setFrequency(getNoiseFrequency(world));
}
ChunkData chunk;
ChunkInterpolator interp;
try(ProfileFuture ignored = measure("ChunkBaseGenTime")) {
interp = interpolationType.getInstance(world, chunkX, chunkZ, this.getBiomeGrid(world), gen);
chunk = generateBase(world, random, chunkX, chunkZ, interp);
}
try(ProfileFuture ignored = measure("BiomeApplyTime")) {
com.dfsek.terra.api.gaea.biome.BiomeGrid grid = getBiomeGrid(world);
int xOrig = (chunkX << 4);
int zOrig = (chunkZ << 4);
for(byte x = 0; x < 4; x++) {
for(byte z = 0; z < 4; z++) {
int cx = xOrig + (x << 2);
int cz = zOrig + (z << 2);
Biome b = grid.getBiome(cx, cz, GenerationPhase.PALETTE_APPLY);
biome.setBiome(x << 2, z << 2, b.getVanillaBiome());
}
}
}
for(GenerationPopulator g : getGenerationPopulators(world)) {
g.populate(world, chunk, random, chunkX, chunkZ, interp);
}
return chunk;
}
}
public void attachProfiler(WorldProfiler p) {
this.profiler = p;
}
public WorldProfiler getProfiler() {
return profiler;
}
private ProfileFuture measure(String id) {
if(profiler != null) return profiler.measure(id);
return null;
}
public abstract ChunkData generateBase(@NotNull World world, @NotNull Random random, int x, int z, ChunkInterpolator noise);
public abstract int getNoiseOctaves(World w);
public abstract double getNoiseFrequency(World w);
public abstract List<GenerationPopulator> getGenerationPopulators(World w);
public abstract com.dfsek.terra.api.gaea.biome.BiomeGrid getBiomeGrid(World w);
public FastNoiseLite getNoiseGenerator() {
return gen;
}
}

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,11 @@
package com.dfsek.terra.api.gaea.generation;
import com.dfsek.terra.api.gaea.math.ChunkInterpolator;
import org.bukkit.World;
import org.bukkit.generator.ChunkGenerator;
import java.util.Random;
public abstract class GenerationPopulator {
public abstract void populate(World world, ChunkGenerator.ChunkData chunk, Random r, int chunkX, int chunkZ, ChunkInterpolator interp);
}

View File

@@ -0,0 +1,40 @@
package com.dfsek.terra.api.gaea.lang;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
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 extends YamlConfiguration {
public Language(File file) throws IOException, InvalidConfigurationException {
load(file);
}
@Override
public void load(@NotNull File file) throws IOException, InvalidConfigurationException {
super.load(file);
}
@SuppressWarnings("unchecked")
public Message getMessage(String id) {
Object m = get(id);
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,12 @@
package com.dfsek.terra.api.gaea.lang;
import org.bukkit.command.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,34 @@
package com.dfsek.terra.api.gaea.lang;
import org.bukkit.ChatColor;
import org.bukkit.command.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, ChatColor.translateAlternateColorCodes('&', String.format(line, Arrays.asList(args).toArray())));
}
}
@Override
public void send(CommandSender sender, String... args) {
for(String line: message) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', 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 org.bukkit.ChatColor;
import org.bukkit.command.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, ChatColor.translateAlternateColorCodes('&', String.format(message, Arrays.asList(args).toArray())));
}
@Override
public void send(CommandSender sender, String... args) {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', String.format(message, Arrays.asList(args).toArray())));
}
@Override
public boolean isEmpty() {
return message == null || message.equals("");
}
}

View File

@@ -0,0 +1,32 @@
package com.dfsek.terra.api.gaea.math;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import org.bukkit.World;
public interface ChunkInterpolator {
double getNoise(double x, double z);
double getNoise(double x, double y, double z);
enum InterpolationType {
/**
* 2D (Bilinear) interpolation
*/
BILINEAR {
@Override
public com.dfsek.terra.api.gaea.math.ChunkInterpolator getInstance(World w, int chunkX, int chunkZ, BiomeGrid grid, FastNoiseLite noise) {
return new ChunkInterpolator2(w, chunkX, chunkZ, grid, noise);
}
},
/**
* 3D (Trilinear) interpolation
*/
TRILINEAR {
@Override
public com.dfsek.terra.api.gaea.math.ChunkInterpolator getInstance(World w, int chunkX, int chunkZ, BiomeGrid grid, FastNoiseLite noise) {
return new ChunkInterpolator3(w, chunkX, chunkZ, grid, noise);
}
};
public abstract com.dfsek.terra.api.gaea.math.ChunkInterpolator getInstance(World w, int chunkX, int chunkZ, BiomeGrid grid, FastNoiseLite noise);
}
}

View File

@@ -0,0 +1,72 @@
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 org.bukkit.World;
/**
* 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 ChunkInterpolator2 implements ChunkInterpolator {
private final Interpolator[][] interpGrid = new Interpolator[4][4];
private final int xOrigin;
private final int zOrigin;
private final FastNoiseLite noise;
private final World w;
/**
* Instantiates a 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.
* @param noise FastNoiseLite instance to use.
*/
public ChunkInterpolator2(World w, int chunkX, int chunkZ, BiomeGrid grid, FastNoiseLite noise) {
this.xOrigin = chunkX << 4;
this.zOrigin = chunkZ << 4;
this.noise = noise;
this.w = w;
Generator[][] gridTemp = new Generator[8][8];
for(int x = - 2; x < 6; x++) {
for(int z = - 2; z < 6; z++) {
gridTemp[x + 2][z + 2] = grid.getBiome(xOrigin + (x << 2), zOrigin + (z << 2), GenerationPhase.BASE).getGenerator();
}
}
for(byte x = 0; x < 4; x++) {
for(byte z = 0; z < 4; z++) {
interpGrid[x][z] = new Interpolator(biomeAvg(x, z, gridTemp),
biomeAvg(x + 1, z, gridTemp),
biomeAvg(x, z + 1, gridTemp),
biomeAvg(x + 1, z + 1, gridTemp), gridTemp[x+1][z+1].getInterpolationType());
}
}
}
private double biomeAvg(int x, int z, Generator[][] g) {
return (g[x + 3][z + 2].getNoise(noise, w, (x << 2) + xOrigin, (z << 2) + zOrigin)
+ g[x + 1][z + 2].getNoise(noise, w, (x << 2) + xOrigin, (z << 2) + zOrigin)
+ g[x + 2][z + 3].getNoise(noise, w, (x << 2) + xOrigin, (z << 2) + zOrigin)
+ g[x + 2][z + 1].getNoise(noise, w, (x << 2) + xOrigin, (z << 2) + zOrigin)) / 4D;
}
/**
* 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.
*/
@Override
public double getNoise(double x, double z) {
return interpGrid[((int) x) / 4][((int) z) / 4].bilerp((x % 4) / 4, (z % 4) / 4);
}
@Override
public double getNoise(double x, double y, double z) {
return getNoise(x, z);
}
}

View File

@@ -0,0 +1,137 @@
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 net.jafama.FastMath;
import org.bukkit.World;
/**
* 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 implements ChunkInterpolator {
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 FastNoiseLite noise;
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.
* @param noise FastNoiseLite instance to use.
*/
public ChunkInterpolator3(World w, int chunkX, int chunkZ, BiomeGrid grid, FastNoiseLite noise) {
this.xOrigin = chunkX << 4;
this.zOrigin = chunkZ << 4;
this.noise = noise;
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(noise, 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;
}
}
@Override
public double getNoise(double x, double z) {
return getNoise(x, 0, z);
}
/**
* 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.
*/
@Override
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,49 @@
package com.dfsek.terra.api.gaea.population;
import org.bukkit.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,11 @@
package com.dfsek.terra.api.gaea.population;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import java.util.Random;
public abstract class GaeaBlockPopulator {
public abstract void populate(@NotNull World world, @NotNull Random random, @NotNull Chunk chunk);
}

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 org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.plugin.java.JavaPlugin;
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 extends BlockPopulator {
private final List<GaeaBlockPopulator> attachedPopulators = new GlueList<>();
private final HashSet<ChunkCoordinate> needsPop = new HashSet<>();
private final JavaPlugin main;
private final Object popLock = new Object();
private WorldProfiler profiler;
public PopulationManager(JavaPlugin main) {
this.main = main;
}
public void attach(GaeaBlockPopulator populator) {
this.attachedPopulators.add(populator);
}
@Override
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(GaeaBlockPopulator r : attachedPopulators) {
r.populate(w, random, currentChunk);
}
needsPop.remove(c);
}
}
}

View File

@@ -0,0 +1,45 @@
package com.dfsek.terra.api.gaea.profiler;
import net.jafama.FastMath;
import net.md_5.bungee.api.ChatColor;
/**
* 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) {
double range = desiredRangePercent * desired;
ChatColor color = ChatColor.YELLOW;
if(FastMath.abs(data - desired) > range) {
if(data > desired) color = type.getDesire().getHighColor();
else color = type.getDesire().getLowColor();
}
return color + type.getFormatted(data) + ChatColor.RESET;
}
}

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,36 @@
package com.dfsek.terra.api.gaea.profiler;
import net.md_5.bungee.api.ChatColor;
/**
* 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(ChatColor.RED, ChatColor.GREEN), HIGH(ChatColor.GREEN, ChatColor.RED);
private final ChatColor high;
private final ChatColor low;
Desire(ChatColor high, ChatColor low) {
this.high = high;
this.low = low;
}
/**
* Gets the color to display when the numerical value is higher than desired.
*
* @return ChatColor - color of the value.
*/
public ChatColor getHighColor() {
return high;
}
/**
* Gets the color to display when the numerical value is lower than desired.
*
* @return ChatColor - color of the value.
*/
public ChatColor getLowColor() {
return low;
}
}

View File

@@ -0,0 +1,94 @@
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 org.bukkit.Bukkit;
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);
if(value / 1000000 > 5000) Bukkit.getLogger().warning("Measurement took " + type.getFormatted(value));
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,88 @@
package com.dfsek.terra.api.gaea.profiler;
import com.dfsek.terra.api.gaea.generation.GaeaChunkGenerator;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import net.jafama.FastMath;
import org.bukkit.ChatColor;
import org.bukkit.World;
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() instanceof GaeaChunkGenerator))
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;
((GaeaChunkGenerator) w.getGenerator()).attachProfiler(this);
}
public String getResultsFormatted() {
if(! isProfiling) return "Profiler is not currently running.";
StringBuilder result = new StringBuilder(ChatColor.GOLD + "Gaea World Profiler Results (Min / Avg / Max / Std Dev): \n");
for(Map.Entry<String, Measurement> e : measures.entrySet()) {
result.append(ChatColor.GOLD)
.append(e.getKey())
.append(": ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMin()))
.append(ChatColor.GOLD)
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().average()))
.append(ChatColor.GOLD)
.append(" / ")
.append(e.getValue().getDataHolder().getFormattedData(e.getValue().getMax()))
.append(ChatColor.GOLD)
.append(" / ")
.append(ChatColor.GREEN)
.append((double) FastMath.round((e.getValue().getStdDev() / 1000000) * 100D) / 100D)
.append("ms")
.append(ChatColor.GOLD).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,101 @@
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.EnchantWithLevelsFunction;
import com.dfsek.terra.api.gaea.structures.loot.functions.Function;
import com.dfsek.terra.api.gaea.util.GlueList;
import net.jafama.FastMath;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
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 Material item;
private final long weight;
private final List<Function> functions = new GlueList<>();
/**
* Instantiates an Entry from a JSON representation.
*
* @param entry The JSON Object to instantiate from.
*/
public Entry(JSONObject entry) {
String id = entry.get("name").toString();
this.item = Material.matchMaterial(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;
case "minecraft:enchant_with_levels":
case "enchant_with_levels":
long maxEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("max");
long minEnchant = (long) ((JSONObject) ((JSONObject) function).get("levels")).get("min");
JSONArray disabled = null;
if(((JSONObject) function).containsKey("disabled_enchants"))
disabled = (JSONArray) ((JSONObject) function).get("disabled_enchants");
functions.add(new EnchantWithLevelsFunction(FastMath.toIntExact(minEnchant), FastMath.toIntExact(maxEnchant), disabled));
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 = new ItemStack(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,77 @@
package com.dfsek.terra.api.gaea.structures.loot;
import com.dfsek.terra.api.gaea.util.GlueList;
import org.bukkit.inventory.Inventory;
import org.bukkit.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) 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));
}
}
/**
* 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() == 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,57 @@
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 net.jafama.FastMath;
import org.bukkit.inventory.ItemStack;
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 = new ProbabilityCollection<>();
/**
* Instantiates a Pool from a JSON representation.
*
* @param pool The JSON Object to instantiate from.
*/
public Pool(JSONObject pool) {
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);
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,37 @@
package com.dfsek.terra.api.gaea.structures.loot.functions;
import org.bukkit.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 org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.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,61 @@
package com.dfsek.terra.api.gaea.structures.loot.functions;
import com.dfsek.terra.api.gaea.util.GlueList;
import net.jafama.FastMath;
import org.bukkit.Bukkit;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.json.simple.JSONArray;
import java.util.Collections;
import java.util.List;
import java.util.Random;
public class EnchantWithLevelsFunction implements Function {
private final int min;
private final int max;
private final JSONArray disabled;
public EnchantWithLevelsFunction(int min, int max, JSONArray disabled) {
this.max = max;
this.min = min;
this.disabled = disabled;
}
/**
* 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 enchant = (r.nextDouble() * (max - min)) + min;
List<Enchantment> possible = new GlueList<>();
for(Enchantment ench : Enchantment.values()) {
if(ench.canEnchantItem(original) && (disabled == null || !this.disabled.contains(ench.getName()))) {
possible.add(ench);
}
}
int numEnchant = (r.nextInt((int) FastMath.abs(enchant)) / 10 + 1);
if(possible.size() >= numEnchant) {
Collections.shuffle(possible);
iter:
for(int i = 0; i < numEnchant; i++) {
Enchantment chosen = possible.get(i);
for(Enchantment ench : original.getEnchantments().keySet()) {
if(chosen.conflictsWith(ench)) continue iter;
}
int lvl = r.nextInt(1 + (int) (((enchant / 40 > 1) ? 1 : enchant / 40) * (chosen.getMaxLevel())));
try {
original.addEnchantment(chosen, FastMath.max(lvl, 1));
} catch(IllegalArgumentException e) {
Bukkit.getLogger().warning("[Gaea] Attempted to enchant " + original.getType() + " with " + chosen + " at level " + FastMath.max(lvl, 1) + ", but an unexpected exception occurred! Usually this is caused by a misbehaving enchantment plugin.");
}
}
}
return original;
}
}

View File

@@ -0,0 +1,19 @@
package com.dfsek.terra.api.gaea.structures.loot.functions;
import org.bukkit.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,66 @@
package com.dfsek.terra.api.gaea.tree;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.dfsek.terra.api.gaea.tree.fractal.TreeGetter;
import com.dfsek.terra.api.gaea.tree.fractal.trees.Cactus;
import com.dfsek.terra.api.gaea.tree.fractal.trees.IceSpike;
import com.dfsek.terra.api.gaea.tree.fractal.trees.OakTree;
import com.dfsek.terra.api.gaea.tree.fractal.trees.ShatteredPillar;
import com.dfsek.terra.api.gaea.tree.fractal.trees.ShatteredTree;
import com.dfsek.terra.api.gaea.tree.fractal.trees.SmallShatteredPillar;
import com.dfsek.terra.api.gaea.tree.fractal.trees.SmallShatteredTree;
import com.dfsek.terra.api.gaea.tree.fractal.trees.SpruceTree;
import org.bukkit.Location;
import java.util.Random;
public enum CustomTreeType implements TreeGetter {
SHATTERED_SMALL {
@Override
public FractalTree getTree(Location l, Random r) {
return new SmallShatteredTree(l, r);
}
},
SHATTERED_LARGE {
@Override
public FractalTree getTree(Location l, Random r) {
return new ShatteredTree(l, r);
}
},
GIANT_OAK {
@Override
public FractalTree getTree(Location l, Random r) {
return new OakTree(l, r);
}
},
GIANT_SPRUCE {
@Override
public FractalTree getTree(Location l, Random r) {
return new SpruceTree(l, r);
}
},
SMALL_SHATTERED_PILLAR {
@Override
public FractalTree getTree(Location l, Random r) {
return new SmallShatteredPillar(l, r);
}
},
LARGE_SHATTERED_PILLAR {
@Override
public FractalTree getTree(Location l, Random r) {
return new ShatteredPillar(l, r);
}
},
CACTUS {
@Override
public FractalTree getTree(Location l, Random r) {
return new Cactus(l, r);
}
},
ICE_SPIKE {
@Override
public FractalTree getTree(Location l, Random r) {
return new IceSpike(l, r);
}
}
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.gaea.tree;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Random;
import java.util.Set;
public interface Tree {
boolean plant(Location l, Random r, JavaPlugin main);
Set<Material> getSpawnable();
}

View File

@@ -0,0 +1,105 @@
package com.dfsek.terra.api.gaea.tree;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import com.google.common.collect.Sets;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Collections;
import java.util.Random;
import java.util.Set;
public enum TreeType implements Tree {
SHATTERED_SMALL(null, Collections.singleton(Material.END_STONE)),
SHATTERED_LARGE(null, Collections.singleton(Material.END_STONE)),
GIANT_OAK(null, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
GIANT_SPRUCE(null, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
SMALL_SHATTERED_PILLAR(null, Collections.singleton(Material.END_STONE)),
LARGE_SHATTERED_PILLAR(null, Collections.singleton(Material.END_STONE)),
CACTUS(null, Sets.newHashSet(Material.SAND, Material.RED_SAND)),
ICE_SPIKE(null, Sets.newHashSet(Material.SNOW_BLOCK, Material.SNOW, Material.STONE, Material.GRASS_BLOCK)),
OAK(org.bukkit.TreeType.TREE, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
LARGE_OAK(org.bukkit.TreeType.BIG_TREE, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
SPRUCE(org.bukkit.TreeType.REDWOOD, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
LARGE_SPRUCE(org.bukkit.TreeType.TALL_REDWOOD, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
MEGA_SPRUCE(org.bukkit.TreeType.MEGA_REDWOOD, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
BIRCH(org.bukkit.TreeType.BIRCH, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
CHORUS_PLANT(org.bukkit.TreeType.CHORUS_PLANT, Sets.newHashSet(Material.END_STONE)),
ACACIA(org.bukkit.TreeType.ACACIA, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
TALL_BIRCH(org.bukkit.TreeType.TALL_BIRCH, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
JUNGLE(org.bukkit.TreeType.JUNGLE, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
SMALL_JUNGLE(org.bukkit.TreeType.SMALL_JUNGLE, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
JUNGLE_COCOA(org.bukkit.TreeType.COCOA_TREE, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
JUNGLE_BUSH(org.bukkit.TreeType.JUNGLE_BUSH, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
DARK_OAK(org.bukkit.TreeType.DARK_OAK, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
BROWN_MUSHROOM(org.bukkit.TreeType.BROWN_MUSHROOM, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL, Material.MYCELIUM, Material.NETHERRACK, Material.CRIMSON_NYLIUM, Material.WARPED_NYLIUM)),
RED_MUSHROOM(org.bukkit.TreeType.RED_MUSHROOM, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL, Material.MYCELIUM, Material.NETHERRACK, Material.CRIMSON_NYLIUM, Material.WARPED_NYLIUM)),
SWAMP_OAK(org.bukkit.TreeType.SWAMP, Sets.newHashSet(Material.GRASS_BLOCK, Material.DIRT, Material.PODZOL)),
WARPED_FUNGUS(org.bukkit.TreeType.WARPED_FUNGUS, Collections.singleton(Material.WARPED_NYLIUM)),
CRIMSON_FUNGUS(org.bukkit.TreeType.CRIMSON_FUNGUS, Collections.singleton(Material.CRIMSON_NYLIUM));
private final org.bukkit.TreeType vanillaType;
private final Set<Material> spawnable;
TreeType(org.bukkit.TreeType vanillaType, Set<Material> spawnable) {
this.vanillaType = vanillaType;
this.spawnable = spawnable;
}
public static com.dfsek.terra.api.gaea.tree.TreeType fromBukkit(org.bukkit.TreeType type) {
switch(type) {
case TREE: return OAK;
case BIRCH: return BIRCH;
case ACACIA: return ACACIA;
case SWAMP: return SWAMP_OAK;
case JUNGLE: return JUNGLE;
case REDWOOD: return SPRUCE;
case BIG_TREE: return LARGE_OAK;
case DARK_OAK: return DARK_OAK;
case COCOA_TREE: return JUNGLE_COCOA;
case TALL_BIRCH: return TALL_BIRCH;
case JUNGLE_BUSH: return JUNGLE_BUSH;
case CHORUS_PLANT: return CHORUS_PLANT;
case MEGA_REDWOOD: return MEGA_SPRUCE;
case RED_MUSHROOM: return RED_MUSHROOM;
case SMALL_JUNGLE: return SMALL_JUNGLE;
case TALL_REDWOOD: return LARGE_SPRUCE;
case WARPED_FUNGUS: return WARPED_FUNGUS;
case BROWN_MUSHROOM: return BROWN_MUSHROOM;
case CRIMSON_FUNGUS: return CRIMSON_FUNGUS;
default: throw new IllegalArgumentException();
}
}
public boolean isCustom() {
return this.vanillaType == null;
}
public org.bukkit.TreeType getVanillaTreeType() {
return vanillaType;
}
public CustomTreeType getCustomTreeType() {
if(getVanillaTreeType() != null) return null;
return CustomTreeType.valueOf(this.toString());
}
public boolean plant(Location l, Random r, JavaPlugin main) {
if(this.getVanillaTreeType() == null) {
if(!spawnable.contains(l.subtract(0, 1, 0).getBlock().getType())) return false;
FractalTree tree = getCustomTreeType().getTree(l, r);
if(main.isEnabled()) co.aikar.taskchain.BukkitTaskChainFactory.create(main).newChain()
.async(tree::grow)
.sync(tree::plant)
.execute();
return true;
}
return l.getWorld().generateTree(l, this.getVanillaTreeType());
}
@Override
public Set<Material> getSpawnable() {
return spawnable;
}
}

View File

@@ -0,0 +1,31 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.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,112 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import com.dfsek.terra.api.gaea.util.GlueList;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Entity;
import org.bukkit.util.Consumer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
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 List<Material> replaceable = Arrays.asList(Material.AIR, Material.GRASS_BLOCK, Material.DIRT, Material.STONE, Material.COARSE_DIRT, Material.GRAVEL, Material.PODZOL,
Material.GRASS, Material.TALL_GRASS, Material.FERN, Material.POPPY, Material.LARGE_FERN, Material.BLUE_ORCHID, Material.AZURE_BLUET, Material.END_STONE, Material.SNOW, Material.SAND, Material.STONE_BUTTON, Material.DEAD_BUSH);
/**
* 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) {
this.origin = origin.add(0, 1, 0);
this.random = random;
}
/**
* 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, Material m) {
treeAssembler.put(l, m.createBlockData());
}
/**
* 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()) {
if(replaceable.contains(entry.getKey().getBlock().getType()))
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 Material getMaterial(Location l) {
return treeAssembler.getOrDefault(l, Material.AIR.createBlockData()).getMaterial();
}
}

View File

@@ -0,0 +1,66 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
public class TreeGeometry {
private final FractalTree tree;
public TreeGeometry(FractalTree tree) {
this.tree = tree;
}
public static Vector getPerpendicular(Vector v) {
return v.getZ() < v.getX() ? new Vector(v.getY(), - v.getX(), 0) : new Vector(0, - v.getZ(), v.getY());
}
public FractalTree getTree() {
return tree;
}
public void generateSphere(Location l, Material m, int radius, boolean overwrite) {
generateSphere(l, new ProbabilityCollection<Material>().add(m, 1), radius, overwrite);
}
public void generateCylinder(Location l, Material m, int radius, int height, boolean overwrite) {
generateCylinder(l, new ProbabilityCollection<Material>().add(m, 1), radius, height, overwrite);
}
public void generateSphere(Location l, ProbabilityCollection<Material> 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++) {
Vector position = l.toVector().clone().add(new Vector(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<Material> 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++) {
Vector position = l.toVector().clone().add(new Vector(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<Material> 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++) {
Vector position = l.toVector().clone().add(new Vector(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,9 @@
package com.dfsek.terra.api.gaea.tree.fractal;
import org.bukkit.Location;
import java.util.Random;
public interface TreeGetter {
FractalTree getTree(Location l, Random r);
}

View File

@@ -0,0 +1,28 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import org.bukkit.Location;
import org.bukkit.Material;
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) {
super(origin, random);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
int h = super.getRandom().nextInt(4) + 1;
for(int i = 0; i < h; i++) setBlock(super.getOrigin().clone().add(0, i, 0), Material.CACTUS);
}
}

View File

@@ -0,0 +1,47 @@
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 org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
import java.util.Random;
public class IceSpike extends FractalTree {
private final TreeGeometry geo;
private static final ProbabilityCollection<Material> ice = new ProbabilityCollection<Material>().add(Material.PACKED_ICE, 95).add(Material.BLUE_ICE, 5);
/**
* 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) {
super(origin, random);
geo = new TreeGeometry(this);
}
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() {
Vector direction = new Vector(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,59 @@
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 net.jafama.FastMath;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
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) {
super(origin, random);
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 Vector(super.getRandom().nextInt(5) - 2, super.getRandom().nextInt(4) + 6, super.getRandom().nextInt(5) - 2), 2, 0);
}
private void growBranch(Location l1, Vector diff, double d1, int recursions) {
if(recursions > 1) {
geo.generateSphere(l1, Material.OAK_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)), Material.OAK_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,47 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
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 org.bukkit.Location;
import org.bukkit.Material;
import java.util.Random;
public class Rock extends FractalTree {
private final TreeGeometry geo;
private final FastNoiseLite noise;
private final FastNoiseLite rock;
private static final ProbabilityCollection<Material> ice = new ProbabilityCollection<Material>().add(Material.PACKED_ICE, 95).add(Material.BLUE_ICE, 5);
/**
* Instantiates a TreeGrower at an origin location.
*
* @param origin - The origin location.
* @param random - The random object to use whilst generating the tree.
*/
public Rock(Location origin, Random random) {
super(origin, random);
int seed = origin.hashCode();
this.noise = new FastNoiseLite(seed);
this.rock = new FastNoiseLite(++seed);
geo = new TreeGeometry(this);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
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), ice, (int) ((1-((double) i / h))*2+1), true, 70);
}
for(int i = 0; i < h/3; i++) {
setBlock(l1.clone().add(0, h+i, 0), ice.get(super.getRandom()));
}
}
}

View File

@@ -0,0 +1,52 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.EnderCrystal;
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) {
super(origin, random);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
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), Material.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), Material.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), Material.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), Material.OBSIDIAN);
if(getRandom().nextInt(100) < 25)
spawnEntity(getOrigin().add(crystalLoc[0] + 0.5, max, crystalLoc[1] + 0.5), EnderCrystal.class,
enderCrystal -> ((EnderCrystal) enderCrystal).setShowingBottom(false));
}
}

View File

@@ -0,0 +1,66 @@
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 net.jafama.FastMath;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
import java.util.Random;
public class ShatteredTree extends FractalTree {
private final TreeGeometry geo;
private final ProbabilityCollection<Material> bark = new ProbabilityCollection<Material>()
.add(Material.OBSIDIAN, 1)
.add(Material.BLACK_CONCRETE, 1);
private final ProbabilityCollection<Material> leaves = new ProbabilityCollection<Material>()
.add(Material.PURPLE_STAINED_GLASS, 1)
.add(Material.MAGENTA_STAINED_GLASS, 1);
/**
* 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) {
super(origin, random);
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 Vector(super.getRandom().nextInt(5) - 2, super.getRandom().nextInt(4) + 6, super.getRandom().nextInt(5) - 2), 1, 0);
}
private void growBranch(Location l1, Vector 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,28 @@
package com.dfsek.terra.api.gaea.tree.fractal.trees;
import com.dfsek.terra.api.gaea.tree.fractal.FractalTree;
import org.bukkit.Location;
import org.bukkit.Material;
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) {
super(origin, random);
}
/**
* Grows the tree in memory. Intended to be invoked from an async thread.
*/
@Override
public void grow() {
int h = super.getRandom().nextInt(5) + 5;
for(int i = - h; i < h; i++) setBlock(super.getOrigin().clone().add(0, i, 0), Material.OBSIDIAN);
}
}

View File

@@ -0,0 +1,66 @@
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 net.jafama.FastMath;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
import java.util.Random;
public class SmallShatteredTree extends FractalTree {
private final TreeGeometry geo;
private final ProbabilityCollection<Material> bark = new ProbabilityCollection<Material>()
.add(Material.OBSIDIAN, 1)
.add(Material.BLACK_CONCRETE, 1);
private final ProbabilityCollection<Material> leaves = new ProbabilityCollection<Material>()
.add(Material.PURPLE_STAINED_GLASS, 1)
.add(Material.MAGENTA_STAINED_GLASS, 1);
/**
* 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) {
super(origin, random);
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 Vector(super.getRandom().nextInt(5) - 2, super.getRandom().nextInt(3) + 4, super.getRandom().nextInt(5) - 2), 1.5, 0);
}
private void growBranch(Location l1, Vector 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,46 @@
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 net.jafama.FastMath;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
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) {
super(origin, random);
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 Vector(0, 16 + super.getRandom().nextInt(5), 0));
}
private void growTrunk(Location l1, Vector diff) {
if(diff.getY() < 0) diff.rotateAroundAxis(TreeGeometry.getPerpendicular(diff.clone()).normalize(), FastMath.PI);
int d = (int) diff.length();
int rad = 7;
for(int i = 0; i < d; i++) {
geo.generateSphere(l1.clone().add(diff.clone().multiply((double) i / d)), Material.SPRUCE_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)), Material.SPRUCE_LEAVES, (int) (((6 - (i % 4))) * (1.25 - ((double) i / d))), 1, false);
}
setBlock(l1.clone().add(diff), Material.SPRUCE_LEAVES);
setBlock(l1.clone().add(diff).add(0, 1, 0), Material.SPRUCE_LEAVES);
}
}

View File

@@ -0,0 +1,64 @@
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), "com.dfsek.betterend.gaea", "com.dfsek.terra.api.gaea"); // Backwards compat with old BetterEnd shade 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,46 @@
package com.dfsek.terra.api.gaea.util;
import net.jafama.FastMath;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import java.util.List;
public class WorldUtil {
public static int getHighestValidSpawnAt(Chunk chunk, int x, int z) {
int y;
for(y = chunk.getWorld().getMaxHeight() - 1; (chunk.getBlock(x, y, z).getType() != Material.GRASS_BLOCK
&& chunk.getBlock(x, y, z).getType() != Material.GRAVEL
&& chunk.getBlock(x, y, z).getType() != Material.PODZOL
&& chunk.getBlock(x, y, z).getType() != Material.END_STONE
&& chunk.getBlock(x, y, z).getType() != Material.DIRT
&& chunk.getBlock(x, y, z).getType() != Material.STONE
&& chunk.getBlock(x, y, z).getType() != Material.SAND
&& chunk.getBlock(x, y, z).getType() != Material.RED_SAND
&& chunk.getBlock(x, y, z).getType() != Material.COARSE_DIRT) && y > 0; y--)
;
return y;
}
public static int getHighestBlockAt(Chunk chunk, int x, int z) {
int y;
for(y = chunk.getWorld().getMaxHeight() - 1; (chunk.getBlock(x, y, z).getType().isAir()) && y > 0; y--) ;
return y;
}
public static List<Location> getLocationListBetween(Location loc1, Location loc2) {
int lowX = FastMath.min(loc1.getBlockX(), loc2.getBlockX());
int lowY = FastMath.min(loc1.getBlockY(), loc2.getBlockY());
int lowZ = FastMath.min(loc1.getBlockZ(), loc2.getBlockZ());
List<Location> locs = new GlueList<>();
for(int x = 0; x <= FastMath.abs(loc1.getBlockX() - loc2.getBlockX()); x++) {
for(int y = 0; y <= FastMath.abs(loc1.getBlockY() - loc2.getBlockY()); y++) {
for(int z = 0; z <= FastMath.abs(loc1.getBlockZ() - loc2.getBlockZ()); z++) {
locs.add(new Location(loc1.getWorld(), (double) lowX + x, (double) lowY + y, (double) lowZ + z));
}
}
}
return locs;
}
}

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.gaea.world;
import com.dfsek.terra.api.gaea.math.Range;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.block.Block;
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,86 @@
package com.dfsek.terra.api.gaea.world;
import com.dfsek.terra.api.gaea.math.Range;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.google.common.collect.Sets;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public enum FloraType implements Flora {
TALL_GRASS(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:tall_grass[half=lower]"), Bukkit.createBlockData("minecraft:tall_grass[half=upper]")),
TALL_FERN(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:large_fern[half=lower]"), Bukkit.createBlockData("minecraft:large_fern[half=upper]")),
SUNFLOWER(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:sunflower[half=lower]"), Bukkit.createBlockData("minecraft:sunflower[half=upper]")),
ROSE_BUSH(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:rose_bush[half=lower]"), Bukkit.createBlockData("minecraft:rose_bush[half=upper]")),
LILAC(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:lilac[half=lower]"), Bukkit.createBlockData("minecraft:lilac[half=upper]")),
PEONY(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:peony[half=lower]"), Bukkit.createBlockData("minecraft:peony[half=upper]")),
GRASS(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:grass")),
FERN(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:fern")),
AZURE_BLUET(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:azure_bluet")),
LILY_OF_THE_VALLEY(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:lily_of_the_valley")),
BLUE_ORCHID(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:blue_orchid")),
POPPY(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:poppy")),
DANDELION(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:dandelion")),
WITHER_ROSE(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:wither_rose")),
DEAD_BUSH(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL, Material.SAND, Material.RED_SAND,
Material.TERRACOTTA, Material.BLACK_TERRACOTTA, Material.BLUE_TERRACOTTA, Material.BROWN_TERRACOTTA, Material.CYAN_TERRACOTTA, Material.GRAY_TERRACOTTA,
Material.GREEN_TERRACOTTA, Material.LIGHT_BLUE_TERRACOTTA, Material.LIGHT_GRAY_TERRACOTTA, Material.LIME_TERRACOTTA, Material.MAGENTA_TERRACOTTA,
Material.ORANGE_TERRACOTTA, Material.PINK_TERRACOTTA, Material.PURPLE_TERRACOTTA, Material.RED_TERRACOTTA, Material.RED_TERRACOTTA, Material.WHITE_TERRACOTTA,
Material.YELLOW_TERRACOTTA), Bukkit.createBlockData("minecraft:dead_bush")),
RED_TULIP(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:red_tulip")),
ORANGE_TULIP(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:orange_tulip")),
WHITE_TULIP(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:white_tulip")),
PINK_TULIP(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:pink_tulip")),
OXEYE_DAISY(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:oxeye_daisy")),
ALLIUM(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:allium")),
CORNFLOWER(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL), Bukkit.createBlockData("minecraft:cornflower")),
LILY_PAD(Sets.newHashSet(Material.WATER), Bukkit.createBlockData("minecraft:lily_pad")),
RED_MUSHROOM(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL, Material.DIRT, Material.STONE, Material.NETHERRACK, Material.MYCELIUM), Bukkit.createBlockData("minecraft:red_mushroom")),
BROWN_MUSHROOM(Sets.newHashSet(Material.GRASS_BLOCK, Material.PODZOL, Material.DIRT, Material.STONE, Material.NETHERRACK, Material.MYCELIUM), Bukkit.createBlockData("minecraft:brown_mushroom")),
;
private final List<BlockData> data = new GlueList<>();
private final Set<Material> spawns;
FloraType(Set<Material> validSpawns, BlockData... type) {
data.addAll(Arrays.asList(type));
this.spawns = validSpawns;
}
@Override
public List<Block> getValidSpawnsAt(Chunk chunk, int x, int z, Range check) {
List<Block> blocks = new GlueList<>();
for(int y : check) {
Block block = chunk.getBlock(x, y, z);
if(spawns.contains(block.getType()) && valid(block)) {
blocks.add(chunk.getBlock(x, y, z));
}
}
return blocks;
}
private boolean valid(Block block) {
for(int i = 1; i < data.size() + 1; i++) {
block = block.getRelative(BlockFace.UP);
if(!block.isEmpty()) return false;
}
return true;
}
@Override
public boolean plant(Location l) {
for(int i = 1; i < data.size() + 1; i++) {
l.clone().add(0, i, 0).getBlock().setBlockData(data.get(i - 1), false);
}
return true;
}
}

View File

@@ -0,0 +1,21 @@
package com.dfsek.terra.api.gaea.world;
import org.bukkit.block.data.BlockData;
public class Ore {
private final int contChance;
private final BlockData oreMaterial;
public Ore(BlockData oreMaterial, int contChance) {
this.contChance = contChance;
this.oreMaterial = oreMaterial;
}
public int getContChance() {
return contChance;
}
public BlockData getType() {
return oreMaterial;
}
}

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 net.jafama.FastMath;
import org.bukkit.World;
import org.bukkit.util.Vector;
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<Vector, 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 Vector((x << 4) + r.nextInt(16), r.nextInt(maxY - minY + 1) + minY, (z << 4) + r.nextInt(16)));
Vector 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, Vector 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 net.jafama.FastMath;
import org.bukkit.util.Vector;
import java.util.Random;
import java.util.function.BiConsumer;
public abstract class Worm {
private final Random r;
private final Vector origin;
private final Vector 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, Vector 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 Vector getOrigin() {
return origin;
}
public int getLength() {
return length;
}
public Vector 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 Vector origin;
private final int topCut;
private final int bottomCut;
private final int[] rad;
public WormPoint(Vector 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 Vector getOrigin() {
return origin;
}
public int getRadius(int index) {
return rad[index];
}
public void carve(int chunkX, int chunkZ, BiConsumer<Vector, 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++) {
Vector position = origin.clone().add(new Vector(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 Vector(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 Vector(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

@@ -1,4 +1,27 @@
package com.dfsek.terra.api.generic.generator;
import java.util.Collections;
import java.util.List;
public abstract class ChunkGenerator {
public boolean isParallelCapable() {
return false;
}
public boolean shouldGenerateCaves() {
return false;
}
public boolean shouldGenerateDecorations() {
return false;
}
public boolean shouldGenerateMobs() {
return false;
}
public boolean shouldGenerateStructures() {
return false;
}
public List<BlockPopulator> getDefaultPopulators() {
return Collections.emptyList();
}
}

View File

@@ -1,12 +1,12 @@
package com.dfsek.terra.async;
import com.dfsek.terra.Terra;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import org.bukkit.Location;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.generation.GenerationPhase;
import java.util.function.Consumer;

View File

@@ -1,6 +1,7 @@
package com.dfsek.terra.async;
import com.dfsek.terra.Terra;
import com.dfsek.terra.api.gaea.util.FastRandom;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import com.dfsek.terra.generation.items.TerraStructure;
@@ -11,7 +12,6 @@ import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.util.FastRandom;
import java.util.Random;
import java.util.function.Consumer;

View File

@@ -1,13 +1,13 @@
package com.dfsek.terra.biome;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.api.gaea.biome.NormalizationUtil;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.base.ConfigPackTemplate;
import com.dfsek.terra.image.ImageLoader;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
import org.polydev.gaea.biome.BiomeGrid;
import org.polydev.gaea.biome.NormalizationUtil;
import org.polydev.gaea.math.FastNoiseLite;
import java.util.Objects;

View File

@@ -1,16 +1,13 @@
package com.dfsek.terra.biome;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.biome.Decorator;
import com.dfsek.terra.api.gaea.biome.Generator;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.builder.GeneratorBuilder;
import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.generation.UserDefinedDecorator;
import org.bukkit.World;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.biome.Decorator;
import org.polydev.gaea.biome.Generator;
import org.polydev.gaea.structures.features.Feature;
import java.util.List;
/**
* Class representing a config-defined biome
@@ -54,15 +51,6 @@ public class UserDefinedBiome implements Biome {
return gen.build(0);
}
/**
* Gets a list of Structure Features to be applied to all structures in the biome.
*
* @return List&lt;Feature&gt; - The list of Features.
*/
@Override
public List<Feature> getStructureFeatures() {
return null;
}
/**
* Returns the Decorator instance containing information about the population in the biome.

View File

@@ -1,10 +1,10 @@
package com.dfsek.terra.biome.grid;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import org.bukkit.Location;
import org.bukkit.World;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.biome.BiomeGrid;
import org.polydev.gaea.generation.GenerationPhase;
/**
* BiomeGrid implementation that holds a single biome.

View File

@@ -1,14 +1,14 @@
package com.dfsek.terra.biome.grid;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.api.gaea.biome.NormalizationUtil;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.base.ConfigPackTemplate;
import com.dfsek.terra.image.ImageLoader;
import org.bukkit.Location;
import org.bukkit.World;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.biome.BiomeGrid;
import org.polydev.gaea.biome.NormalizationUtil;
import org.polydev.gaea.generation.GenerationPhase;
public class UserDefinedGrid extends BiomeGrid {
private final ImageLoader imageLoader;

View File

@@ -1,8 +1,8 @@
package com.dfsek.terra.biome.grid.master;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.biome.grid.UserDefinedGrid;
import org.bukkit.World;
import org.polydev.gaea.biome.BiomeGrid;
public abstract class TerraBiomeGrid extends BiomeGrid {
public TerraBiomeGrid(World w, double freq1, double freq2, int sizeX, int sizeZ) {

View File

@@ -1,5 +1,8 @@
package com.dfsek.terra.biome.grid.master;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.biome.BiomeGrid;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.api.generic.world.vector.Vector2;
import com.dfsek.terra.biome.BiomeZone;
import com.dfsek.terra.biome.UserDefinedBiome;
@@ -11,9 +14,6 @@ import com.dfsek.terra.config.base.ConfigPackTemplate;
import net.jafama.FastMath;
import org.bukkit.Location;
import org.bukkit.World;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.biome.BiomeGrid;
import org.polydev.gaea.generation.GenerationPhase;
public class TerraRadialBiomeGrid extends TerraBiomeGrid {
private static final int failNum = 0;

View File

@@ -1,5 +1,7 @@
package com.dfsek.terra.biome.grid.master;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.api.generic.world.vector.Vector2;
import com.dfsek.terra.biome.BiomeZone;
import com.dfsek.terra.biome.UserDefinedBiome;
@@ -10,8 +12,6 @@ import com.dfsek.terra.config.base.ConfigPack;
import com.dfsek.terra.config.base.ConfigPackTemplate;
import org.bukkit.Location;
import org.bukkit.World;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.generation.GenerationPhase;
public class TerraStandardBiomeGrid extends TerraBiomeGrid {
private static final int failNum = 0;

View File

@@ -1,7 +1,7 @@
package com.dfsek.terra.biome.palette;
import com.dfsek.terra.api.gaea.world.palette.Palette;
import org.bukkit.block.data.BlockData;
import org.polydev.gaea.world.palette.Palette;
public class PaletteHolder {
private final Palette<BlockData>[] palettes;

View File

@@ -1,9 +1,9 @@
package com.dfsek.terra.biome.palette;
import com.dfsek.terra.api.gaea.world.palette.Palette;
import com.dfsek.terra.util.PaletteUtil;
import net.jafama.FastMath;
import org.bukkit.block.data.BlockData;
import org.polydev.gaea.world.palette.Palette;
import java.util.Map;
import java.util.TreeMap;

View File

@@ -1,8 +1,8 @@
package com.dfsek.terra.biome.palette;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.math.ProbabilityCollection;
public class PaletteLayer {
private final ProbabilityCollection<BlockData> layer;

View File

@@ -1,6 +1,6 @@
package com.dfsek.terra.biome.palette;
import org.polydev.gaea.world.palette.Palette;
import com.dfsek.terra.api.gaea.world.palette.Palette;
public class SinglePalette<E> extends Palette<E> {
private final E item;

View File

@@ -1,7 +1,7 @@
package com.dfsek.terra.biome.postprocessing;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.generic.world.vector.Vector2;
import org.polydev.gaea.math.FastNoiseLite;
/**
* Offset a coordinate pair by an amount.

View File

@@ -1,7 +1,7 @@
package com.dfsek.terra.biome.postprocessing;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import net.jafama.FastMath;
import org.polydev.gaea.math.FastNoiseLite;
/**
* Class to hold noise function to determine erosion.

View File

@@ -1,16 +1,16 @@
package com.dfsek.terra.carving;
import com.dfsek.terra.Terra;
import com.dfsek.terra.api.gaea.biome.Biome;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.api.gaea.math.MathUtil;
import com.dfsek.terra.api.gaea.util.FastRandom;
import com.dfsek.terra.api.gaea.util.GlueList;
import com.dfsek.terra.api.gaea.world.carving.Worm;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.biome.grid.master.TerraBiomeGrid;
import org.bukkit.World;
import org.bukkit.util.Vector;
import org.polydev.gaea.biome.Biome;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.math.MathUtil;
import org.polydev.gaea.util.FastRandom;
import org.polydev.gaea.util.GlueList;
import org.polydev.gaea.world.carving.Worm;
import java.util.HashMap;
import java.util.List;

View File

@@ -1,9 +1,9 @@
package com.dfsek.terra.carving;
import com.dfsek.terra.api.gaea.math.ProbabilityCollection;
import com.dfsek.terra.util.MaterialSet;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.polydev.gaea.math.ProbabilityCollection;
import java.util.Map;
import java.util.TreeMap;

View File

@@ -1,5 +1,8 @@
package com.dfsek.terra.carving;
import com.dfsek.terra.api.gaea.math.FastNoiseLite;
import com.dfsek.terra.api.gaea.math.MathUtil;
import com.dfsek.terra.api.gaea.util.FastRandom;
import com.dfsek.terra.procgen.GridSpawn;
import com.dfsek.terra.procgen.voxel.DeformedSphere;
import com.dfsek.terra.procgen.voxel.Tube;
@@ -7,9 +10,6 @@ import com.dfsek.terra.procgen.voxel.VoxelGeometry;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.util.Vector;
import org.polydev.gaea.math.FastNoiseLite;
import org.polydev.gaea.math.MathUtil;
import org.polydev.gaea.util.FastRandom;
import java.util.Random;

View File

@@ -1,6 +1,11 @@
package com.dfsek.terra.carving;
import com.dfsek.terra.Terra;
import com.dfsek.terra.api.gaea.generation.GenerationPhase;
import com.dfsek.terra.api.gaea.math.Range;
import com.dfsek.terra.api.gaea.util.FastRandom;
import com.dfsek.terra.api.gaea.world.carving.Carver;
import com.dfsek.terra.api.gaea.world.carving.Worm;
import com.dfsek.terra.biome.UserDefinedBiome;
import com.dfsek.terra.config.templates.BiomeTemplate;
import com.dfsek.terra.config.templates.CarverTemplate;
@@ -8,11 +13,6 @@ import com.dfsek.terra.math.RandomFunction;
import net.jafama.FastMath;
import org.bukkit.World;
import org.bukkit.util.Vector;
import org.polydev.gaea.generation.GenerationPhase;
import org.polydev.gaea.math.Range;
import org.polydev.gaea.util.FastRandom;
import org.polydev.gaea.world.carving.Carver;
import org.polydev.gaea.world.carving.Worm;
import parsii.eval.Expression;
import parsii.eval.Parser;
import parsii.eval.Scope;

View File

@@ -1,18 +1,18 @@
package com.dfsek.terra.command;
import com.dfsek.terra.api.gaea.command.WorldCommand;
import com.dfsek.terra.generation.TerraChunkGenerator;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.command.WorldCommand;
import java.util.Collections;
import java.util.List;
public class FixChunkCommand extends WorldCommand {
public FixChunkCommand(org.polydev.gaea.command.Command parent) {
public FixChunkCommand(com.dfsek.terra.api.gaea.command.Command parent) {
super(parent);
}
@@ -28,7 +28,7 @@ public class FixChunkCommand extends WorldCommand {
}
@Override
public List<org.polydev.gaea.command.Command> getSubCommands() {
public List<com.dfsek.terra.api.gaea.command.Command> getSubCommands() {
return Collections.emptyList();
}

View File

@@ -1,17 +1,17 @@
package com.dfsek.terra.command;
import com.dfsek.terra.api.gaea.command.WorldCommand;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.polydev.gaea.command.WorldCommand;
import java.util.Collections;
import java.util.List;
public class OreCommand extends WorldCommand {
public OreCommand(org.polydev.gaea.command.Command parent) {
public OreCommand(com.dfsek.terra.api.gaea.command.Command parent) {
super(parent);
}
@@ -47,7 +47,7 @@ public class OreCommand extends WorldCommand {
}
@Override
public List<org.polydev.gaea.command.Command> getSubCommands() {
public List<com.dfsek.terra.api.gaea.command.Command> getSubCommands() {
return Collections.emptyList();
}

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