Push of current world generation code. Doesn't compile yet.

This commit is contained in:
Waterpicker 2017-12-10 03:22:19 -06:00
parent 48a003012a
commit 858874b66b
9 changed files with 506 additions and 0 deletions

View file

@ -9,6 +9,8 @@ import com.zixiken.dimdoors.shared.items.ModItems;
import com.zixiken.dimdoors.shared.RiftRegistry;
import com.zixiken.dimdoors.shared.SchematicHandler;
import com.zixiken.dimdoors.shared.util.DefaultSchematicGenerator;
import com.zixiken.dimdoors.shared.world.gateways.GatewayGenerator;
import lombok.Getter;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
@ -19,6 +21,7 @@ import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.*;
import net.minecraftforge.fml.common.registry.GameRegistry;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@ -29,6 +32,7 @@ public class DimDoors {
public static final String MODID = "dimdoors";
public static final String VERSION = "${version}";
@Getter private GatewayGenerator gatewayGenerator;
@SidedProxy(clientSide = "com.zixiken.dimdoors.client.DDProxyClient",
serverSide = "com.zixiken.dimdoors.server.DDProxyServer")
@ -45,6 +49,7 @@ public class DimDoors {
}
};
@Mod.EventHandler
public void onPreInitialization(FMLPreInitializationEvent event) {
proxy.onPreInitialization(event);
@ -54,6 +59,8 @@ public class DimDoors {
@Mod.EventHandler
public void onInitialization(FMLInitializationEvent event) {
proxy.onInitialization(event);
gatewayGenerator = new GatewayGenerator();
GameRegistry.registerWorldGenerator(gatewayGenerator, 0);
}
@Mod.EventHandler

View file

@ -11,6 +11,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.zixiken.dimdoors.shared.world.gateways.DimensionFilter;
import com.zixiken.dimdoors.shared.world.gateways.GatewayGenerator;
import lombok.Getter;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
@ -44,6 +46,18 @@ public class DDConfig {
@Getter private static boolean dangerousLimboMonolithsEnabled = false;
@Getter private static boolean monolithTeleportationEnabled = true;
@Getter private static int clusterGenerationChance;
@Getter private static int gatewayGenerationChance;
@Getter private static boolean limboEscapeEnabled;
@Getter private static boolean universalLimboEnabled;
@Getter private static DimensionFilter riftClusterDimensions;
@Getter private static DimensionFilter riftGatewayDimensions;
//Names of categories
private static final String CATEGORY_WORLD_GENERATION = "world generation";
private static int setConfigIntWithMaxAndMin(Configuration config, String category, String key, int defaultValue, String comment, int minValue, int maxValue) {
Property prop = config.get(category, key, defaultValue, comment, minValue, maxValue);
int value = prop.getInt(defaultValue);
@ -122,6 +136,35 @@ public class DDConfig {
publicPocketSize = setConfigIntWithMaxAndMin(config, "pocket_dimension", "publicPocketSize", publicPocketSize,
"Sets how deep and wide any public pocket can be. [min: 0, max: maxPocketSize, default: 2]", 0, maxPocketSize);
clusterGenerationChance = config.get(Configuration.CATEGORY_GENERAL, "Cluster Generation Chance", 2,
"Sets the chance (out of " + GatewayGenerator.MAX_CLUSTER_GENERATION_CHANCE + ") that a cluster of rifts will " +
"generate in a given chunk. The default chance is 2.").getInt();
gatewayGenerationChance = config.get(Configuration.CATEGORY_GENERAL, "Gateway Generation Chance", 15,
"Sets the chance (out of " + GatewayGenerator.MAX_GATEWAY_GENERATION_CHANCE + ") that a Rift Gateway will " +
"generate in a given chunk. The default chance is 15.").getInt();
//World Generation
config.addCustomCategoryComment(CATEGORY_WORLD_GENERATION,
"The following settings require lists of dimensions in a specific format. " +
"A list must consist of ranges separated by commas. A range may be a single number to indicate " +
"just one dimension or two numbers in the form \"X - Y\". Spaces are permitted " +
"but not required. Example: -100, -10 - -1, 20 - 30");
riftClusterDimensions = loadFilter(config, "Rift Cluster", "Rift Clusters");
riftGatewayDimensions = loadFilter(config, "Rift Gateway", "Rift Gateways");
limboEscapeEnabled = config.get(Configuration.CATEGORY_GENERAL, "Enable Limbo Escape", true,
"Sets whether players are teleported out of Limbo when walking over the Eternal Fabric that " +
"generates near the bottom of the dimension. If disabled, players could still leave through " +
"dungeons in Limbo or by dying (if Hardcore Limbo is disabled). The default value is true.").getBoolean(true);
universalLimboEnabled = config.get(Configuration.CATEGORY_GENERAL, "Enable Universal Limbo", false,
"Sets whether players are teleported to Limbo when they die in any dimension (except Limbo). " +
"Normally, players only go to Limbo if they die in a pocket dimension. This setting will not " +
"affect deaths in Limbo, which can be set with the Hardcore Limbo option. " +
"The default value is false.").getBoolean(false);
// Save config
config.save();
}
@ -142,4 +185,27 @@ public class DDConfig {
}
}
}
private static DimensionFilter loadFilter(Configuration config, String prefix, String description) {
boolean enableBlacklist = config.get(CATEGORY_WORLD_GENERATION, "Enable " + prefix + " Blacklist", true,
"Sets whether " + description + " will not generate in certain blacklisted dimensions. " +
"If set to false, then " + description + " will follow a whitelist instead.").getBoolean(true);
String whitelist = config.get(CATEGORY_WORLD_GENERATION, prefix + " Whitelist", "",
"A list of the only dimensions in which " + description + " may generate.").getString();
String blacklist = config.get(CATEGORY_WORLD_GENERATION, prefix + " Blacklist", "",
"A list of dimensions in which " + description + " may not generate.").getString();
try {
if (enableBlacklist) {
return DimensionFilter.parseBlacklist(blacklist);
} else {
return DimensionFilter.parseWhitelist(whitelist);
}
} catch (Exception inner) {
throw new RuntimeException("An error occurred while loading a whitelist or blacklist setting for " +
description + ". Please make sure that your configuration file is set up correctly.", inner);
}
}
}

View file

@ -0,0 +1,57 @@
package com.zixiken.dimdoors.shared.world.gateways;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
public abstract class BaseGateway {
public BaseGateway() { }
/**
* Generates the gateway centered on the given coordinates
* @param world - the world in which to generate the gateway
* @param x - the x-coordinate at which to center the gateway; usually where the door is placed
* @param y - the y-coordinate of the block on which the gateway may be built
* @param z - the z-coordinate at which to center the gateway; usually where the door is placed
*/
public abstract boolean generate(World world, int x, int y, int z);
/**
* Determines whether the specified biome is a valid biome in which to generate this gateway
* @param biome - the biome to be checked
* @return <code>true</code> true if the specified biome is a valid for generating this gateway, otherwise <code>false</code>
*/
protected boolean isBiomeValid(Biome biome) {
Biome[] biomes = this.getBiomes();
if (biomes != null) {
for (Biome b : biomes) {
if (b.equals(biome)) {
return true;
}
}
return false;
}
return true;
}
/**
* Determines whether the specified world and coordinates are a valid location for generating this gateway
* @param world - the world in which to generate the gateway
* @param x - the x-coordinate at which to center the gateway; usually where the door is placed
* @param y - the y-coordinate of the block on which the gateway may be built
* @param z - the z-coordinate at which to center the gateway; usually where the door is placed
* @return <code>true</code> if the location is valid, otherwise <code>false</code>
*/
public boolean isLocationValid(World world, int x, int y, int z)
{
return isBiomeValid(world.getBiome(new BlockPos(x,y,z)));
}
/**
* Gets the lowercase keywords to be used in checking whether a given biome is a valid location for this gateway
* @return an array of biome keywords to match against
*/
public Biome[] getBiomes() {
return null;
}
}

View file

@ -0,0 +1,74 @@
package com.zixiken.dimdoors.shared.world.gateways;
import com.zixiken.dimdoors.DimDoors;
import com.zixiken.dimdoors.shared.SchematicHandler;
import com.zixiken.dimdoors.shared.util.Schematic;
import com.zixiken.dimdoors.shared.util.SchematicConverter;
import net.minecraft.nbt.CompressedStreamTools;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public abstract class BaseSchematicGateway extends BaseGateway {
private Schematic schematic;
public BaseSchematicGateway(String name) {
String schematicJarDirectory = "/assets/dimdoors/gateways/";
//Initialising the possible locations/formats for the schematic file
InputStream oldVersionSchematicStream = DimDoors.class.getResourceAsStream(schematicJarDirectory + name + ".schematic"); //@todo also check for other schematics
//determine which location to load the schematic file from (and what format)
DataInputStream schematicDataStream = null;
boolean streamOpened = false;
if (oldVersionSchematicStream != null) {
schematicDataStream = new DataInputStream(oldVersionSchematicStream);
streamOpened = true;
} else {
DimDoors.warn(SchematicHandler.class, "Schematic '" + name + "' was not found in the jar or config directory, neither with the .schem extension, nor with the .schematic extension.");
}
NBTTagCompound schematicNBT;
this.schematic = null;
if (streamOpened) {
try {
schematicNBT = CompressedStreamTools.readCompressed(schematicDataStream);
schematic = SchematicConverter.loadOldDimDoorSchematicFromNBT(schematicNBT, name);
schematicDataStream.close();
} catch (IOException ex) {
Logger.getLogger(SchematicHandler.class.getName()).log(Level.SEVERE, "Schematic file for " + name + " could not be read as a valid schematic NBT file.", ex);
} finally {
try {
schematicDataStream.close();
} catch (IOException ex) {
Logger.getLogger(SchematicHandler.class.getName()).log(Level.SEVERE, "Error occured while closing schematicDataStream", ex);
}
}
}
}
@Override
public boolean generate(World world, int x, int y, int z) {
Schematic.place(schematic, world, x, y, z);
this.generateRandomBits(world, x, y, z);
return true;
}
/**
* Generates randomized portions of the gateway structure (e.g. rubble, foliage)
*
* @param world - the world in which to generate the gateway
* @param x - the x-coordinate at which to center the gateway; usually where the door is placed
* @param y - the y-coordinate of the block on which the gateway may be built
* @param z - the z-coordinate at which to center the gateway; usually where the door is placed
*/
protected void generateRandomBits(World world, int x, int y, int z) {
}
}

View file

@ -0,0 +1,75 @@
package com.zixiken.dimdoors.shared.world.gateways;
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
public class DimensionFilter {
private RangeSet<Integer> blacklist;
private DimensionFilter(RangeSet<Integer> blacklist) {
this.blacklist = blacklist;
}
public boolean isAccepted(int dimensionID) {
return !blacklist.contains(dimensionID);
}
public boolean isRejected(int dimensionID) {
return blacklist.contains(dimensionID);
}
private static RangeSet<Integer> parseRangeSet(String list) {
int index;
int start;
int end;
String startPart;
String endPart;
String[] intervals;
RangeSet<Integer> ranges = TreeRangeSet.create();
// Strip out all whitespace characters
list = list.replaceAll("\\s", "");
if (list.isEmpty()) {
return ranges;
}
intervals = list.split(",");
// Iterate over all the interval strings
for (String interval : intervals) {
// Check if the interval contains a minus sign after the first character
// That indicates that we're dealing with an interval and not a single number
if (interval.length() > 1) {
index = interval.indexOf("-", 1);
} else {
index = -1;
} try {
if (index >= 0) {
// Parse this as a range with two values as endpoints
startPart = interval.substring(0, index);
endPart = interval.substring(index + 1);
start = Integer.parseInt(startPart);
end = Integer.parseInt(endPart);
} else {
// Parse this as a single value
start = Integer.parseInt(interval);
end = start;
}
// Add the interval to the set of intervals
ranges.add( Range.closed(start, end) );
} catch (Exception e) {
throw new IllegalArgumentException("\"" + interval + "\" is not a valid value or range for dimension IDs");
}
}
return ranges;
}
public static DimensionFilter parseWhitelist(String list) {
return new DimensionFilter(parseRangeSet(list).complement());
}
public static DimensionFilter parseBlacklist(String list) {
return new DimensionFilter(parseRangeSet(list));
}
}

View file

@ -0,0 +1,138 @@
package com.zixiken.dimdoors.shared.world.gateways;
import com.zixiken.dimdoors.shared.DDConfig;
import com.zixiken.dimdoors.shared.PocketRegistry;
import com.zixiken.dimdoors.shared.blocks.ModBlocks;
import com.zixiken.dimdoors.shared.world.PocketProvider;
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.IChunkGenerator;
import net.minecraftforge.fml.common.IWorldGenerator;
import java.util.ArrayList;
import java.util.Random;
public class GatewayGenerator implements IWorldGenerator
{
public static final int MAX_GATEWAY_GENERATION_CHANCE = 10000;
public static final int MAX_CLUSTER_GENERATION_CHANCE = 10000;
private static final int CLUSTER_GROWTH_CHANCE = 80;
private static final int MAX_CLUSTER_GROWTH_CHANCE = 100;
private static final int MIN_RIFT_Y = 4;
private static final int MAX_RIFT_Y = 240;
private static final int CHUNK_LENGTH = 16;
private static final int GATEWAY_RADIUS = 4;
private static final int MAX_GATEWAY_GENERATION_ATTEMPTS = 10;
private static final int OVERWORLD_DIMENSION_ID = 0;
private static final int NETHER_DIMENSION_ID = -1;
private static final int END_DIMENSION_ID = 1;
private ArrayList<BaseGateway> gateways;
private BaseGateway defaultGateway;
public GatewayGenerator() {
this.initialize();
}
private void initialize() {
gateways = new ArrayList<>();
defaultGateway = new GatewayTwoPillars();
// Add gateways here
gateways.add(new GatewaySandstonePillars());
gateways.add(new GatewayLimbo());
}
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkGenerator chunkGenerator, IChunkProvider chunkProvider) {
// Don't generate rifts or gateways if the current world is a pocket dimension or the world is remote.
// Also don't generate anything in the Nether, The End, or in Witchery's Spirit World.
// We only match against Spirit World using hashing to speed up the process a little (hopefully).
int dimensionID = world.provider.getDimension();
if (world.isRemote || (world.provider instanceof PocketProvider) || (dimensionID == END_DIMENSION_ID) || (dimensionID == NETHER_DIMENSION_ID)) {
return;
}
int x, y, z;
int attempts;
boolean valid;
// Check if we're allowed to generate rift clusters in this dimension.
// If so, randomly decide whether to one.
if (DDConfig.getRiftClusterDimensions().isAccepted(dimensionID) && random.nextInt(MAX_CLUSTER_GENERATION_CHANCE) < DDConfig.getClusterGenerationChance()) {
do {
//Pick a random point on the surface of the chunk
x = chunkX * CHUNK_LENGTH + random.nextInt(CHUNK_LENGTH);
z = chunkZ * CHUNK_LENGTH + random.nextInt(CHUNK_LENGTH);
y = world.getHeight(x, z);
//If the point is within the acceptable altitude range, the block above is empty, and we're
//not building on bedrock, then generate a rift there
if (y >= MIN_RIFT_Y && y <= MAX_RIFT_Y && world.isAirBlock(new BlockPos(x, y + 1, z)) &&
world.getBlockState(new BlockPos(x, y, z)).getBlock() != Blocks.BEDROCK && //<-- Stops Nether roof spawning. DO NOT REMOVE!
world.getBlockState(new BlockPos(x, y - 1, z)).getBlock() != Blocks.BEDROCK &&
world.getBlockState(new BlockPos(x, y - 2, z)).getBlock() != Blocks.BEDROCK) {
//Create a link. If this is not the first time, create a child link and connect it to the first link.
world.setBlockState(new BlockPos(x,y,z), ModBlocks.RIFT.getDefaultState());
}
}
//Randomly decide whether to repeat the process and add another rift to the cluster
while (random.nextInt(MAX_CLUSTER_GROWTH_CHANCE) < CLUSTER_GROWTH_CHANCE);
}
// Check if we can place a Rift Gateway in this dimension, then randomly decide whether to place one.
// This only happens if a rift cluster was NOT generated.
else if (DDConfig.getRiftGatewayDimensions().isAccepted(dimensionID) && random.nextInt(MAX_GATEWAY_GENERATION_CHANCE) < DDConfig.getGatewayGenerationChance()) {
valid = false;
x = y = z = 0; //Stop the compiler from freaking out
//Check locations for the gateway until we are satisfied or run out of attempts.
for (attempts = 0; attempts < MAX_GATEWAY_GENERATION_ATTEMPTS && !valid; attempts++) {
//Pick a random point on the surface of the chunk and check its materials
x = chunkX * CHUNK_LENGTH + random.nextInt(CHUNK_LENGTH);
z = chunkZ * CHUNK_LENGTH + random.nextInt(CHUNK_LENGTH);
y = world.getHeight(x, z);
valid = checkGatewayLocation(world, new BlockPos(x, y, z));
}
// Build the gateway if we found a valid location
if (valid) {
ArrayList<BaseGateway> validGateways = new ArrayList<BaseGateway>();
for (BaseGateway gateway : gateways) {
if (gateway.isLocationValid(world, x, y, z)) {
validGateways.add(gateway);
}
}
// Add the default gateway if the rest were rejected
if (validGateways.isEmpty()) {
validGateways.add(defaultGateway);
}
// Randomly select a gateway from the pool of viable gateways
validGateways.get(random.nextInt(validGateways.size())).generate(world, x, y - 1, z);
}
}
}
private static boolean checkGatewayLocation(World world, BlockPos pos) {
//Check if the point is within the acceptable altitude range, the block above that point is empty,
//and the block two levels down is opaque and has a reasonable material. Plus that we're not building
//on top of bedrock.
return (pos.getY() >= MIN_RIFT_Y && pos.getY() <= MAX_RIFT_Y &&
world.isAirBlock(pos.up()) &&
world.getBlockState(pos).getBlock() != Blocks.BEDROCK && //<-- Stops Nether roof spawning. DO NOT REMOVE!
world.getBlockState(pos.down()) != Blocks.BEDROCK &&
checkFoundationMaterial(world, pos.down()));
}
private static boolean checkFoundationMaterial(World world, BlockPos pos) {
//We check the material and opacity to prevent generating gateways on top of trees or houses,
//or on top of strange things like tall grass, water, slabs, or torches.
//We also want to avoid generating things on top of the Nether's bedrock!
Material material = world.getBlockState(pos).getMaterial();
return (material != Material.LEAVES && material != Material.WOOD && material != Material.GOURD
&& world.isBlockNormalCube(pos, false) && world.getBlockState(pos).getBlock() != Blocks.BEDROCK);
}
}

View file

@ -0,0 +1,36 @@
package com.zixiken.dimdoors.shared.world.gateways;
import com.zixiken.dimdoors.shared.blocks.BlockFabric;
import com.zixiken.dimdoors.shared.blocks.ModBlocks;
import com.zixiken.dimdoors.shared.world.limbodimension.WorldProviderLimbo;
import net.minecraft.block.state.IBlockState;
import net.minecraft.item.ItemDoor;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class GatewayLimbo extends BaseGateway {
@Override
public boolean generate(World world, int x, int y, int z)
{
IBlockState limbo = ModBlocks.FABRIC.getDefaultState().withProperty(BlockFabric.TYPE, BlockFabric.EnumType.UNRAVELED);
// Build the gateway out of Unraveled Fabric. Since nearly all the blocks in Limbo are of
// that type, there is no point replacing the ground.
world.setBlockState(new BlockPos(x, y + 3, z + 1), limbo);
world.setBlockState(new BlockPos(x, y + 3, z - 1), limbo);
// Build the columns around the door
world.setBlockState(new BlockPos(x, y + 2, z - 1), limbo);
world.setBlockState(new BlockPos(x, y + 2, z + 1), limbo);
world.setBlockState(new BlockPos(x, y + 1, z - 1), limbo);
world.setBlockState(new BlockPos(x, y + 1, z + 1), limbo);
ItemDoor.placeDoor(world, new BlockPos(x, y + 1, z), EnumFacing.getHorizontal(0), ModBlocks.TRANSIENT_DIMENSIONAL_DOOR, false);
return true;
}
@Override
public boolean isLocationValid(World world, int x, int y, int z) {
return world.provider instanceof WorldProviderLimbo;
}
}

View file

@ -0,0 +1,14 @@
package com.zixiken.dimdoors.shared.world.gateways;
import net.minecraft.init.Biomes;
import net.minecraft.world.biome.Biome;
public class GatewaySandstonePillars extends BaseSchematicGateway {
public GatewaySandstonePillars() {
super("sandstonePillars");
}
public Biome[] getBiomes() {
return new Biome[] {Biomes.DESERT, Biomes.DESERT_HILLS, Biomes.MUTATED_DESERT};
}
}

View file

@ -0,0 +1,39 @@
package com.zixiken.dimdoors.shared.world.gateways;
import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class GatewayTwoPillars extends BaseSchematicGateway {
private static final int GATEWAY_RADIUS = 4;
public GatewayTwoPillars() {
super("twoPillars");
}
@Override
protected void generateRandomBits(World world, int x, int y, int z) {
IBlockState state = Blocks.STONEBRICK.getDefaultState();
//Replace some of the ground around the gateway with bricks
for (int xc = -GATEWAY_RADIUS; xc <= GATEWAY_RADIUS; xc++) {
for (int zc = -GATEWAY_RADIUS; zc <= GATEWAY_RADIUS; zc++) {
//Check that the block is supported by an opaque block.
//This prevents us from building over a cliff, on the peak of a mountain,
//or the surface of the ocean or a frozen lake.
if (world.getBlockState(new BlockPos(x + xc, y - 1, z + zc)).getMaterial().isSolid()) {
//Randomly choose whether to place bricks or not. The math is designed so that the
//chances of placing a block decrease as we get farther from the gateway's center.
if (Math.abs(xc) + Math.abs(zc) < world.rand.nextInt(2) + 3) {
//Place Stone Bricks
world.setBlockState(new BlockPos(x + xc, y, z + zc), state);
} else if (Math.abs(xc) + Math.abs(zc) < world.rand.nextInt(3) + 3) {
//Place Cracked Stone Bricks
world.setBlockState(new BlockPos(x + xc, y, z + zc), state); //TODO find out what the data for cracked Stonebrick is.
}
}
}
}
}
}