Updated celestial objects configuration to use XML

Added explicit render options for sky objects
Added explicit gravity setting parameters
Added /wreload command to reload the mod configuration
Improved handling of asteroid fields world generation
Fixed XML child elements being read multiple times
Removed partial blocks from gravity pull (ladder will no longer push you down)
This commit is contained in:
LemADEC 2017-03-28 21:33:34 +02:00
parent 8ccce42471
commit 50369c715e
36 changed files with 1471 additions and 777 deletions

View file

@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
/**
@ -249,6 +250,24 @@ public class Commons {
return Math.min(max, Math.max(value, min));
}
// clamping while keeping the sign
public static float clampMantisse(final float min, final float max, final float value) {
return Math.min(max, Math.max(Math.abs(value), min)) * Math.signum(value == 0.0F ? 1.0F : value);
}
// clamping while keeping the sign
public static double clampMantisse(final double min, final double max, final double value) {
return Math.min(max, Math.max(Math.abs(value), min)) * Math.signum(value == 0.0D ? 1.0D : value);
}
public static int randomRange(Random random, final int min, final int max) {
return min + ((max - min > 0) ? random.nextInt(max - min + 1) : 0);
}
public static double randomRange(Random random, final double min, final double max) {
return min + ((max - min > 0) ? random.nextDouble() * (max - min) : 0);
}
// configurable interpolation

View file

@ -1,12 +1,15 @@
package cr0s.warpdrive;
import cr0s.warpdrive.config.Dictionary;
import cr0s.warpdrive.data.CelestialObject;
import cr0s.warpdrive.data.StarMapRegistry;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.MathHelper;
public class GravityManager {
@ -25,9 +28,17 @@ public class GravityManager {
public static double getGravityForEntity(Entity entity) {
final double gravity = StarMapRegistry.getGravity(entity);
if (gravity < 1.0D) {
if (gravity == CelestialObject.GRAVITY_NONE) {
return SPACE_VOID_GRAVITY;
}
if (gravity == CelestialObject.GRAVITY_NORMAL) {
return OVERWORLD_ENTITY_GRAVITY;
}
if (gravity == CelestialObject.GRAVITY_LEGACY_SPACE || gravity == CelestialObject.GRAVITY_LEGACY_HYPERSPACE) {
// Is entity in hyper-space?
boolean inHyperspace = gravity > 0.4D;
final boolean inHyperspace = gravity == CelestialObject.GRAVITY_LEGACY_HYPERSPACE;
if (isEntityInGraviField(entity)) {
if (inHyperspace) {
@ -36,7 +47,7 @@ public class GravityManager {
return SPACE_FIELD_ENTITY_GRAVITY;
}
} else {
double jitter = inHyperspace ? (entity.worldObj.rand.nextDouble() - 0.5D) * 2.0D * HYPERSPACE_VOID_ENTITY_JITTER : 0.0D;
final double jitter = inHyperspace ? (entity.worldObj.rand.nextDouble() - 0.5D) * 2.0D * HYPERSPACE_VOID_ENTITY_JITTER : 0.0D;
if (entity instanceof EntityPlayer) {
EntityPlayer player = (EntityPlayer) entity;
@ -59,45 +70,65 @@ public class GravityManager {
}
}
return OVERWORLD_ENTITY_GRAVITY;
return gravity * OVERWORLD_ENTITY_GRAVITY;
}
public static double getItemGravity(EntityItem entity) {
final double gravity = StarMapRegistry.getGravity(entity);
if (gravity < 1.0D) {
if (gravity == CelestialObject.GRAVITY_NONE) {
return SPACE_VOID_GRAVITY;
}
if (gravity == CelestialObject.GRAVITY_NORMAL) {
return OVERWORLD_ITEM_GRAVITY;
}
if (gravity == CelestialObject.GRAVITY_LEGACY_SPACE || gravity == CelestialObject.GRAVITY_LEGACY_HYPERSPACE) {
if (isEntityInGraviField(entity)) {
return SPACE_FIELD_ITEM_GRAVITY;
} else {
return SPACE_VOID_GRAVITY;
}
} else {
return OVERWORLD_ITEM_GRAVITY; // On Earth
}
}
return gravity * OVERWORLD_ITEM_GRAVITY;
}
public static double getItemGravity2(EntityItem entity) {
final double gravity = StarMapRegistry.getGravity(entity);
if (gravity < 1.0D) {
if (gravity == CelestialObject.GRAVITY_NONE) {
return SPACE_VOID_GRAVITY;
}
if (gravity == CelestialObject.GRAVITY_NORMAL) {
return OVERWORLD_ITEM_GRAVITY;
}
if (gravity == CelestialObject.GRAVITY_LEGACY_SPACE || gravity == CelestialObject.GRAVITY_LEGACY_HYPERSPACE) {
if (isEntityInGraviField(entity)) {
return SPACE_FIELD_ITEM_GRAVITY2;
} else {
return SPACE_VOID_GRAVITY;
}
} else {
return OVERWORLD_ITEM_GRAVITY2;
}
return gravity * OVERWORLD_ITEM_GRAVITY2;
}
public static boolean isEntityInGraviField(Entity entity) {
int y = MathHelper.floor_double(entity.posY);
int x = MathHelper.floor_double(entity.posX);
int z = MathHelper.floor_double(entity.posZ);
final int y = MathHelper.floor_double(entity.posY);
final int x = MathHelper.floor_double(entity.posX);
final int z = MathHelper.floor_double(entity.posZ);
final int CHECK_DISTANCE = 20;
// Search non-air blocks under player
for (int ny = y; ny > (y - CHECK_DISTANCE); ny--) {
if (!entity.worldObj.isAirBlock(x, ny, z)) {
return true;
final Block block = entity.worldObj.getBlock(x, ny, z);
final AxisAlignedBB axisAlignedBB = block.getCollisionBoundingBoxFromPool(entity.worldObj, x, ny, z);
if (axisAlignedBB != null && axisAlignedBB.getAverageEdgeLength() > 0.90D) {
return true;
}
}
}

View file

@ -84,7 +84,9 @@ import cr0s.warpdrive.command.CommandEntity;
import cr0s.warpdrive.command.CommandGenerate;
import cr0s.warpdrive.command.CommandInvisible;
import cr0s.warpdrive.command.CommandJumpgates;
import cr0s.warpdrive.command.CommandReload;
import cr0s.warpdrive.command.CommandSpace;
import cr0s.warpdrive.config.CelestialObjectManager;
import cr0s.warpdrive.config.RecipeParticleShapedOre;
import cr0s.warpdrive.config.RecipeTuningDriver;
import cr0s.warpdrive.config.Recipes;
@ -641,8 +643,8 @@ public class WarpDrive implements LoadingCallback {
DimensionManager.registerProviderType(WarpDriveConfig.G_HYPERSPACE_PROVIDER_ID, HyperSpaceWorldProvider.class, true);
// only create dimensions if we own them
for (CelestialObject celestialObject : WarpDriveConfig.celestialObjects) {
if (celestialObject.isWarpDrive) {
for (CelestialObject celestialObject : CelestialObjectManager.celestialObjects) {
if (!celestialObject.isVirtual && celestialObject.isProvidedByWarpDrive) {
if (celestialObject.isSpace()) {
DimensionManager.registerDimension(celestialObject.dimensionId, WarpDriveConfig.G_SPACE_PROVIDER_ID);
} else if (celestialObject.isHyperspace()) {
@ -662,8 +664,8 @@ public class WarpDrive implements LoadingCallback {
@EventHandler
public void onFMLPostInitialization(FMLPostInitializationEvent event) {
// load all owned dimensions at boot
for (CelestialObject celestialObject : WarpDriveConfig.celestialObjects) {
if (celestialObject.isWarpDrive) {
for (CelestialObject celestialObject : CelestialObjectManager.celestialObjects) {
if (celestialObject.isProvidedByWarpDrive) {
DimensionManager.getWorld(celestialObject.dimensionId);
}
}
@ -713,6 +715,7 @@ public class WarpDrive implements LoadingCallback {
event.registerServerCommand(new CommandJumpgates());
event.registerServerCommand(new CommandDebug());
event.registerServerCommand(new CommandEntity());
event.registerServerCommand(new CommandReload());
}
private Ticket registerChunkLoadTE(TileEntityAbstractChunkLoading tileEntity, boolean refreshLoading) {

View file

@ -0,0 +1,5 @@
package cr0s.warpdrive.api;
public interface IStringSerializable {
String getName();
}

View file

@ -5,7 +5,6 @@ import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.structures.AbstractStructure;
import cr0s.warpdrive.config.structures.StructureManager;
import cr0s.warpdrive.world.JumpgateGenerator;
import cr0s.warpdrive.world.SpaceWorldGenerator;
import cr0s.warpdrive.world.WorldGenSmallShip;
import cr0s.warpdrive.world.WorldGenStation;
@ -80,11 +79,10 @@ public class CommandGenerate extends CommandBase {
generateStructure(commandSender, StructureManager.GROUP_ASTEROIDS, name, world, x, y - 10, z);
break;
case "astfield":
WarpDrive.logger.info("/generate: generating asteroid field at " + x + ", " + y + ", " + z);
SpaceWorldGenerator.generateAsteroidField(world, x, y, z);
generateStructure(commandSender, StructureManager.GROUP_ASTEROIDS_FIELDS, name, world, x, y, z);
break;
case "gascloud":
generateStructure(commandSender, StructureManager.GROUP_GASCLOUDS, name, world, x, y, z);
generateStructure(commandSender, StructureManager.GROUP_GAS_CLOUDS, name, world, x, y, z);
break;
case "moon":
generateStructure(commandSender, StructureManager.GROUP_MOONS, name, world, x, y - 16, z);

View file

@ -0,0 +1,32 @@
package cr0s.warpdrive.command;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.config.WarpDriveConfig;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
public class CommandReload extends CommandBase {
@Override
public int getRequiredPermissionLevel() {
return 2;
}
@Override
public String getCommandName() {
return "wreload";
}
@Override
public void processCommand(ICommandSender sender, String[] params) {
if (sender == null) { return; }
WarpDriveConfig.reload();
Commons.addChatMessage(sender, "WarpDrive configuration has been reloaded. Use at your own risk!");
}
@Override
public String getCommandUsage(ICommandSender icommandsender) {
return "/wreload";
}
}

View file

@ -90,11 +90,22 @@ public class CommandSpace extends CommandBase {
int newZ = MathHelper.floor_double(entityPlayerMP.posZ);
if (targetDimensionId == Integer.MAX_VALUE) {
CelestialObject celestialObject = StarMapRegistry.getCelestialObject(entityPlayerMP.worldObj.provider.dimensionId, (int) entityPlayerMP.posX, (int) entityPlayerMP.posZ);
if (celestialObject == null) {
Commons.addChatMessage(sender,
String.format("/space: player %s is in unknown dimension %d. Try specifying an explicit target dimension instead.",
entityPlayerMP.getCommandSenderName(), entityPlayerMP.worldObj.provider.dimensionId));
return;
}
if (celestialObject.isSpace() || celestialObject.isHyperspace()) {
// in space or hyperspace => move to closest child
celestialObject = StarMapRegistry.getClosestChildCelestialObject(entityPlayerMP.worldObj.provider.dimensionId, (int) entityPlayerMP.posX, (int) entityPlayerMP.posZ);
if (celestialObject == null) {
targetDimensionId = 0;
} else if (celestialObject.isVirtual) {
Commons.addChatMessage(sender,
String.format("/space: player %s closest celestial object is virtual (%s). Try specifying an explicit target dimension instead.",
entityPlayerMP.getCommandSenderName(), celestialObject.getFullName()));
return;
} else {
targetDimensionId = celestialObject.dimensionId;
VectorI vEntry = celestialObject.getEntryOffset();

View file

@ -0,0 +1,116 @@
package cr0s.warpdrive.config;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.data.CelestialObject;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import net.minecraft.util.AxisAlignedBB;
public class CelestialObjectManager extends XmlFileManager {
private static CelestialObjectManager INSTANCE = new CelestialObjectManager();
private static HashMap<String, HashMap<String, CelestialObject>> celestialObjectsByGroup = new HashMap<>();
public static CelestialObject[] celestialObjects = null;
public static void clearForReload() {
// create a new object instead of clearing, in case another thread is iterating through it
celestialObjectsByGroup = new HashMap<>();
}
public static void load(File dir) {
INSTANCE.load(dir, "celestialObjects", "celestialObject");
// optimize execution speed by flattening the data structure
int count = 0;
for(HashMap<String, CelestialObject> mapCelestialObjectByName : celestialObjectsByGroup.values()) {
count += mapCelestialObjectByName.size();
}
celestialObjects = new CelestialObject[count];
int index = 0;
for(HashMap<String, CelestialObject> mapCelestialObjectByName : celestialObjectsByGroup.values()) {
for (CelestialObject celestialObject : mapCelestialObjectByName.values()) {
celestialObjects[index++] = celestialObject;
celestialObject.resolveParent();
}
}
// check overlapping regions
for (CelestialObject celestialObject1 : celestialObjects) {
for (CelestialObject celestialObject2 : celestialObjects) {
if (celestialObject1 == celestialObject2) {
continue;
}
// are they overlapping in a common parent dimension?
if (!celestialObject1.isHyperspace() && !celestialObject2.isHyperspace() && celestialObject1.parentDimensionId == celestialObject2.parentDimensionId) {
AxisAlignedBB areaInParent1 = celestialObject1.getAreaInParent();
AxisAlignedBB areaInParent2 = celestialObject2.getAreaInParent();
if (areaInParent1.intersectsWith(areaInParent2)) {
WarpDrive.logger.error("Overlapping parent areas detected " + celestialObject1.parentDimensionId);
WarpDrive.logger.error("Celestial object 1 is " + celestialObject1 + " with area " + areaInParent1);
WarpDrive.logger.error("Celestial object 2 is " + celestialObject2 + " with area " + areaInParent2);
throw new RuntimeException(String.format(
"Invalid celestial objects definition:\n %s\nand\n %s\nare overlapping each others. Update your configuration to fix it.",
celestialObject1.toString(), celestialObject2.toString()));
}
}
// are they in the same dimension?
if (!celestialObject1.isVirtual && !celestialObject2.isVirtual && celestialObject1.dimensionId == celestialObject2.dimensionId) {
AxisAlignedBB areaToReachParent1 = celestialObject1.getAreaToReachParent();
AxisAlignedBB areaToReachParent2 = celestialObject2.getAreaToReachParent();
if (areaToReachParent1.intersectsWith(areaToReachParent2)) {
WarpDrive.logger.error("Overlapping areas detected in dimension " + celestialObject1.dimensionId);
WarpDrive.logger.error("Celestial object 1 is " + celestialObject1 + " with area " + areaToReachParent1);
WarpDrive.logger.error("Celestial object 2 is " + celestialObject2 + " with area " + areaToReachParent2);
throw new RuntimeException(String.format(
"Invalid celestial objects definition:\n %s\nand\n %s\nare overlapping each others. Update your configuration to fix it.",
celestialObject1.toString(), celestialObject2.toString()));
}
}
}
}
// We're not checking invalid dimension id, so they can be pre-allocated (see MystCraft)
}
@Override
protected void parseRootElement(final String location, final Element elementCelestialObject) throws InvalidXmlException, SAXException, IOException {
parseCelestiaObjectElement(location, elementCelestialObject, "", "");
}
protected void parseCelestiaObjectElement(final String location, final Element elementCelestialObject, final String groupParent, final String nameParent) throws InvalidXmlException, SAXException, IOException {
CelestialObject celestialObjectRead = new CelestialObject(location, groupParent, nameParent, elementCelestialObject);
HashMap<String, CelestialObject> celestialObjectByName = celestialObjectsByGroup.computeIfAbsent(celestialObjectRead.group, k -> new HashMap<>());
CelestialObject celestialObjectExisting = celestialObjectByName.get(celestialObjectRead.name);
if (celestialObjectExisting == null) {
celestialObjectByName.put(celestialObjectRead.name, celestialObjectRead);
} else {
WarpDrive.logger.warn(String.format("Celestial object %s is already defined, keeping original definition", celestialObjectRead.getFullName()));
}
// look for optional child element(s)
List<Element> listChildren = XmlFileManager.getChildrenElementByTagName(elementCelestialObject, "celestialObject");
if (!listChildren.isEmpty()) {
for (int indexElement = 0; indexElement < listChildren.size(); indexElement++) {
Element elementChild = listChildren.get(indexElement);
String locationChild = String.format("%s Celestial object %s > child %d/%d", location, celestialObjectRead.getFullName(), indexElement + 1, listChildren.size());
parseCelestiaObjectElement(locationChild, elementChild, celestialObjectRead.group, celestialObjectRead.name);
}
}
}
public static CelestialObject get(final String group, final String name) {
HashMap<String, CelestialObject> celestialObjectByName = celestialObjectsByGroup.get(group);
if (celestialObjectByName == null) {
return null;
}
return celestialObjectByName.get(name);
}
}

View file

@ -1,13 +1,9 @@
package cr0s.warpdrive.config;
import org.w3c.dom.Document;
import cr0s.warpdrive.api.IStringSerializable;
import org.w3c.dom.Element;
public interface IXmlRepresentable {
String getName();
public interface IXmlRepresentable extends IStringSerializable {
// Load the XML element, return true if successful
boolean loadFromXmlElement(Element element) throws InvalidXmlException;
void saveToXmlElement(Element element, Document document) throws InvalidXmlException;
}

View file

@ -1,8 +1,9 @@
package cr0s.warpdrive.config;
import cr0s.warpdrive.WarpDrive;
import org.w3c.dom.Element;
import cr0s.warpdrive.api.IStringSerializable;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.NavigableMap;
@ -16,7 +17,7 @@ import java.util.TreeMap;
*
* @param <E>
**/
public class RandomCollection<E extends IXmlRepresentable> {
public class RandomCollection<E extends IStringSerializable> {
private final NavigableMap<Integer, E> weightMap = new TreeMap<>();
private int totalWeight = 0;
private final NavigableMap<Double, E> ratioMap = new TreeMap<>();
@ -141,54 +142,48 @@ public class RandomCollection<E extends IXmlRepresentable> {
}
/**
* Loads object from given XML element and parses configurations for weighted pick.
* Add an object for weighted pick.
*
* @param object
* Object to load into
* @param element
* @param stringRatio
* Element of an XML file
* @throws InvalidXmlException
* @throws InvalidParameterException
**/
public void loadFromXML(E object, Element element) throws InvalidXmlException {
if (!object.loadFromXmlElement(element)) {// skip invalid entries
return;
}
public void add(E object, final String stringRatio, final String stringWeight) throws InvalidParameterException {
// detect and handle loading of an existing object
E existing = getNamedEntry(object.getName());
if (existing != null) {
if (existing.equals(object)) {
// all good, nothing to do
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Object already exists in collection, skipping " + object.getName());
}
return;
} else {
throw new InvalidXmlException("Invalid merge of different objects with the same name " + object.getName()
throw new InvalidParameterException("Invalid merge of different objects with the same name " + object.getName()
+ "\nnew entry is " + object
+ "\nwhile existing entry is " + existing + "");
}
}
// ratio takes priority over weight
String stringRatio = element.getAttribute("ratio");
if (!stringRatio.isEmpty()) {
if (stringRatio != null && !stringRatio.isEmpty()) {
double ratio;
try {
ratio = Double.parseDouble(stringRatio);
} catch (NumberFormatException exceptionRatio) {
throw new InvalidXmlException("Ratio must be double!");
throw new InvalidParameterException("Ratio must be double!");
}
addRatio(ratio, object);
} else { // defaults to weight=1
int weight = 1;
String stringWeight = element.getAttribute("weight");
if (!stringWeight.isEmpty()) {
if (stringWeight != null && !stringWeight.isEmpty()) {
try {
weight = Integer.parseInt(stringWeight);
} catch (NumberFormatException exceptionWeight) {
throw new InvalidXmlException("Weight must be an integer!");
throw new InvalidParameterException("Weight must be an integer!");
}
weight = Math.max(1, weight);
}

View file

@ -31,8 +31,6 @@ import cr0s.warpdrive.compat.CompatThermalExpansion;
import cr0s.warpdrive.compat.CompatWarpDrive;
import cr0s.warpdrive.config.filler.FillerManager;
import cr0s.warpdrive.config.structures.StructureManager;
import cr0s.warpdrive.config.structures.StructureReference;
import cr0s.warpdrive.data.CelestialObject;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
@ -56,19 +54,25 @@ import net.minecraft.item.ItemStack;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraftforge.common.config.ConfigCategory;
import net.minecraftforge.common.config.Configuration;
public class WarpDriveConfig {
private static final boolean unused = false; // TODO
private static String stringConfigDirectory;
private static File configDirectory;
private static DocumentBuilder xmlDocumentBuilder;
private static final String[] defaultXMLfilenames = {
// fillers
"filler-default.xml", "filler-netherores.xml", "filler-undergroundbiomes.xml",
// structures
"structures-default.xml", "structures-netherores.xml",
private static final String[] defaultXML_fillers = {
"filler-default.xml",
"filler-netherores.xml",
"filler-undergroundbiomes.xml",
};
private static final String[] defaultXML_structures = {
"structures-default.xml",
"structures-netherores.xml",
};
private static final String[] defaultXML_celestialObjects = {
"celestialObjects-default.xml"
};
/*
@ -133,7 +137,7 @@ public class WarpDriveConfig {
public static boolean LOGGING_LUA = false;
public static boolean LOGGING_RADAR = false;
public static boolean LOGGING_BREATHING = false;
public static boolean LOGGING_WORLDGEN = false;
public static boolean LOGGING_WORLD_GENERATION = false;
public static boolean LOGGING_PROFILING = true;
public static boolean LOGGING_DICTIONARY = false;
public static boolean LOGGING_STARMAP = false;
@ -141,17 +145,18 @@ public class WarpDriveConfig {
public static boolean LOGGING_FORCEFIELD = false;
public static boolean LOGGING_FORCEFIELD_REGISTRY = false;
public static boolean LOGGING_ACCELERATOR = false;
public static boolean LOGGING_XML_PREPROCESSOR = false;
public static boolean LOGGING_RENDERING = false;
// Starmap
public static CelestialObject[] celestialObjects = null;
public static int STARMAP_REGISTRY_UPDATE_INTERVAL_SECONDS = 10;
public static boolean STARMAP_ALLOW_OVERLAPPING_CELESTIAL_OBJECTS = false;
// Space generator
public static int SPACE_GENERATOR_Y_MIN_CENTER = 55;
public static int SPACE_GENERATOR_Y_MAX_CENTER = 128;
public static int SPACE_GENERATOR_Y_MIN_BORDER = 5;
public static int SPACE_GENERATOR_Y_MAX_BORDER = 200;
public static RandomCollection<StructureReference> SPACE_GENERATOR_STRUCTURES_CHANCES = null;
// Ship
public static int SHIP_MAX_ENERGY_STORED = 100000000;
@ -354,7 +359,15 @@ public class WarpDriveConfig {
return new ItemStack(Blocks.fire);
}
public static void reload() {
CelestialObjectManager.clearForReload();
onFMLpreInitialization(stringConfigDirectory);
onFMLPostInitialization();
}
public static void onFMLpreInitialization(final String stringConfigDirectory) {
WarpDriveConfig.stringConfigDirectory = stringConfigDirectory;
// create mod folder
configDirectory = new File(stringConfigDirectory, WarpDrive.MODID);
//noinspection ResultOfMethodCallIgnored
@ -363,8 +376,17 @@ public class WarpDriveConfig {
throw new RuntimeException("Unable to create config directory " + configDirectory);
}
// unpack default XML files if none are defined
unpackResourcesToFolder("filler", ".xml", defaultXML_fillers, "config", configDirectory);
unpackResourcesToFolder("structures", ".xml", defaultXML_structures, "config", configDirectory);
unpackResourcesToFolder("celestialObjects", ".xml", defaultXML_celestialObjects, "config", configDirectory);
// always unpack the XML Schema
unpackResourceToFolder("WarpDrive.xsd", "config", configDirectory);
// read configuration file
loadWarpDriveConfig(new File(configDirectory, WarpDrive.MODID + ".cfg"));
CelestialObjectManager.load(configDirectory);
}
public static void loadWarpDriveConfig(File file) {
@ -426,7 +448,7 @@ public class WarpDriveConfig {
LOGGING_LUA = config.get("logging", "enable_LUA_logs", LOGGING_LUA, "Detailed LUA logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_RADAR = config.get("logging", "enable_radar_logs", LOGGING_RADAR, "Detailed radar logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_BREATHING = config.get("logging", "enable_breathing_logs", LOGGING_BREATHING, "Detailed breathing logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_WORLDGEN = config.get("logging", "enable_worldgen_logs", LOGGING_WORLDGEN, "Detailed world generation logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_WORLD_GENERATION = config.get("logging", "enable_world_generation_logs", LOGGING_WORLD_GENERATION, "Detailed world generation logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_PROFILING = config.get("logging", "enable_profiling_logs", LOGGING_PROFILING, "Profiling logs, enable it to check for lag").getBoolean(true);
LOGGING_DICTIONARY = config.get("logging", "enable_dictionary_logs", LOGGING_DICTIONARY, "Dictionary logs, enable it to dump blocks hardness and blast resistance at boot").getBoolean(true);
LOGGING_STARMAP = config.get("logging", "enable_starmap_logs", LOGGING_STARMAP, "Starmap logs, enable it to dump starmap registry updates").getBoolean(false);
@ -434,88 +456,14 @@ public class WarpDriveConfig {
LOGGING_FORCEFIELD = config.get("logging", "enable_forcefield_logs", LOGGING_FORCEFIELD, "Detailed forcefield logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_FORCEFIELD_REGISTRY = config.get("logging", "enable_forcefield_registry_logs", LOGGING_FORCEFIELD_REGISTRY, "ForceField registry logs, enable it to dump forcefield registry updates").getBoolean(false);
LOGGING_ACCELERATOR = config.get("logging", "enable_accelerator_logs", LOGGING_ACCELERATOR, "Detailed accelerator logs to help debug the mod, enable it before reporting a bug").getBoolean(false);
LOGGING_XML_PREPROCESSOR = config.get("logging", "enable_XML_preprocessor_logs", LOGGING_XML_PREPROCESSOR, "Save XML preprocessor results as output*.xml file, enable it to debug your XML configuration files").getBoolean(false);
LOGGING_RENDERING = config.get("logging", "enable_rendering_logs", LOGGING_RENDERING, "Detailed rendering logs to help debug the mod.").getBoolean(false);
// Celestial objects
{
config.addCustomCategoryComment("celestial_objects",
"Celestial objects are generally planets. They can also be a solar system (space) or the all mighty hyperspace.\n"
+ "Each celestial object is defined with a list of 14 integers in the following exact order:\n"
+ "- dimensionId : this is the id of the dimension. 0 is the Overworld, -1 is the Nether, 1 is the End."
+ "- dimensionCenterX, dimensionCenterZ: those are the center coordinate of that dimension world border.\n"
+ " This is measured in blocks. For convenience, it's usually 0, 0\n"
+ "- radiusX, radiusZ: this is the world border radius, measured in blocks. The total size is twice that.\n"
+ " This is also the size of the orbit area in space, so don't go too big\n"
+ "- parentDimensionId: this is the id of the parent dimension. For planets, this is the space dimension id.\n"
+ "- parentCenterX, parentCenterZ: this is the center coordinates in the parent dimension. For a planet, it needs to be different than 0, 0.\n"
+ "- isWarpDrive: this is a boolean flag defining if WarpDrive provides this dimension or not.\n"
+ " Currently only Space and Hyperspace can be provided: use other mods to generate planet world.\n"
+ " Set this to 0 to use another mod Space dimension.\n"
+ "- moonRatio: this is the chance for a moon to generate in a chunk.\n"
+ "- asteroidRatio: this is the chance for a lone asteroid to generate in a chunk.\n"
+ "- asteroidFieldRatio: this is the chance for an asteroid field to generate in a chunk.\n"
+ " All 3 ratios work the same way: 100000 is 100% chance, 0 will disable it. Those only works in WarpDrive dimensions, they're ignored otherwise.\n"
+ "- gravity: this is the gravity simulation type. 0 is vanilla, 1 is space, 2 is hyperspace.\n"
+ "- isBreathable: this is a boolean flag defining if ambient atmosphere is breathable.\n"
+ "Hyperspace is a dimension which is its own parent. In other words, hyperspace.dimensionId = hyperspace.parentDimensionId. There can be only one.\n"
+ "A Space is a dimension with Hyperspace as its parent.\n"
+ "In theory, multiple planets can exists in the same minecraft world.");
final String commentDimension = "dimensionId, dimensionCenterX, dimensionCenterZ, radiusX, radiusZ,\n"
+ "parentDimensionId, parentCenterX, parentCenterZ, isWarpDrive,\n"
+ "moonRatio, asteroidRatio, asteroidFieldRatio, gravity, isBreathable";
ConfigCategory categoryDimensions = config.getCategory("celestial_objects");
String[] nameDimensions = categoryDimensions.getValues().keySet().toArray(new String[0]);
if (nameDimensions.length == 0) {
nameDimensions = new String[] { "overworld", "nether", "end", "space", "hyperspace" };
int[][] defaultDimensions = {
// id x z radiusX radiusZ id parentX parentZ W moon ast astF g air
{ 0, 0, 0, 10000, 10000, -2, 0, 0, 0, 0, 0, 0, 0, 1 },
{ -1, 0, 0, 2500, 2500, -2, 30000, -50000, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1000, 1000, -2, -80000, 60000, 0, 0, 0, 0, 0, 1 },
{ -2, 0, 0, 100000, 100000, -3, 0, 0, 1, 125, 666, 166, 1, 0 },
{ -3, 0, 0, 100000, 100000, -3, 0, 0, 1, 0, 0, 0, 2, 0 }
};
for (int index = 0; index < nameDimensions.length; index++) {
config.get("celestial_objects", nameDimensions[index], defaultDimensions[index], commentDimension).getIntList();
}
}
int[] intDefaultDimension = { 0, 0, 0, 10000, 10000, -2, 0, 0, 0, 0, 0, 0, 0, 1 }; // 30000000 is Minecraft limit for SetBlock
celestialObjects = new CelestialObject[nameDimensions.length];
int index = 0;
for (String name : nameDimensions) {
int[] intDimension = config.get("celestial_objects", name, intDefaultDimension, commentDimension).getIntList();
if (intDimension.length != 14) {
WarpDrive.logger.warn("Invalid dimension definition '" + name + "' (exactly 8 integers are expected), using default instead");
intDimension = intDefaultDimension.clone();
}
CelestialObject celestialObject = new CelestialObject(intDimension[0], intDimension[1], intDimension[2], intDimension[3], intDimension[4],
intDimension[5], intDimension[6], intDimension[7]);
celestialObject.setSelfProvider(intDimension[8] != 0);
celestialObject.addGenerationRatio(intDimension[ 9] / 100000.0D, "moon");
celestialObject.addGenerationRatio(intDimension[10] / 100000.0D, "asteroid");
celestialObject.addGenerationRatio(intDimension[11] / 100000.0D, "asteroidField");
switch(intDimension[12]) {
case 0:
default:
celestialObject.setGravity(1.0D);
break;
case 1:
celestialObject.setGravity(0.3D);
break;
case 2:
celestialObject.setGravity(0.45D);
break;
}
celestialObject.setBreathable(intDimension[13] != 0);
WarpDrive.logger.info("Adding '" + name + "' as " + celestialObject);
celestialObjects[index] = celestialObject;
index++;
}
// FIXME: check planets aren't overlapping
// We're not checking invalid dimension id, so they can be pre-allocated (see MystCraft)
}
// Starmap registry
STARMAP_REGISTRY_UPDATE_INTERVAL_SECONDS = Commons.clamp(0, 300,
config.get("starmap", "registry_update_interval", STARMAP_REGISTRY_UPDATE_INTERVAL_SECONDS, "(measured in seconds)").getInt());
config.get("starmap", "registry_update_interval", STARMAP_REGISTRY_UPDATE_INTERVAL_SECONDS, "(measured in seconds)").getInt());
STARMAP_ALLOW_OVERLAPPING_CELESTIAL_OBJECTS =
config.get("starmap", "allow_overlapping_celestial_objects", STARMAP_ALLOW_OVERLAPPING_CELESTIAL_OBJECTS, "Enable to bypass the check at boot. Use at your own risk!").getBoolean();
// Ship
SHIP_MAX_ENERGY_STORED = Commons.clamp(0, Integer.MAX_VALUE,
@ -968,20 +916,6 @@ public class WarpDriveConfig {
}
public static void onFMLPostInitialization() {
// unpack default XML files if none are defined
File[] files = configDirectory.listFiles((file_notUsed, name) -> name.endsWith(".xml"));
if (files == null) {
throw new RuntimeException("Critical error accessing configuration directory, searching for *.xml files: " + configDirectory);
}
if (files.length == 0) {
for (String defaultXMLfilename : defaultXMLfilenames) {
unpackResourceToFolder(defaultXMLfilename, "config", configDirectory);
}
}
// always unpack the XML Schema
unpackResourceToFolder("WarpDrive.xsd", "config", configDirectory);
// load XML files
FillerManager.load(configDirectory);
StructureManager.load(configDirectory);
@ -1056,14 +990,29 @@ public class WarpDriveConfig {
}
/**
* Copy a default configuration file from the mod's resources to the specified configuration folder
* Check if a category of configuration files are missing, unpack default ones from the mod's resources to the specified target folder
* Target folder should be already created
**/
private static void unpackResourceToFolder(final String filename, final String sourceResourcePath, File targetFolder) {
// targetFolder is already created by caller
private static void unpackResourcesToFolder(final String prefix, final String suffix, final String[] filenames, final String resourcePathSource, File folderTarget) {
File[] files = configDirectory.listFiles((file_notUsed, name) -> name.startsWith(prefix) && name.endsWith(suffix));
if (files == null) {
throw new RuntimeException(String.format("Critical error accessing configuration directory, searching for %s*%s files: %s", prefix, suffix, configDirectory));
}
if (files.length == 0) {
for (String filename : filenames) {
unpackResourceToFolder(filename, resourcePathSource, folderTarget);
}
}
}
/**
* Copy a default configuration file from the mod's resources to the specified configuration folder
* Target folder should be already created
**/
private static void unpackResourceToFolder(final String filename, final String resourcePathSource, File folderTarget) {
String resourceName = resourcePathSource + "/" + filename;
String resourceName = sourceResourcePath + "/" + filename;
File destination = new File(targetFolder, filename);
File destination = new File(folderTarget, filename);
try {
InputStream inputStream = WarpDrive.class.getClassLoader().getResourceAsStream(resourceName);

View file

@ -0,0 +1,72 @@
package cr0s.warpdrive.config;
import cr0s.warpdrive.WarpDrive;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public abstract class XmlFileManager {
protected void load(File dir, final String prefixFilename, final String elementName) {
// (directory is created by caller, so it can copy default files if any)
if (!dir.isDirectory()) {
throw new IllegalArgumentException("File path " + dir.getPath() + " must be a directory!");
}
File[] files = dir.listFiles((file_notUsed, name) -> name.startsWith(prefixFilename) && name.endsWith(".xml"));
if (files == null || files.length == 0) {
throw new IllegalArgumentException("File path " + dir.getPath() + " contains no " + prefixFilename + "*.xml files!");
}
for (File file : files) {
try {
WarpDrive.logger.info("Loading configuration file " + file.getName());
Document document = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
// pre-process the file
String result = XmlPreprocessor.checkModRequirements(document.getDocumentElement());
if (!result.isEmpty()) {
WarpDrive.logger.info("Skipping configuration file " + file.getName() + " due to " + result);
return;
}
XmlPreprocessor.doModReqSanitation(document);
XmlPreprocessor.doLogicPreprocessing(document);
// only add selected root elements
List<Element> listElements = getChildrenElementByTagName(document.getDocumentElement(), elementName);
for (int indexElement = 0; indexElement < listElements.size(); indexElement++) {
Element element = listElements.get(indexElement);
String location = String.format("%d/%d", indexElement + 1, listElements.size());
parseRootElement(location, element);
}
} catch (Exception exception) {
WarpDrive.logger.error("Error loading file " + file.getName() + ": " + exception.getMessage());
exception.printStackTrace();
}
}
}
protected abstract void parseRootElement(final String location, Element elementCelestialObject) throws InvalidXmlException, SAXException, IOException;
public static List<Element> getChildrenElementByTagName(final Element parent, final String name) {
List<Element> listElements = new ArrayList<>();
for (Node nodeChild = parent.getFirstChild(); nodeChild != null; nodeChild = nodeChild.getNextSibling()) {
if ( nodeChild.getNodeType() == Node.ELEMENT_NODE
&& name.equals(nodeChild.getNodeName()) ) {
listElements.add((Element) nodeChild);
}
}
return listElements;
}
}

View file

@ -1,6 +1,5 @@
package cr0s.warpdrive.config;
import cr0s.warpdrive.WarpDrive;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
@ -19,12 +18,13 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;
import cpw.mods.fml.common.Loader;
public class XmlPreprocessor {
static final boolean enableOutput = false;
static int outputCount = 1;
static AtomicInteger outputCount = new AtomicInteger(1);
/**
* Check the given element for a mod attribute and return a string of all the ones that are not loaded, separated by commas
@ -111,7 +111,7 @@ public class XmlPreprocessor {
// copy children with replaced variable
for(String variableValue : inOptions) {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Resolving for-loop with variable " + variableName + " = " + variableValue);
}
NodeList allChildren = root.getChildNodes();
@ -141,7 +141,7 @@ public class XmlPreprocessor {
// copy children with replaced variable
for (int variableValue = intFrom; variableValue <= intTo; variableValue++) {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Resolving for-loop with variable " + variableName + " = " + variableValue);
}
NodeList allChildren = root.getChildNodes();
@ -155,14 +155,14 @@ public class XmlPreprocessor {
//Remove the old node
root.getParentNode().removeChild(root);
if (enableOutput) {
if (WarpDriveConfig.LOGGING_XML_PREPROCESSOR) {
try {
Transformer transformer = TransformerFactory.newInstance().newTransformer();
Result output = new StreamResult(new File("output" + outputCount + ".xml"));
Source input = new DOMSource(root.getOwnerDocument());
transformer.transform(input, output);
outputCount++;
outputCount.incrementAndGet();
} catch (Exception exception) {
exception.printStackTrace();
}

View file

@ -0,0 +1,33 @@
package cr0s.warpdrive.config;
import org.w3c.dom.Element;
/**
* Collection of elements with ratios and weights. Helps to select element with controlled odds.
*
* @author ncrashed, LemADEC
*
* @param <E>
**/
public class XmlRandomCollection<E extends IXmlRepresentable> extends RandomCollection<E> {
/**
* Loads object from given XML element and parses configurations for weighted pick.
*
* @param object
* Object to load into
* @param element
* Element of an XML file
* @throws InvalidXmlException
**/
public void loadFromXML(E object, Element element) throws InvalidXmlException {
if (!object.loadFromXmlElement(element)) {// skip invalid entries
return;
}
String stringRatio = element.getAttribute("ratio");
String stringWeight = element.getAttribute("weight");
add(object, stringRatio, stringWeight);
}
}

View file

@ -4,7 +4,6 @@ import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.IXmlRepresentable;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.data.JumpBlock;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import net.minecraft.block.Block;
@ -58,15 +57,6 @@ public class Filler implements IXmlRepresentable {
return true;
}
/**
* @deprecated Not implemented
**/
@Deprecated
@Override
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException {
throw new InvalidXmlException("Not implemented");
}
public void setBlock(World world, int x, int y, int z) {
JumpBlock.setBlockNoLight(world, x, y, z, block, metadata, 2);

View file

@ -2,111 +2,56 @@ package cr0s.warpdrive.config.filler;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.RandomCollection;
import cr0s.warpdrive.config.XmlRandomCollection;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlPreprocessor;
import org.w3c.dom.Document;
import cr0s.warpdrive.config.XmlFileManager;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Random;
public class FillerManager {
// all fillerSets
private static HashMap<String, RandomCollection<FillerSet>> fillerSetsByGroup;
public class FillerManager extends XmlFileManager {
@SuppressWarnings("MismatchedReadAndWriteOfArray") // we've no required filler groups
private static final String[] REQUIRED_GROUPS = { };
private static FillerManager INSTANCE = new FillerManager();
// all fillerSets
private static HashMap<String, XmlRandomCollection<FillerSet>> fillerSetsByGroup;
public static void load(File dir) {
// (directory is created by caller, so it can copy default files if any)
if (!dir.isDirectory()) {
throw new IllegalArgumentException("File path " + dir.getName() + " must be a directory!");
}
File[] files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file_notUsed, String name) {
return name.startsWith("filler") && name.endsWith(".xml");
}
});
fillerSetsByGroup = new HashMap<>();
for(File file : files) {
try {
loadXmlFillerFile(file);
} catch (Exception exception) {
WarpDrive.logger.error("Error loading filler data file " + file.getName() + ": " + exception.getMessage());
exception.printStackTrace();
}
}
for (String group : REQUIRED_GROUPS) {
if (!fillerSetsByGroup.containsKey(group)) {
WarpDrive.logger.error("Error: no fillerSet defined for mandatory group " + group);
}
}
INSTANCE.load(dir, "filler", "fillerSet");
propagateFillerSets();
WarpDrive.logger.info("Loading filler data files done");
}
@SuppressWarnings("Convert2Diamond")
private static void loadXmlFillerFile(File file) throws InvalidXmlException, SAXException, IOException {
WarpDrive.logger.info("Loading filler data file " + file.getName());
Document document = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
// pre-process the file
String result = XmlPreprocessor.checkModRequirements(document.getDocumentElement());
if (!result.isEmpty()) {
WarpDrive.logger.info("Skipping filler data file " + file.getName() + " due to " + result);
return;
@Override
protected void parseRootElement(final String location, Element elementFillerSet) throws InvalidXmlException, SAXException, IOException {
String group = elementFillerSet.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException("FillerSet " + location + " is missing a group attribute!");
}
XmlPreprocessor.doModReqSanitation(document);
XmlPreprocessor.doLogicPreprocessing(document);
// only add FillerSets
NodeList nodeListFillerSet = document.getElementsByTagName("fillerSet");
for (int fillerSetIndex = 0; fillerSetIndex < nodeListFillerSet.getLength(); fillerSetIndex++) {
Element elementFillerSet = (Element) nodeListFillerSet.item(fillerSetIndex);
String group = elementFillerSet.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException("FillerSet " + (fillerSetIndex + 1) + "/" + nodeListFillerSet.getLength() + " is missing a group attribute!");
}
String name = elementFillerSet.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException("FillerSet " + (fillerSetIndex + 1) + "/" + nodeListFillerSet.getLength() + " is missing a name attribute!");
}
if (WarpDriveConfig.LOGGING_WORLDGEN) {
WarpDrive.logger.info("- found FillerSet " + group + ":" + name);
}
RandomCollection<FillerSet> randomCollection = fillerSetsByGroup.get(group);
if (randomCollection == null) {
randomCollection = new RandomCollection<>();
fillerSetsByGroup.put(group, randomCollection);
}
FillerSet fillerSet = randomCollection.getNamedEntry(name);
if (fillerSet == null) {
fillerSet = new FillerSet(group, name);
}
randomCollection.loadFromXML(fillerSet, elementFillerSet);
String name = elementFillerSet.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException("FillerSet " + location + " is missing a name attribute!");
}
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("- found FillerSet " + group + ":" + name);
}
XmlRandomCollection<FillerSet> xmlRandomCollection = fillerSetsByGroup.computeIfAbsent(group, k -> new XmlRandomCollection<>());
FillerSet fillerSet = xmlRandomCollection.getNamedEntry(name);
if (fillerSet == null) {
fillerSet = new FillerSet(group, name);
}
xmlRandomCollection.loadFromXML(fillerSet, elementFillerSet);
}
@SuppressWarnings("Convert2Diamond")
@ -114,13 +59,9 @@ public class FillerManager {
HashMap<FillerSet, ArrayList<String>> fillerSetsDependencies = new HashMap<>();
// scan for static import dependencies
for (RandomCollection<FillerSet> fillerSets : fillerSetsByGroup.values()) {
for (XmlRandomCollection<FillerSet> fillerSets : fillerSetsByGroup.values()) {
for (FillerSet fillerSet : fillerSets.elements()) {
ArrayList<String> dependencies = fillerSetsDependencies.get(fillerSet);
if (dependencies == null) {
dependencies = new ArrayList<>();
fillerSetsDependencies.put(fillerSet, dependencies);
}
ArrayList<String> dependencies = fillerSetsDependencies.computeIfAbsent(fillerSet, k -> new ArrayList<>());
dependencies.addAll(fillerSet.getImportGroupNames());
}
}
@ -143,7 +84,7 @@ public class FillerManager {
} else {
try {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Importing FillerSet " + fillerSet.getFullName() + " in " + entry.getKey().getFullName());
}
entry.getKey().loadFrom(fillerSet);
@ -178,7 +119,7 @@ public class FillerManager {
}
public static FillerSet getRandomFillerSetFromGroup(Random random, final String groupName) {
RandomCollection<FillerSet> group = fillerSetsByGroup.get(groupName);
XmlRandomCollection<FillerSet> group = fillerSetsByGroup.get(groupName);
if (group == null) {
return null;
}
@ -191,7 +132,7 @@ public class FillerManager {
WarpDrive.logger.error("Invalid FillerSet '" + groupAndName + "'. Expecting '{group}:{name}'");
return null;
}
RandomCollection<FillerSet> group = fillerSetsByGroup.get(parts[0]);
XmlRandomCollection<FillerSet> group = fillerSetsByGroup.get(parts[0]);
if (group == null) {
return null;
}

View file

@ -3,13 +3,14 @@ package cr0s.warpdrive.config.filler;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.IXmlRepresentable;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.RandomCollection;
import org.w3c.dom.Document;
import cr0s.warpdrive.config.XmlFileManager;
import cr0s.warpdrive.config.XmlRandomCollection;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import net.minecraft.init.Blocks;
@ -20,7 +21,7 @@ import net.minecraft.init.Blocks;
public class FillerSet implements IXmlRepresentable, Comparable {
protected String group;
protected String name;
private RandomCollection<Filler> fillers;
private XmlRandomCollection<Filler> fillers;
private ArrayList<String> importGroupNames;
private ArrayList<String> importGroups;
@ -36,7 +37,7 @@ public class FillerSet implements IXmlRepresentable, Comparable {
public FillerSet(final String group, final String name) {
this.group = group;
this.name = name;
fillers = new RandomCollection<>();
fillers = new XmlRandomCollection<>();
importGroupNames = new ArrayList<>();
importGroups = new ArrayList<>();
}
@ -57,19 +58,15 @@ public class FillerSet implements IXmlRepresentable, Comparable {
@Override
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
NodeList nodeListFillers = element.getElementsByTagName("filler");
for (int i = 0; i < nodeListFillers.getLength(); i++) {
Element elementFiller = (Element) nodeListFillers.item(i);
List<Element> listFillers = XmlFileManager.getChildrenElementByTagName(element, "filler");
for (Element elementFiller : listFillers) {
Filler filler = new Filler();
fillers.loadFromXML(filler, elementFiller);
}
NodeList nodeListImports = element.getElementsByTagName("import");
if (nodeListImports.getLength() > 0) {
for (int importIndex = 0; importIndex < nodeListImports.getLength(); importIndex++) {
Element elementImport = (Element) nodeListImports.item(importIndex);
List<Element> listImports = XmlFileManager.getChildrenElementByTagName(element, "import");
if (!listImports.isEmpty()) {
for (Element elementImport : listImports) {
String importGroup = elementImport.getAttribute("group");
String importName = elementImport.getAttribute("name");
if (!importGroup.isEmpty()) {
@ -87,17 +84,8 @@ public class FillerSet implements IXmlRepresentable, Comparable {
return true;
}
/**
* @deprecated Not implemented
**/
@Deprecated
@Override
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException {
throw new InvalidXmlException("Not implemented");
}
@Override
public int compareTo(Object object) {
public int compareTo(@Nonnull Object object) {
return name.compareTo(((FillerSet) object).name);
}

View file

@ -2,11 +2,11 @@ package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.config.IXmlRepresentable;
import cr0s.warpdrive.config.InvalidXmlException;
import org.w3c.dom.Document;
import cr0s.warpdrive.config.XmlFileManager;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import net.minecraft.world.gen.feature.WorldGenerator;
@ -35,14 +35,13 @@ public abstract class AbstractStructure extends WorldGenerator implements IXmlRe
}
abstract public AbstractInstance instantiate(Random random);
abstract public AbstractStructureInstance instantiate(Random random);
@Override
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
NodeList nodeListVariables = element.getElementsByTagName("variable");
for (int variableIndex = 0; variableIndex < nodeListVariables.getLength(); variableIndex++) {
Element elementVariable = (Element) nodeListVariables.item(variableIndex);
List<Element> listVariables = XmlFileManager.getChildrenElementByTagName(element, "variable");
for (Element elementVariable : listVariables) {
String variableName = elementVariable.getAttribute("name");
String variableExpression = elementVariable.getTextContent();
variables.put(variableName, variableExpression);
@ -50,13 +49,4 @@ public abstract class AbstractStructure extends WorldGenerator implements IXmlRe
return true;
}
/**
* @deprecated Not implemented
**/
@Deprecated
@Override
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException {
throw new InvalidXmlException("Not implemented");
}
}

View file

@ -11,11 +11,11 @@ import net.minecraft.world.gen.feature.WorldGenerator;
* @author LemADEC
*
*/
public abstract class AbstractInstance extends WorldGenerator {
public abstract class AbstractStructureInstance extends WorldGenerator {
protected AbstractStructure structure;
protected HashMap<String,Double> variables = new HashMap<>();
public AbstractInstance(AbstractStructure structure, Random random) {
public AbstractStructureInstance(AbstractStructure structure, Random random) {
this.structure = structure;
// evaluate variables
@ -40,14 +40,6 @@ public abstract class AbstractInstance extends WorldGenerator {
}
}
protected static int randomRange(Random random, final int min, final int max) {
return min + ((max - min > 0) ? random.nextInt(max - min + 1) : 0);
}
protected static double randomRange(Random random, final double min, final double max) {
return min + ((max - min > 0) ? random.nextDouble() * (max - min) : 0);
}
protected String evaluate(final String valueOrExpression) {
if (!valueOrExpression.contains("%")) {
return valueOrExpression;
@ -59,7 +51,7 @@ public abstract class AbstractInstance extends WorldGenerator {
return result;
}
public AbstractInstance(NBTTagCompound tag) {
public AbstractStructureInstance(NBTTagCompound tag) {
// FIXME to be implemented
// get deployable

View file

@ -0,0 +1,30 @@
package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.config.InvalidXmlException;
import org.w3c.dom.Element;
import java.util.Random;
import net.minecraft.world.World;
public class AsteroidField extends AbstractStructure {
public AsteroidField(String group, String name) {
super(group, name);
}
@Override
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
return false;
}
@Override
public boolean generate(World world, Random random, int x, int y, int z) {
return instantiate(random).generate(world, random, x, y, z);
}
@Override
public AbstractStructureInstance instantiate(Random random) {
return new AsteroidFieldInstance(this, random);
}
}

View file

@ -0,0 +1,177 @@
package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.LocalProfiler;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.world.WorldGenSmallShip;
import cr0s.warpdrive.world.WorldGenStation;
import java.util.Random;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
public class AsteroidFieldInstance extends AbstractStructureInstance {
public AsteroidFieldInstance(AsteroidField asteroidField, Random random) {
super(asteroidField, random);
}
public AsteroidFieldInstance(NBTTagCompound tag) {
super(tag);
// TODO not implemented
}
@Override
public void WriteToNBT(NBTTagCompound tag) {
super.WriteToNBT(tag);
// TODO not implemented
}
private static float binomialRandom(World world) {
float linear = world.rand.nextFloat();
// ideal sphere repartition = x ^ 0.5 (sqrt)
// Dilution but slow to compute = 0.5 * ( x ^ 0.3 + 1 + (x - 1) ^ 3 )
// Optimized 'pushed out' form = 1.25 - 0.625 / (0.5 + 2 * x)
// Natural sphere with ring = (1 - x ^ 2.5) * x ^ 0.5 + x ^ 4
// rectangular approach: return 0.5F * linear + 0.5F * linear * linear;
return 1.25F - 0.625F / (0.5F + 2.0F * linear);
}
@Override
public boolean generate(World world, Random random, final int x, final int y1, final int z) {
LocalProfiler.start("SpaceWorldGenerator.generateAsteroidField");
// 6.0.1 au = 120 radius with 60 to 140 big + 60 to 140 small + 5 to 13 gaz
// 45238 blocks surface with 120 to 280 asteroids => 161 to 376 blocks per asteroid (big & small)
// 6.0.2 av big = 80 to 180 radius with 40 to 90 big + 80 to 200 small + 5 to 13 gaz
// 20106 to 101787 surface with 120 to 290 asteroids => 69 to 848 blocks per asteroid
// 6.0.2 av small = 30 to 80 radius with 2 to 22 big + 15 to 75 small + 0 to 3 gaz
// 2827 to 20106 surface with 17 to 97 asteroids => 29 to 1182 blocks per asteroid
// random distanced one = 89727 surface 256 asteroids => 350 blocks per asteroid
/*
boolean isBig = world.rand.nextInt(3) == 1;
int numOfBigAsteroids, numOfSmallAsteroids, numOfClouds, maxDistance, maxHeight;
if (isBig) {
numOfBigAsteroids = 40 + world.rand.nextInt(50);
numOfSmallAsteroids = 80 + world.rand.nextInt(120);
numOfClouds = 5 + world.rand.nextInt(8);
maxDistance = 80 + world.rand.nextInt(100);
maxHeight = 40 + world.rand.nextInt(40);
} else {
numOfBigAsteroids = 2 + world.rand.nextInt(20);
numOfSmallAsteroids = 15 + world.rand.nextInt(60);
numOfClouds = 0 + world.rand.nextInt(3);
maxDistance = 30 + world.rand.nextInt(50);
maxHeight = 30 + world.rand.nextInt(30);
}
/**/
float surfacePerAsteroid = 80.0F + world.rand.nextFloat() * 300;
int maxDistance = 30 + world.rand.nextInt(170);
int maxDistanceBig = Math.round(maxDistance * (0.6F + 0.2F * world.rand.nextFloat()));
int maxDistanceSmall = Math.round(maxDistance * 1.1F);
float bigRatio = 0.3F + world.rand.nextFloat() * 0.3F;
float surfaceBig = (float) (Math.PI * Math.pow(maxDistanceBig, 2));
float surfaceSmall = (float) (Math.PI * Math.pow(maxDistanceSmall, 2));
int numOfBigAsteroids = Math.round(bigRatio * surfaceBig / surfacePerAsteroid);
int numOfSmallAsteroids = Math.round((1.0F - bigRatio) * surfaceSmall / surfacePerAsteroid);
int numOfClouds = Math.round(numOfBigAsteroids * 1.0F / (10.0F + world.rand.nextInt(10)));
int maxHeight = 70 + world.rand.nextInt(50);
int y2 = Math.min(WarpDriveConfig.SPACE_GENERATOR_Y_MAX_BORDER - maxHeight,
Math.max(y1, WarpDriveConfig.SPACE_GENERATOR_Y_MIN_BORDER + maxHeight));
WarpDrive.logger.info("Generating asteroid field at " + x + "," + y2 + "," + z + " qty " + numOfBigAsteroids + ", " + numOfSmallAsteroids + ", "
+ numOfClouds + " over " + maxDistance + ", " + maxHeight + " surfacePerAsteroid " + String.format("%.1f", surfacePerAsteroid));
// Setting up of big asteroids
for (int i = 1; i <= numOfBigAsteroids; i++) {
float binomial = binomialRandom(world);
double bearing = world.rand.nextFloat() * 2.0D * Math.PI;
double yawn = world.rand.nextFloat() * Math.PI;
float horizontalRange = Math.max(6.0F, binomial * maxDistanceBig);
float verticalRange = Math.max(3.0F, binomial * maxHeight);
int aX = (int) (x + Math.round(horizontalRange * Math.cos(bearing)));
int aY = (int) (y2 + Math.round(verticalRange * Math.cos(yawn)));
int aZ = (int) (z + Math.round(horizontalRange * Math.sin(bearing)));
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(String.format("Big asteroid: %.3f %.3f r %.3f r makes %3d, %3d, %3d",
(double) binomial, bearing, yawn, aX, aY, aZ));
}
// Place an asteroid
AbstractStructure moon = StructureManager.getStructure(world.rand, StructureManager.GROUP_ASTEROIDS, null);
moon.generate(world, world.rand, aX, aY, aZ);
}
// Setting up small asteroids
for (int i = 1; i <= numOfSmallAsteroids; i++) {
float binomial = binomialRandom(world);
double bearing = world.rand.nextFloat() * 2.0D * Math.PI;
double yawn = world.rand.nextFloat() * Math.PI;
float horizontalRange = Math.max(6.0F, binomial * maxDistanceSmall);
float verticalRange = Math.max(3.0F, binomial * maxHeight);
int aX = (int) (x + Math.round(horizontalRange * Math.cos(bearing)));
int aY = (int) (y2 + Math.round(verticalRange * Math.cos(yawn)));
int aZ = (int) (z + Math.round(horizontalRange * Math.sin(bearing)));
// Placing
if (world.rand.nextInt(400) != 1) {
AbstractStructure moon = StructureManager.getStructure(world.rand, StructureManager.GROUP_ASTEROIDS, null);
moon.generate(world, world.rand, aX, aY, aZ);
} else {
if (world.rand.nextInt(20) != 1) {
generateSmallShip(world, aX, aY, aZ, 8);
} else {
generateStation(world, aX, aY, aZ, 8);
}
}
}
// Setting up gas clouds
for (int i = 1; i <= numOfClouds; i++) {
float binomial = binomialRandom(world);
double bearing = world.rand.nextFloat() * 2.0D * Math.PI;
double yawn = world.rand.nextFloat() * Math.PI;
float horizontalRange = Math.max(6.0F, binomial * maxDistanceBig);
float verticalRange = Math.max(3.0F, binomial * maxHeight);
int aX = (int) (x + Math.round(horizontalRange * Math.cos(bearing)));
int aY = (int) (y2 + Math.round(verticalRange * Math.cos(yawn)));
int aZ = (int) (z + Math.round(horizontalRange * Math.sin(bearing)));
// Placing
if (world.rand.nextBoolean()) {
AbstractStructure gasCloud = StructureManager.getStructure(world.rand, StructureManager.GROUP_GAS_CLOUDS, null);
if (gasCloud != null) {
gasCloud.generate(world, world.rand, aX, aY, aZ);
}
}
}
LocalProfiler.stop();
return true;
}
private static void generateSmallShip(World world, final int x, final int y, final int z, final int jitter) {
int x2 = x + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int y2 = y + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int z2 = z + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
WarpDrive.logger.info("Generating small ship at " + x2 + "," + y2 + "," + z2);
new WorldGenSmallShip(world.rand.nextFloat() > 0.2F).generate(world, world.rand, x2, y2, z2);
}
private static void generateStation(World world, final int x, final int y, final int z, final int jitter) {
int x2 = x + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int y2 = y + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int z2 = z + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
WarpDrive.logger.info("Generating small ship at " + x2 + "," + y2 + "," + z2);
new WorldGenStation(world.rand.nextBoolean()).generate(world, world.rand, x2, y2, z2);
}
}

View file

@ -4,10 +4,10 @@ import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.IXmlRepresentable;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.WarpDriveConfig;
import org.w3c.dom.Document;
import cr0s.warpdrive.config.XmlFileManager;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.util.List;
import java.util.Random;
import net.minecraft.block.Block;
@ -24,13 +24,13 @@ public class MetaOrb extends Orb {
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
super.loadFromXmlElement(element);
NodeList nodeListMetaShells = element.getElementsByTagName("metaShell");
if (nodeListMetaShells.getLength() > 1) {
List<Element> listMetaShells = XmlFileManager.getChildrenElementByTagName(element, "metaShell");
if (listMetaShells.size() > 1) {
throw new InvalidXmlException("Too many metaShell defined in structure " + getFullName() + ". Maximum is 1.");
}
if (nodeListMetaShells.getLength() == 1) {
if (listMetaShells.size() == 1) {
metaShell = new MetaShell(getFullName());
metaShell.loadFromXmlElement((Element) nodeListMetaShells.item(0));
metaShell.loadFromXmlElement(listMetaShells.get(0));
}
return true;
@ -42,7 +42,7 @@ public class MetaOrb extends Orb {
}
@Override
public AbstractInstance instantiate(Random random) {
public AbstractStructureInstance instantiate(Random random) {
return new MetaOrbInstance(this, random);
}
@ -66,7 +66,7 @@ public class MetaOrb extends Orb {
@Override
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(" + found metashell");
}
@ -152,14 +152,5 @@ public class MetaOrb extends Orb {
return true;
}
/**
* @deprecated Not implemented
**/
@Deprecated
@Override
public void saveToXmlElement(Element element, Document document) throws InvalidXmlException {
throw new InvalidXmlException("Not implemented");
}
}
}

View file

@ -1,5 +1,6 @@
package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.LocalProfiler;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.WarpDriveConfig;
@ -26,7 +27,7 @@ public class MetaOrbInstance extends OrbInstance {
@Override
public boolean generate(World world, Random random, int x, int y, int z) {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Generating MetaOrb " + structure.name + " of " + metaShell.count + " cores with radius of " + totalThickness);
}
LocalProfiler.start("[AsteroidInstance] Generating MetaOrb " + structure.name + " of " + metaShell.count + " cores with radius of " + totalThickness);
@ -115,7 +116,7 @@ public class MetaOrbInstance extends OrbInstance {
metadata = 0;
return;
}
count = randomRange(random, asteroid.metaShell.minCount, asteroid.metaShell.maxCount);
count = Commons.randomRange(random, asteroid.metaShell.minCount, asteroid.metaShell.maxCount);
radius = Math.max(asteroid.metaShell.minRadius, asteroid.metaShell.relativeRadius * totalThickness);
block = asteroid.metaShell.block;
metadata = asteroid.metaShell.metadata;

View file

@ -3,11 +3,12 @@ package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlFileManager;
import cr0s.warpdrive.config.filler.FillerManager;
import cr0s.warpdrive.config.filler.FillerSet;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.util.List;
import java.util.Random;
import net.minecraft.world.World;
@ -26,11 +27,10 @@ public class Orb extends AbstractStructure {
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
super.loadFromXmlElement(element);
NodeList nodeListShells = element.getElementsByTagName("shell");
orbShells = new OrbShell[nodeListShells.getLength()];
List<Element> listShells = XmlFileManager.getChildrenElementByTagName(element, "shell");
orbShells = new OrbShell[listShells.size()];
int shellIndexOut = 0;
for (int shellIndexIn = 0; shellIndexIn < nodeListShells.getLength(); shellIndexIn++) {
Element elementShell = (Element) nodeListShells.item(shellIndexIn);
for (Element elementShell : listShells) {
String orbShellName = elementShell.getAttribute("name");
orbShells[shellIndexOut] = new OrbShell(getFullName(), orbShellName);
@ -43,12 +43,12 @@ public class Orb extends AbstractStructure {
}
}
NodeList nodeListSchematic = element.getElementsByTagName("schematic");
if (nodeListSchematic.getLength() > 1) {
List<Element> listSchematic = XmlFileManager.getChildrenElementByTagName(element, "schematic");
if (listSchematic.size() > 1) {
WarpDrive.logger.error("Too many schematic defined, only first one will be used in structure " + getFullName());
}
if (nodeListSchematic.getLength() > 0) {
schematicName = ((Element)nodeListSchematic.item(0)).getAttribute("group");
if (listSchematic.size() > 0) {
schematicName = listSchematic.get(0).getAttribute("group");
}
return true;
@ -60,7 +60,7 @@ public class Orb extends AbstractStructure {
}
@Override
public AbstractInstance instantiate(Random random) {
public AbstractStructureInstance instantiate(Random random) {
return new OrbInstance(this, random);
}
@ -76,7 +76,7 @@ public class Orb extends AbstractStructure {
@Override
public boolean loadFromXmlElement(Element element) throws InvalidXmlException {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(" + found shell " + element.getAttribute("name"));
}
@ -131,7 +131,7 @@ public class Orb extends AbstractStructure {
WarpDrive.logger.info("Ignoring invalid group " + importGroup + " in shell " + name + " of structure " + parentFullName);
continue;
}
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Filling " + parentFullName + ":" + name + " with " + importGroup + ":" + fillerSet.getName());
}
orbShell.loadFrom(fillerSet);
@ -141,7 +141,7 @@ public class Orb extends AbstractStructure {
WarpDrive.logger.error("Failed to instantiate shell " + name + " from structure " + parentFullName);
}
if (orbShell.isEmpty()) {
if (WarpDriveConfig.LOGGING_WORLDGEN) {
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info("Ignoring empty shell " + name + " in structure " + parentFullName + "");
}
return null;

View file

@ -1,5 +1,6 @@
package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.structures.Orb.OrbShell;
import cr0s.warpdrive.world.EntitySphereGen;
@ -11,7 +12,7 @@ import java.util.Random;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
public class OrbInstance extends AbstractInstance {
public class OrbInstance extends AbstractStructureInstance {
protected OrbShell[] orbShells;
protected int[] orbShellThicknesses;
protected int totalThickness;
@ -32,7 +33,7 @@ public class OrbInstance extends AbstractInstance {
OrbShell orbShell = orb.orbShells[orbShellIndexIn].instantiate(random);
if (orbShell != null) {
orbShells[orbShellIndexOut] = orbShell;
int thickness = randomRange(random, orbShell.minThickness, orbShell.maxThickness);
int thickness = Commons.randomRange(random, orbShell.minThickness, orbShell.maxThickness);
orbShellThicknesses[orbShellIndexOut] = thickness;
totalThickness += thickness;
minThickness += orbShell.minThickness;

View file

@ -17,7 +17,7 @@ public class Schematic extends AbstractStructure {
}
@Override
public AbstractInstance instantiate(Random random) {
public AbstractStructureInstance instantiate(Random random) {
// TODO Auto-generated method stub
return null;
}

View file

@ -0,0 +1,39 @@
package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.api.IStringSerializable;
import java.util.Random;
/**
* @author Francesco, LemADEC
*
*/
public class StructureGroup implements IStringSerializable {
protected String group;
protected String name;
public StructureGroup(final String group, final String name) {
this.group = group;
this.name = name;
}
@Override
public String getName() {
return name;
}
public String getFullName() {
return group + ":" + name;
}
public AbstractStructureInstance instantiate(Random random) {
if (group.equals("asteroidField")) {
return new AsteroidFieldInstance(null, random);
}
return StructureManager.getStructure(random, group, name).instantiate(random);
}
public String getGroup() {
return group;
}
}

View file

@ -2,120 +2,73 @@ package cr0s.warpdrive.config.structures;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.RandomCollection;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlPreprocessor;
import cr0s.warpdrive.config.XmlRandomCollection;
import cr0s.warpdrive.config.XmlFileManager;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Random;
public class StructureManager {
public class StructureManager extends XmlFileManager {
private static StructureManager INSTANCE = new StructureManager();
public static final String GROUP_STARS = "star";
public static final String GROUP_MOONS = "moon";
public static final String GROUP_GASCLOUDS = "gascloud";
public static final String GROUP_GAS_CLOUDS = "gascloud";
public static final String GROUP_ASTEROIDS = "asteroid";
public static final String GROUP_ASTFIELDS_BIG = "astfield_big";
public static final String GROUP_ASTFIELDS_SMALL = "astfield_small";
public static final String GROUP_ASTEROIDS_FIELDS = "asteroids_field";
private static HashMap<String, RandomCollection<AbstractStructure>> structuresByGroup;
private static HashMap<String, XmlRandomCollection<AbstractStructure>> structuresByGroup;
private static final String[] REQUIRED_GROUPS = { GROUP_STARS, GROUP_MOONS, GROUP_GASCLOUDS, GROUP_ASTEROIDS, GROUP_ASTFIELDS_BIG, GROUP_ASTFIELDS_SMALL };
private static final String[] REQUIRED_GROUPS = { GROUP_STARS, GROUP_MOONS, GROUP_GAS_CLOUDS, GROUP_ASTEROIDS, GROUP_ASTEROIDS_FIELDS };
public static void load(File dir) {
dir.mkdir();
if (!dir.isDirectory()) {
throw new IllegalArgumentException("File path " + dir.getPath() + " must be a directory!");
}
File[] files = dir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file_notUsed, String name) {
return name.startsWith("structure") && name.endsWith(".xml");
}
});
structuresByGroup = new HashMap<>();
for (File file : files) {
try {
loadXmlStructureFile(file);
} catch (Exception exception) {
WarpDrive.logger.error("Error loading file " + file.getName() + ": " + exception.getMessage());
exception.printStackTrace();
}
}
INSTANCE.load(dir, "structure", "structure");
for (String group : REQUIRED_GROUPS) {
if (!structuresByGroup.containsKey(group)) {
WarpDrive.logger.error("Error: no structure defined for mandatory group " + group);
}
}
WarpDrive.logger.info("Loading structure data files done");
}
private static void loadXmlStructureFile(File file) throws InvalidXmlException, SAXException, IOException {
WarpDrive.logger.info("Loading structure data file " + file.getName());
Document document = WarpDriveConfig.getXmlDocumentBuilder().parse(file);
// pre-process the file
String result = XmlPreprocessor.checkModRequirements(document.getDocumentElement());
if (!result.isEmpty()) {
WarpDrive.logger.info("Skipping structure data file " + file.getName() + " due to " + result);
return;
@Override
protected void parseRootElement(final String location, Element elementStructure) throws InvalidXmlException, SAXException, IOException {
String group = elementStructure.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException("Structure " + location + " is missing a group attribute!");
}
XmlPreprocessor.doModReqSanitation(document);
XmlPreprocessor.doLogicPreprocessing(document);
// only add FillerSets
NodeList nodeListStructures = document.getElementsByTagName("structure");
for (int structureIndex = 0; structureIndex < nodeListStructures.getLength(); structureIndex++) {
Element elementStructure = (Element) nodeListStructures.item(structureIndex);
String group = elementStructure.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException("Structure " + (structureIndex + 1) + "/" + nodeListStructures.getLength() + " is missing a group attribute!");
}
String name = elementStructure.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException("Structure " + (structureIndex + 1) + "/" + nodeListStructures.getLength() + " is missing a name attribute!");
}
WarpDrive.logger.info("- found Structure " + group + ":" + name);
RandomCollection<AbstractStructure> randomCollection = structuresByGroup.get(group);
if (randomCollection == null) {
randomCollection = new RandomCollection<>();
structuresByGroup.put(group, randomCollection);
}
AbstractStructure abstractStructure = randomCollection.getNamedEntry(name);
if (abstractStructure == null) {
switch (group) {
case GROUP_STARS:
abstractStructure = new Star(group, name);
break;
case GROUP_MOONS:
abstractStructure = new Orb(group, name);
break;
default:
abstractStructure = new MetaOrb(group, name);
break;
}
}
randomCollection.loadFromXML(abstractStructure, elementStructure);
String name = elementStructure.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException("Structure " + location + " is missing a name attribute!");
}
WarpDrive.logger.info("- found Structure " + group + ":" + name);
XmlRandomCollection<AbstractStructure> xmlRandomCollection = structuresByGroup.computeIfAbsent(group, k -> new XmlRandomCollection<>());
AbstractStructure abstractStructure = xmlRandomCollection.getNamedEntry(name);
if (abstractStructure == null) {
switch (group) {
case GROUP_STARS:
abstractStructure = new Star(group, name);
break;
case GROUP_MOONS:
abstractStructure = new Orb(group, name);
break;
default:
abstractStructure = new MetaOrb(group, name);
break;
}
}
xmlRandomCollection.loadFromXML(abstractStructure, elementStructure);
}
public static AbstractStructure getStructure(Random random, final String group, final String name) {
@ -123,25 +76,28 @@ public class StructureManager {
return null;
}
RandomCollection<AbstractStructure> randomCollection = structuresByGroup.get(group);
if (randomCollection == null) {
// @TODO XML configuration for Asteroids Fields
if (group.equals(GROUP_ASTEROIDS_FIELDS)) {
return new AsteroidField(null, null);
}
XmlRandomCollection<AbstractStructure> xmlRandomCollection = structuresByGroup.get(group);
if (xmlRandomCollection == null) {
return null;
}
if (name == null || name.isEmpty()) {
return randomCollection.getRandomEntry(random);
return xmlRandomCollection.getRandomEntry(random);
} else {
return randomCollection.getNamedEntry(name);
return xmlRandomCollection.getNamedEntry(name);
}
}
public static String getStructureNames(final String group) {
if (group == null || group.isEmpty()) {
// no operation
} else {
RandomCollection<AbstractStructure> randomCollection = structuresByGroup.get(group);
if (randomCollection != null) {
return randomCollection.getNames();
if (group != null && !group.isEmpty()) {
XmlRandomCollection<AbstractStructure> xmlRandomCollection = structuresByGroup.get(group);
if (xmlRandomCollection != null) {
return xmlRandomCollection.getNames();
}
}
return "Error: group '" + group + "' isn't defined. Try one of: " + StringUtils.join(structuresByGroup.keySet(), ", ");

View file

@ -26,7 +26,7 @@ public class StructureReference extends AbstractStructure {
}
@Override
public AbstractInstance instantiate(Random random) {
public AbstractStructureInstance instantiate(Random random) {
return StructureManager.getStructure(random, group, name).instantiate(random);
}
}

View file

@ -1,40 +1,64 @@
package cr0s.warpdrive.data;
import cr0s.warpdrive.Commons;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.api.IStringSerializable;
import cr0s.warpdrive.config.CelestialObjectManager;
import cr0s.warpdrive.config.InvalidXmlException;
import cr0s.warpdrive.config.RandomCollection;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.XmlFileManager;
import cr0s.warpdrive.config.structures.StructureGroup;
import org.w3c.dom.Element;
import java.util.NavigableMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.TreeMap;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ResourceLocation;
/**
* Celestial objects are area in space. They can be a planet or solar system (space dimension) or the all mighty hyperspace.
* An astronomical object or celestial object is a naturally occurring physical entity, association, or structure in the observable universe.
* They can be a planet, a more abstract construct like solar system (space dimension) or the all mighty hyperspace.
*
* @author LemADEC
*/
public class CelestialObject implements Cloneable {
public class CelestialObject implements Cloneable, IStringSerializable {
public static final double GRAVITY_NONE = 0.0D;
public static final double GRAVITY_LEGACY_SPACE = -1.0D;
public static final double GRAVITY_LEGACY_HYPERSPACE = -2.0D;
public static final double GRAVITY_NORMAL = 1.0D;
public String group;
public String name;
public boolean isVirtual;
public int dimensionId;
public int dimensionCenterX, dimensionCenterZ;
public int borderRadiusX, borderRadiusZ;
public String parentGroup;
public String parentName;
public int parentDimensionId;
public int parentCenterX, parentCenterZ;
public boolean isWarpDrive;
public boolean isProvidedByWarpDrive;
public double gravity;
public boolean isBreathable;
// @TODO replace with RandomCollection once we switch to XML
private final NavigableMap<Double, String> mapGenerationRatios = new TreeMap<>();
private double totalRatio;
private final RandomCollection<StructureGroup> randomStructures = new RandomCollection<>();
public LinkedHashSet<RenderData> setRenderData;
public CelestialObject() {
this(0, 0, 0, 5000, 5000, -2, 0, 0);
public CelestialObject(final String location, final String parentElementGroup, final String parentElementName, Element elementCelestialObject) throws InvalidXmlException {
loadFromXmlElement(location, parentElementGroup, parentElementName, elementCelestialObject);
}
public CelestialObject(final int parDimensionId, final int parDimensionCenterX, final int parDimensionCenterZ,
final int parBorderRadiusX, final int parBorderRadiusZ,
final int parParentDimensionId, final int parParentCenterX, final int parParentCenterZ) {
isVirtual = false;
dimensionId = parDimensionId;
dimensionCenterX = parDimensionCenterX;
dimensionCenterZ = parDimensionCenterZ;
@ -49,50 +73,196 @@ public class CelestialObject implements Cloneable {
readFromNBT(nbt);
}
@Override
public String getName() {
return name;
}
public String getFullName() {
return String.format("%s:%s", group, name);
}
public boolean loadFromXmlElement(final String location, final String parentElementGroup, final String parentElementName, Element elementCelestialObject) throws InvalidXmlException {
// get identity
group = elementCelestialObject.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException(String.format("Celestial object %s is missing a group attribute!", location));
}
name = elementCelestialObject.getAttribute("name");
if (name.isEmpty()) {
throw new InvalidXmlException(String.format("Celestial object %s is missing a name attribute!", location));
}
WarpDrive.logger.info("- found Celestial object " + getFullName());
// get optional parent element, defaulting to parent defined by element hierarchy
parentGroup = parentElementGroup;
parentName = parentElementName;
List<Element> listParents = XmlFileManager.getChildrenElementByTagName(elementCelestialObject,"parent");
if (listParents.size() > 1) {
throw new InvalidXmlException(String.format("Celestial object %s can only have up to one parent element", getFullName()));
}
if (listParents.size() == 1) {
Element elementParent = listParents.get(0);
// save linked parent
String parentGroupRead = elementParent.getAttribute("group");
String parentNameRead = elementParent.getAttribute("name");
if (!parentNameRead.isEmpty()) {
parentName = parentNameRead;
if (!parentGroupRead.isEmpty()) {
parentGroup = parentGroupRead;
}
} else if (!parentGroupRead.isEmpty()) {
throw new InvalidXmlException(String.format("Celestial object %s parent can't have a group without a name", getFullName()));
}
// get required center element
List<Element> listCenters = XmlFileManager.getChildrenElementByTagName(elementParent, "center");
if (listCenters.size() != 1) {
throw new InvalidXmlException(String.format("Celestial object %s parent requires exactly one center element", getFullName()));
}
Element elementCenter = listCenters.get(0);
parentCenterX = Integer.parseInt(elementCenter.getAttribute("x"));
parentCenterZ = Integer.parseInt(elementCenter.getAttribute("z"));
}
// get required size element
{
List<Element> listSizes = XmlFileManager.getChildrenElementByTagName(elementCelestialObject, "size");
if (listSizes.size() != 1) {
throw new InvalidXmlException(String.format("Celestial object %s requires exactly one size element", getFullName()));
}
Element elementSize = listSizes.get(0);
borderRadiusX = Integer.parseInt(elementSize.getAttribute("x")) / 2;
borderRadiusZ = Integer.parseInt(elementSize.getAttribute("z")) / 2;
}
// get optional dimension element
List<Element> listDimensions = XmlFileManager.getChildrenElementByTagName(elementCelestialObject, "dimension");
if (listDimensions.size() > 1) {
throw new InvalidXmlException(String.format("Celestial object %s can only have up to one dimension element", getFullName()));
}
if (listDimensions.size() == 0) {
isVirtual = true;
dimensionId = 0;
isProvidedByWarpDrive = false;
isBreathable = true;
gravity = GRAVITY_NORMAL;
dimensionCenterX = 0;
dimensionCenterZ = 0;
} else {
isVirtual = false;
Element elementDimension = listDimensions.get(0);
dimensionId = Integer.parseInt(elementDimension.getAttribute("id"));
isBreathable = Boolean.parseBoolean(elementDimension.getAttribute("isBreathable"));
gravity = parseGravity(elementDimension.getAttribute("gravity"));
if (elementDimension.hasAttribute("isProvidedByWarpDrive")) {
isProvidedByWarpDrive = Boolean.parseBoolean(elementDimension.getAttribute("isProvidedByWarpDrive"));
} else {
isProvidedByWarpDrive = isHyperspace() || isSpace();
}
// get required center element
List<Element> listCenters = XmlFileManager.getChildrenElementByTagName(elementDimension, "center");
if (listCenters.size() != 1) {
throw new InvalidXmlException( String.format("Celestial object %s dimension requires exactly one center element", getFullName()));
}
Element elementCenter = listCenters.get(0);
dimensionCenterX = Integer.parseInt(elementCenter.getAttribute("x"));
dimensionCenterZ = Integer.parseInt(elementCenter.getAttribute("z"));
// get optional generate element(s)
List<Element> listGenerates = XmlFileManager.getChildrenElementByTagName(elementCelestialObject, "generate");
for (int indexElement = 0; indexElement < listGenerates.size(); indexElement++) {
Element elementGenerate = listGenerates.get(indexElement);
String locationGenerate = String.format("Celestial object %s generate %d/%d", getFullName(), indexElement + 1, listGenerates.size());
parseGenerateElement(locationGenerate, elementGenerate);
}
// get optional effect element(s)
// @TODO not implemented
WarpDrive.logger.info(" loaded " + this);
}
// get optional render element(s)
List<Element> listRenders = XmlFileManager.getChildrenElementByTagName(elementCelestialObject, "render");
setRenderData = new LinkedHashSet<>(listRenders.size());
if (!listRenders.isEmpty()) {
for (int indexElement = 0; indexElement < listRenders.size(); indexElement++) {
Element elementRender = listRenders.get(indexElement);
String locationRender = String.format("Celestial object %s generate %d/%d", getFullName(), indexElement + 1, listRenders.size());
RenderData renderData = new RenderData(locationRender, elementRender);
setRenderData.add(renderData);
}
}
return true;
}
private static double parseGravity(final String stringGravity) {
try {
switch(stringGravity) {
case "none" : return GRAVITY_NONE;
case "legacySpace" : return GRAVITY_LEGACY_SPACE;
case "legacyHyperspace": return GRAVITY_LEGACY_HYPERSPACE;
case "normal" : return GRAVITY_NORMAL;
default:
double gravity = Double.parseDouble(stringGravity);
if (gravity < 0) {
throw new RuntimeException();
}
return gravity;
}
} catch (Exception exception) {
WarpDrive.logger.error("Invalid gravity value, expecting none, legacySpace, legacyHyperspace, normal or a positive double. Found: " + stringGravity);
exception.printStackTrace();
return 1.0D;
}
}
private void parseGenerateElement(final String location, final Element elementGenerate) throws InvalidXmlException {
final String group = elementGenerate.getAttribute("group");
if (group.isEmpty()) {
throw new InvalidXmlException(location + " is missing a group attribute!");
}
final String name = elementGenerate.getAttribute("name");
if (WarpDriveConfig.LOGGING_WORLD_GENERATION) {
WarpDrive.logger.info(" + found Generate " + group + ":" + name);
}
final String stringRatio = elementGenerate.getAttribute("ratio");
final String stringWeight = elementGenerate.getAttribute("weight");
StructureGroup structureGroup = new StructureGroup(group, name);
randomStructures.add(structureGroup, stringRatio, stringWeight);
}
public void resolveParent() {
// is it an hyperspace/top level dimension?
if (parentGroup.isEmpty() && parentName.isEmpty()) {
parentDimensionId = dimensionId;
} else {
CelestialObject celestialObjectParent = CelestialObjectManager.get(parentGroup, parentName);
if (celestialObjectParent != null) {
parentDimensionId = celestialObjectParent.dimensionId;
}
}
}
@SuppressWarnings("CloneDoesntCallSuperClone")
@Override
public CelestialObject clone() {
return new CelestialObject(dimensionId, dimensionCenterX, dimensionCenterZ, borderRadiusX, borderRadiusZ, parentDimensionId, parentCenterX, parentCenterZ);
}
public void setSelfProvider(final boolean isWarpDrive) {
this.isWarpDrive = isWarpDrive;
}
public void setGravity(final double gravity) {
this.gravity = gravity;
}
public void setBreathable(final boolean isBreathable) {
this.isBreathable = isBreathable;
}
public void addGenerationRatio(final double ratio, final String name) {
if (ratio <= 0 || ratio >= 1.0) {
WarpDrive.logger.warn("Ratio isn't in ]0, 1.0] bounds, skipping " + name + " with ratio " + ratio);
return;
}
if (mapGenerationRatios.containsValue(name)) {
WarpDrive.logger.warn("Object already has a ratio defined, skipping " + name + " with ratio " + ratio);
return;
}
if (totalRatio + ratio > 1.0) {
WarpDrive.logger.warn("Total ratio is greater than 1.0, skipping " + name + " with ratio " + ratio);
return;
}
totalRatio += ratio;
mapGenerationRatios.put(totalRatio, name);
}
public String getRandomGeneration(Random random) {
double value = random.nextDouble();
if (value >= totalRatio) {
return null;
}
return mapGenerationRatios.ceilingEntry(value).getValue();
public StructureGroup getRandomStructure(Random random, final int x, final int z) {
return randomStructures.getRandomEntry(random);
}
public AxisAlignedBB getWorldBorderArea() {
@ -221,7 +391,7 @@ public class CelestialObject implements Cloneable {
parentDimensionId = tag.getInteger("parentDimensionId");
parentCenterX = tag.getInteger("parentCenterX");
parentCenterZ = tag.getInteger("parentCenterZ");
isWarpDrive = tag.getBoolean("isWarpDrive");
isProvidedByWarpDrive = tag.getBoolean("isProvidedByWarpDrive");
gravity = tag.getDouble("gravity");
isBreathable = tag.getBoolean("isBreathable");
// @TODO: mapGenerationRatios
@ -236,7 +406,7 @@ public class CelestialObject implements Cloneable {
tag.setInteger("parentDimensionId", parentDimensionId);
tag.setInteger("parentCenterX", parentCenterX);
tag.setInteger("parentCenterZ", parentCenterZ);
tag.setBoolean("isWarpDrive", isWarpDrive);
tag.setBoolean("isProvidedByWarpDrive", isProvidedByWarpDrive);
tag.setDouble("gravity", gravity);
tag.setBoolean("isBreathable", isBreathable);
// @TODO: mapGenerationRatios
@ -266,9 +436,53 @@ public class CelestialObject implements Cloneable {
@Override
public String toString() {
return "CelestialObject [Dimension " + dimensionId + " @ (" + dimensionCenterX + " " + dimensionCenterZ + ")"
+ " Border(" + borderRadiusX + " " + borderRadiusZ + ")"
+ " Parent(" + parentDimensionId + " @ (" + parentCenterX + " " + parentCenterZ + "))]"
+ " isWarpDrive + " + isWarpDrive + " gravity " + gravity + " isBreathable " + isBreathable;
return String.format("CelestialObject %s:%s [Dimension %d @ %d %d Border(%d %d) Parent(%d @ %d %d) isProvidedByWarpDrive %s gravity %.3f isBreathable %s]",
group, name, dimensionId, dimensionCenterX, dimensionCenterZ,
borderRadiusX, borderRadiusZ,
parentDimensionId, parentCenterX, parentCenterZ,
isProvidedByWarpDrive, gravity, isBreathable);
}
public class RenderData {
public float red;
public float green;
public float blue;
public float alpha;
public String texture;
public ResourceLocation resourceLocation;
public double periodU;
public double periodV;
public boolean isAdditive;
RenderData(final String location, final Element elementRender) throws InvalidXmlException {
try {
red = Commons.clamp(0.0F, 1.0F, Float.parseFloat(elementRender.getAttribute("red")));
green = Commons.clamp(0.0F, 1.0F, Float.parseFloat(elementRender.getAttribute("green")));
blue = Commons.clamp(0.0F, 1.0F, Float.parseFloat(elementRender.getAttribute("blue")));
alpha = Commons.clamp(0.0F, 1.0F, Float.parseFloat(elementRender.getAttribute("alpha")));
} catch (Exception exception) {
exception.printStackTrace();
WarpDrive.logger.error("Exception while parsing Render element at " + location);
red = 0.5F;
green = 0.5F;
blue = 0.5F;
alpha = 0.5F;
}
texture = elementRender.getAttribute("texture");
if (texture == null || texture.isEmpty()) {
texture = null;
resourceLocation = null;
periodU = 1.0F;
periodV = 1.0F;
isAdditive = false;
} else {
resourceLocation = new ResourceLocation(texture);
periodU = Commons.clampMantisse(0.001D, 1000000.0D, Double.parseDouble(elementRender.getAttribute("periodU")));
periodV = Commons.clampMantisse(0.001D, 1000000.0D, Double.parseDouble(elementRender.getAttribute("periodV")));
isAdditive = Boolean.parseBoolean(elementRender.getAttribute("additive"));
}
}
}
}

View file

@ -5,6 +5,7 @@ import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.api.IStarMapRegistryTileEntity;
import cr0s.warpdrive.block.movement.TileEntityShipCore;
import cr0s.warpdrive.block.movement.TileEntityShipCore.EnumShipCoreMode;
import cr0s.warpdrive.config.CelestialObjectManager;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.data.StarMapRegistryItem.EnumStarMapEntryType;
@ -113,7 +114,7 @@ public class StarMapRegistry {
public static CelestialObject getCelestialObject(final int dimensionId, final int x, final int z) {
double distanceClosest = Double.POSITIVE_INFINITY;
CelestialObject celestialObjectClosest = null;
for (CelestialObject celestialObject : WarpDriveConfig.celestialObjects) {
for (CelestialObject celestialObject : CelestialObjectManager.celestialObjects) {
if (dimensionId == celestialObject.dimensionId) {
final double distanceSquared = celestialObject.getSquareDistanceOutsideBorder(dimensionId, x, z);
if (distanceSquared <= 0) {
@ -135,7 +136,7 @@ public class StarMapRegistry {
public static CelestialObject getClosestParentCelestialObject(final int dimensionId, final int x, final int z) {
double closestPlanetDistance = Double.POSITIVE_INFINITY;
CelestialObject celestialObjectClosest = null;
for (CelestialObject celestialObject : WarpDriveConfig.celestialObjects) {
for (CelestialObject celestialObject : CelestialObjectManager.celestialObjects) {
final double distanceSquared = celestialObject.getSquareDistanceOutsideBorder(dimensionId, x, z);
if (distanceSquared <= 0) {
return celestialObject;
@ -150,7 +151,7 @@ public class StarMapRegistry {
public static CelestialObject getClosestChildCelestialObject(final int dimensionId, final int x, final int z) {
double closestPlanetDistance = Double.POSITIVE_INFINITY;
CelestialObject celestialObjectClosest = null;
for (CelestialObject celestialObject : WarpDriveConfig.celestialObjects) {
for (CelestialObject celestialObject : CelestialObjectManager.celestialObjects) {
final double distanceSquared = celestialObject.getSquareDistanceInParent(dimensionId, x, z);
if (distanceSquared <= 0.0D) {
return celestialObject;

View file

@ -165,11 +165,20 @@ public class LivingHandler {
if (entity instanceof EntityPlayerMP) {
EntityPlayerMP player = (EntityPlayerMP) entity;
// add tolerance to fall distance
player.fallDistance = -5.0F;
// transfer player to new dimension
player.mcServer.getConfigurationManager().transferPlayerToDimension(player, celestialObjectChild.dimensionId,
new SpaceTeleporter(worldTarget, 0, x, yTarget, z));
if (celestialObjectChild.hasAtmosphere()) {
// add fire if we're entering an atmosphere
if (!celestialObject.hasAtmosphere() && celestialObjectChild.hasAtmosphere()) {
player.setFire(30);
}
// close player transfer
player.setPositionAndUpdate(entity.posX, yTarget, entity.posZ);
player.sendPlayerAbilities();
}

View file

@ -1,8 +1,10 @@
package cr0s.warpdrive.render;
import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.CelestialObjectManager;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.data.CelestialObject;
import cr0s.warpdrive.data.CelestialObject.RenderData;
import java.awt.Color;
import java.util.Random;
@ -12,7 +14,6 @@ import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.util.MathHelper;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Vec3;
import org.lwjgl.opengl.GL11;
@ -30,15 +31,6 @@ public class RenderSpaceSky extends IRenderHandler {
return INSTANCE;
}
private static final ResourceLocation[] texturePlanets = {
new ResourceLocation("warpdrive:textures/celestial/planet_icy.png"),
new ResourceLocation("warpdrive:textures/celestial/planet_magma.png"),
new ResourceLocation("warpdrive:textures/celestial/planet_metallic.png"),
new ResourceLocation("warpdrive:textures/celestial/planet_oceanic.png"),
new ResourceLocation("warpdrive:textures/celestial/planet_temperate.png")
};
private static final ResourceLocation textureStar = new ResourceLocation("warpdrive:textures/celestial/star_yellow.png");
public static final int callListStars = GLAllocation.generateDisplayLists(3);
public static final int callListUpperSkyBox = callListStars + 1;
public static final int callListBottomSkyBox = callListStars + 2;
@ -146,6 +138,7 @@ public class RenderSpaceSky extends IRenderHandler {
GL11.glDisable(GL11.GL_ALPHA_TEST);
// Star
/*
{
GL11.glPushMatrix();
final double starScale = isSpace ? 30.0D : 40.0D;
@ -164,6 +157,7 @@ public class RenderSpaceSky extends IRenderHandler {
tessellator.draw();
GL11.glPopMatrix();
}
/**/
// CelestialObject
/*
@ -192,7 +186,7 @@ public class RenderSpaceSky extends IRenderHandler {
/**/
// Planets
for(CelestialObject celestialObject : WarpDriveConfig.celestialObjects) {
for(CelestialObject celestialObject : CelestialObjectManager.celestialObjects) {
renderCelestialObject(tessellator, celestialObject, isSpace, mc.thePlayer.getEntityWorld().provider.dimensionId, playerCoordinates);
}
@ -291,7 +285,7 @@ public class RenderSpaceSky extends IRenderHandler {
final double planetY = planetY_far * transitionApproaching;
// render range is only used for Z-ordering
final double renderRange = 180.0D + 10.0D * (distanceToCenter / Math.max(celestialObject.borderRadiusX, celestialObject.borderRadiusZ));
double renderRange = 180.0D + 10.0D * (distanceToCenter / Math.max(celestialObject.borderRadiusX, celestialObject.borderRadiusZ));
// render size is 1 at space border range
// render size is 10 at approaching range
@ -300,7 +294,7 @@ public class RenderSpaceSky extends IRenderHandler {
final double renderSize = 100.0D / 1000.0D * Math.min(1000.0D, Math.max(celestialObject.borderRadiusX, celestialObject.borderRadiusZ)) * (1.0D - transitionOrbit)
+ 50.0D * (transitionOrbit < 1.0D ? transitionOrbit : (1.0D - transitionApproaching))
+ 5.0D * (transitionApproaching < 1.0D ? transitionApproaching : (1.0D - transitionFar))
+ 1.0D * transitionFar;
+ 2.0D * transitionFar;
// angles
@SuppressWarnings("SuspiciousNameCombination")
@ -311,7 +305,9 @@ public class RenderSpaceSky extends IRenderHandler {
final double angleS = 0.15D * celestialObject.dimensionId * transitionApproaching // + (world.getTotalWorldTime() + partialTicks) * Math.PI / 6000.0D;
+ angleH * (1.0D - transitionApproaching);
if (celestialObject.dimensionId == 1 && (Minecraft.getSystemTime() / 10) % 100 == 0) {
if ( WarpDriveConfig.LOGGING_RENDERING
&& celestialObject.dimensionId == 1
&& (Minecraft.getSystemTime() / 10) % 100 == 0) {
WarpDrive.logger.info(String.format("transition Far %.2f Approaching %.2f Orbit %.2f distanceToCenter %.3f %.3f offset %.3f %.3f angle H %.3f V_far %.3f V %.3f S %.3f",
transitionFar, transitionApproaching, transitionOrbit, distanceToCenterX, distanceToCenterZ, offsetX, offsetZ, angleH, angleV_far, angleV, angleS));
}
@ -325,21 +321,54 @@ public class RenderSpaceSky extends IRenderHandler {
final double cosS = Math.cos(angleS);
GL11.glPushMatrix();
GL11.glColor4f(1.0F, 1.0F, 1.0F, isSpace ? 1.0F : 0.2F);
FMLClientHandler.instance().getClient().renderEngine.bindTexture(texturePlanets[Math.abs(celestialObject.dimensionId) % texturePlanets.length]);
tessellator.startDrawingQuads();
for (int indexVertex = 0; indexVertex < 4; indexVertex++) {
final double offset1 = ((indexVertex & 2) - 1) * renderSize;
final double offset2 = ((indexVertex + 1 & 2) - 1) * renderSize;
final double valV = offset1 * cosS - offset2 * sinS;
final double valH = offset2 * cosS + offset1 * sinS;
final double y = valV * sinV + renderRange * cosV;
final double valD = renderRange * sinV - valV * cosV;
final double x = valD * sinH - valH * cosH + renderSize * offsetX;
final double z = valH * sinH + valD * cosH + renderSize * offsetZ;
tessellator.addVertexWithUV(x, y, z, (indexVertex & 2) / 2, (indexVertex + 1 & 2) / 2);
// GL11.glEnable(GL11.GL_BLEND); // by caller
final double time = Minecraft.getSystemTime() / 1000.0D;
for (RenderData renderData : celestialObject.setRenderData) {
// compute texture offsets for clouds animation
final float offsetU = (float) ( Math.signum(renderData.periodU) * ((time / Math.abs(renderData.periodU)) % 1.0D) );
final float offsetV = (float) ( Math.signum(renderData.periodV) * ((time / Math.abs(renderData.periodV)) % 1.0D) );
// apply rendering parameters
GL11.glColor4f(renderData.red, renderData.green, renderData.blue, renderData.alpha * (isSpace ? 1.0F : 0.2F));
if (renderData.texture != null) {
GL11.glEnable(GL11.GL_TEXTURE_2D);
FMLClientHandler.instance().getClient().renderEngine.bindTexture(renderData.resourceLocation);
} else {
GL11.glDisable(GL11.GL_TEXTURE_2D);
}
if (renderData.isAdditive) {
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
} else {
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
}
// draw current layer
tessellator.startDrawingQuads();
for (int indexVertex = 0; indexVertex < 4; indexVertex++) {
final double offset1 = ((indexVertex & 2) - 1) * renderSize;
final double offset2 = ((indexVertex + 1 & 2) - 1) * renderSize;
final double valV = offset1 * cosS - offset2 * sinS;
final double valH = offset2 * cosS + offset1 * sinS;
final double y = valV * sinV + renderRange * cosV;
final double valD = renderRange * sinV - valV * cosV;
final double x = valD * sinH - valH * cosH + renderSize * offsetX;
final double z = valH * sinH + valD * cosH + renderSize * offsetZ;
if (renderData.texture != null) {
tessellator.addVertexWithUV(x, y, z, (indexVertex & 2) / 2 + offsetU, (indexVertex + 1 & 2) / 2 + offsetV);
} else {
tessellator.addVertex(x, y, z);
}
}
tessellator.draw();
// slight offset to get volumetric illusion
renderRange -= 5.0D;
}
tessellator.draw();
// restore settings
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glPopMatrix();
}

View file

@ -5,8 +5,10 @@ import cr0s.warpdrive.WarpDrive;
import cr0s.warpdrive.config.WarpDriveConfig;
import cr0s.warpdrive.config.filler.Filler;
import cr0s.warpdrive.config.structures.AbstractStructure;
import cr0s.warpdrive.config.structures.AbstractStructureInstance;
import cr0s.warpdrive.config.structures.Orb.OrbShell;
import cr0s.warpdrive.config.structures.OrbInstance;
import cr0s.warpdrive.config.structures.StructureGroup;
import cr0s.warpdrive.config.structures.StructureManager;
import cr0s.warpdrive.data.CelestialObject;
import cr0s.warpdrive.data.StarMapRegistry;
@ -38,179 +40,17 @@ public class SpaceWorldGenerator implements IWorldGenerator {
int y = WarpDriveConfig.SPACE_GENERATOR_Y_MIN_CENTER
+ random.nextInt(WarpDriveConfig.SPACE_GENERATOR_Y_MAX_CENTER - WarpDriveConfig.SPACE_GENERATOR_Y_MIN_CENTER);
String group = celestialObject.getRandomGeneration(random);
if (group == null) {
StructureGroup structureGroup = celestialObject.getRandomStructure(random, x, z);
if (structureGroup == null) {
return;
}
switch (group) {
case "moon":
AbstractStructure moon = StructureManager.getStructure(world.rand, StructureManager.GROUP_MOONS, null);
moon.generate(world, world.rand, x, y, z);
break;
case "asteroid":
AbstractStructure asteroid = StructureManager.getStructure(world.rand, StructureManager.GROUP_ASTEROIDS, null);
asteroid.generate(world, world.rand, x, y, z);
break;
case "asteroidField":
generateAsteroidField(world, x, y, z);
break;
default:
break;
}
AbstractStructureInstance abstractStructureInstance = structureGroup.instantiate(random);
abstractStructureInstance.generate(world, random, x, y, z);
} catch (Exception exception) {
exception.printStackTrace();
}
}
private static void generateSmallShip(World world, int x, int y, int z, int jitter) {
int x2 = x + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int y2 = y + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int z2 = z + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
WarpDrive.logger.info("Generating small ship at " + x2 + "," + y2 + "," + z2);
new WorldGenSmallShip(world.rand.nextFloat() > 0.2F).generate(world, world.rand, x2, y2, z2);
}
private static void generateStation(World world, int x, int y, int z, int jitter) {
int x2 = x + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int y2 = y + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
int z2 = z + (((world.rand.nextBoolean()) ? -1 : 1) * world.rand.nextInt(jitter));
WarpDrive.logger.info("Generating small ship at " + x2 + "," + y2 + "," + z2);
new WorldGenStation(world.rand.nextBoolean()).generate(world, world.rand, x2, y2, z2);
}
private static float binomialRandom(World world) {
float linear = world.rand.nextFloat();
// ideal sphere repartition = x ^ 0.5 (sqrt)
// Dilution but slow to compute = 0.5 * ( x ^ 0.3 + 1 + (x - 1) ^ 3 )
// Optimized 'pushed out' form = 1.25 - 0.625 / (0.5 + 2 * x)
// Natural sphere with ring = (1 - x ^ 2.5) * x ^ 0.5 + x ^ 4
// rectangular approach: return 0.5F * linear + 0.5F * linear * linear;
return 1.25F - 0.625F / (0.5F + 2.0F * linear);
}
public static void generateAsteroidField(World world, int x, int y1, int z) {
LocalProfiler.start("SpaceWorldGenerator.generateAsteroidField");
// 6.0.1 au = 120 radius with 60 to 140 big + 60 to 140 small + 5 to 13 gaz
// 45238 blocks surface with 120 to 280 asteroids => 161 to 376 blocks per asteroid (big & small)
// 6.0.2 av big = 80 to 180 radius with 40 to 90 big + 80 to 200 small + 5 to 13 gaz
// 20106 to 101787 surface with 120 to 290 asteroids => 69 to 848 blocks per asteroid
// 6.0.2 av small = 30 to 80 radius with 2 to 22 big + 15 to 75 small + 0 to 3 gaz
// 2827 to 20106 surface with 17 to 97 asteroids => 29 to 1182 blocks per asteroid
// random distanced one = 89727 surface 256 asteroids => 350 blocks per asteroid
/*
boolean isBig = world.rand.nextInt(3) == 1;
int numOfBigAsteroids, numOfSmallAsteroids, numOfClouds, maxDistance, maxHeight;
if (isBig) {
numOfBigAsteroids = 40 + world.rand.nextInt(50);
numOfSmallAsteroids = 80 + world.rand.nextInt(120);
numOfClouds = 5 + world.rand.nextInt(8);
maxDistance = 80 + world.rand.nextInt(100);
maxHeight = 40 + world.rand.nextInt(40);
} else {
numOfBigAsteroids = 2 + world.rand.nextInt(20);
numOfSmallAsteroids = 15 + world.rand.nextInt(60);
numOfClouds = 0 + world.rand.nextInt(3);
maxDistance = 30 + world.rand.nextInt(50);
maxHeight = 30 + world.rand.nextInt(30);
}
/**/
float surfacePerAsteroid = 80.0F + world.rand.nextFloat() * 300;
int maxDistance = 30 + world.rand.nextInt(170);
int maxDistanceBig = Math.round(maxDistance * (0.6F + 0.2F * world.rand.nextFloat()));
int maxDistanceSmall = Math.round(maxDistance * 1.1F);
float bigRatio = 0.3F + world.rand.nextFloat() * 0.3F;
float surfaceBig = (float) (Math.PI * Math.pow(maxDistanceBig, 2));
float surfaceSmall = (float) (Math.PI * Math.pow(maxDistanceSmall, 2));
int numOfBigAsteroids = Math.round(bigRatio * surfaceBig / surfacePerAsteroid);
int numOfSmallAsteroids = Math.round((1.0F - bigRatio) * surfaceSmall / surfacePerAsteroid);
int numOfClouds = Math.round(numOfBigAsteroids * 1.0F / (10.0F + world.rand.nextInt(10)));
int maxHeight = 70 + world.rand.nextInt(50);
int y2 = Math.min(WarpDriveConfig.SPACE_GENERATOR_Y_MAX_BORDER - maxHeight,
Math.max(y1, WarpDriveConfig.SPACE_GENERATOR_Y_MIN_BORDER + maxHeight));
WarpDrive.logger.info("Generating asteroid field at " + x + "," + y2 + "," + z + " qty " + numOfBigAsteroids + ", " + numOfSmallAsteroids + ", "
+ numOfClouds + " over " + maxDistance + ", " + maxHeight + " surfacePerAsteroid " + String.format("%.1f", surfacePerAsteroid));
// Setting up of big asteroids
for (int i = 1; i <= numOfBigAsteroids; i++) {
float binomial = binomialRandom(world);
double bearing = world.rand.nextFloat() * 2.0D * Math.PI;
double yawn = world.rand.nextFloat() * Math.PI;
float horizontalRange = Math.max(6.0F, binomial * maxDistanceBig);
float verticalRange = Math.max(3.0F, binomial * maxHeight);
int aX = (int) (x + Math.round(horizontalRange * Math.cos(bearing)));
int aY = (int) (y2 + Math.round(verticalRange * Math.cos(yawn)));
int aZ = (int) (z + Math.round(horizontalRange * Math.sin(bearing)));
if (WarpDriveConfig.LOGGING_WORLDGEN) {
WarpDrive.logger.info(String.format("Big asteroid: %.3f %.3f r %.3f r makes %3d, %3d, %3d",
(double) binomial, bearing, yawn, aX, aY, aZ));
}
// Place an asteroid
AbstractStructure moon = StructureManager.getStructure(world.rand, StructureManager.GROUP_ASTEROIDS, null);
moon.generate(world, world.rand, aX, aY, aZ);
}
// Setting up small asteroids
for (int i = 1; i <= numOfSmallAsteroids; i++) {
float binomial = binomialRandom(world);
double bearing = world.rand.nextFloat() * 2.0D * Math.PI;
double yawn = world.rand.nextFloat() * Math.PI;
float horizontalRange = Math.max(6.0F, binomial * maxDistanceSmall);
float verticalRange = Math.max(3.0F, binomial * maxHeight);
int aX = (int) (x + Math.round(horizontalRange * Math.cos(bearing)));
int aY = (int) (y2 + Math.round(verticalRange * Math.cos(yawn)));
int aZ = (int) (z + Math.round(horizontalRange * Math.sin(bearing)));
// Placing
if (world.rand.nextInt(400) != 1) {
AbstractStructure moon = StructureManager.getStructure(world.rand, StructureManager.GROUP_ASTEROIDS, null);
moon.generate(world, world.rand, aX, aY, aZ);
} else {
if (world.rand.nextInt(20) != 1) {
generateSmallShip(world, aX, aY, aZ, 8);
} else {
generateStation(world, aX, aY, aZ, 8);
}
}
}
// Setting up gas clouds
for (int i = 1; i <= numOfClouds; i++) {
float binomial = binomialRandom(world);
double bearing = world.rand.nextFloat() * 2.0D * Math.PI;
double yawn = world.rand.nextFloat() * Math.PI;
float horizontalRange = Math.max(6.0F, binomial * maxDistanceBig);
float verticalRange = Math.max(3.0F, binomial * maxHeight);
int aX = (int) (x + Math.round(horizontalRange * Math.cos(bearing)));
int aY = (int) (y2 + Math.round(verticalRange * Math.cos(yawn)));
int aZ = (int) (z + Math.round(horizontalRange * Math.sin(bearing)));
// Placing
if (world.rand.nextBoolean()) {
AbstractStructure gasCloud = StructureManager.getStructure(world.rand, StructureManager.GROUP_GASCLOUDS, null);
if (gasCloud != null) {
gasCloud.generate(world, world.rand, aX, aY, aZ);
}
}
}
LocalProfiler.stop();
}
/**
*
* @deprecated reference design for EntitySphereGenerator

View file

@ -1,102 +1,216 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema elementFormDefault="qualified" targetNamespace="WarpDrive" xmlns="WarpDrive" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="forElement">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="for" type="forElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="fillerSet" type="fillerSetElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="import" type="importElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="filler" type="fillerElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="structure" type="structureElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="metashell" type="metashellElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="shell" type="shellElement" maxOccurs="unbounded" minOccurs="0" />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="for" type="forElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="fillerSet" type="fillerSetElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="import" type="importElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="filler" type="fillerElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="structure" type="structureElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="metashell" type="metashellElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="shell" type="shellElement" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
<xs:attribute type="xs:string" name="variable" use="required" />
<xs:attribute type="xs:string" name="from" use="optional" />
<xs:attribute type="xs:string" name="to" use="optional" />
<xs:attribute type="xs:string" name="in" use="optional" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="variable" type="xs:string" use="required" />
<xs:attribute name="from" type="xs:string" use="optional" />
<xs:attribute name="to" type="xs:string" use="optional" />
<xs:attribute name="in" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="fillerSetElement">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="for" type="forElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="import" type="importElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="filler" type="fillerElement" maxOccurs="unbounded" minOccurs="0" />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="for" type="forElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="import" type="importElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="filler" type="fillerElement" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
<xs:attribute type="xs:string" name="group" use="required" />
<xs:attribute type="xs:string" name="name" use="required" />
<xs:attribute type="xs:string" name="weight" use="optional" />
<xs:attribute type="xs:string" name="ratio" use="optional" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="group" type="xs:string" use="required" />
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="weight" type="xs:string" use="optional" />
<xs:attribute name="ratio" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="importElement">
<xs:attribute type="xs:string" name="group" use="required" />
<xs:attribute type="xs:string" name="name" use="optional" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="group" type="xs:string" use="required" />
<xs:attribute name="name" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="fillerElement">
<xs:attribute type="xs:string" name="block" use="required" />
<xs:attribute type="xs:string" name="metadata" use="optional" />
<xs:attribute type="xs:string" name="weight" use="optional" />
<xs:attribute type="xs:string" name="ratio" use="optional" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="block" type="xs:string" use="required" />
<xs:attribute name="metadata" type="xs:string" use="optional" />
<xs:attribute name="weight" type="xs:string" use="optional" />
<xs:attribute name="ratio" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="structureElement">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="for" type="forElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="import" type="importElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="schematic" type="schematicElement" maxOccurs="1" minOccurs="0" />
<xs:element name="metashell" type="metashellElement" maxOccurs="1" minOccurs="0" />
<xs:element name="shell" type="shellElement" maxOccurs="unbounded" minOccurs="0" />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="for" type="forElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="import" type="importElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="schematic" type="schematicElement" minOccurs="0" maxOccurs="1" />
<xs:element name="metashell" type="metashellElement" minOccurs="0" maxOccurs="1" />
<xs:element name="shell" type="shellElement" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
<xs:attribute type="xs:string" name="group" use="required" />
<xs:attribute type="xs:string" name="name" use="required" />
<xs:attribute type="xs:string" name="weight" use="optional" />
<xs:attribute type="xs:string" name="ratio" use="optional" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="group" type="xs:string" use="required" />
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="weight" type="xs:string" use="optional" />
<xs:attribute name="ratio" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="schematicElement">
<xs:attribute type="xs:string" name="group" use="required" />
<xs:attribute type="xs:string" name="name" use="optional" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="group" type="xs:string" use="required" />
<xs:attribute name="name" type="xs:string" use="optional" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="metashellElement">
<xs:attribute type="xs:string" name="block" use="optional" />
<xs:attribute type="xs:string" name="metadata" use="optional" />
<xs:attribute type="xs:string" name="minCount" use="required" />
<xs:attribute type="xs:string" name="maxCount" use="required" />
<xs:attribute type="xs:string" name="minRadius" use="required" />
<xs:attribute type="xs:string" name="relativeRadius" use="required" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="block" type="xs:string" use="optional" />
<xs:attribute name="metadata" type="xs:string" use="optional" />
<xs:attribute name="minCount" type="xs:string" use="required" />
<xs:attribute name="maxCount" type="xs:string" use="required" />
<xs:attribute name="minRadius" type="xs:string" use="required" />
<xs:attribute name="relativeRadius" type="xs:string" use="required" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="shellElement">
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="for" type="forElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="import" type="importElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="filler" type="fillerElement" maxOccurs="unbounded" minOccurs="0" />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="for" type="forElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="import" type="importElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="filler" type="fillerElement" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
<xs:attribute type="xs:string" name="name" use="required" />
<xs:attribute type="xs:string" name="minThickness" use="required" />
<xs:attribute type="xs:string" name="maxThickness" use="required" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="minThickness" type="xs:string" use="required" />
<xs:attribute name="maxThickness" type="xs:string" use="required" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="celestialObjectElement">
<xs:sequence>
<xs:element name="parent" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:annotation>
<xs:documentation xml:lang="en">
Coordinates in parent celestial object
Optional: when not defined, current celestial object is an hyperspace dimension.
</xs:documentation>
</xs:annotation>
<xs:sequence>
<xs:element name="center" type="positionElement" minOccurs="1" maxOccurs="1" />
</xs:sequence>
<xs:attribute name="name" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
<xs:element name="size" minOccurs="1" maxOccurs="1">
<xs:complexType>
<xs:attribute name="x" type="worldSizeType" use="required" />
<xs:attribute name="z" type="worldSizeType" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="dimension" type="dimensionElement" minOccurs="0" maxOccurs="1" />
<xs:element name="render" type="renderElement" minOccurs="0" maxOccurs="unbounded" />
<xs:choice minOccurs="0" maxOccurs="1">
<xs:element name="for" type="forElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="celestialObject" type="celestialObjectElement" minOccurs="0" maxOccurs="unbounded" />
</xs:choice>
</xs:sequence>
<xs:attribute name="group" type="xs:string" use="required" />
<xs:attribute name="name" type="xs:string" use="required" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="dimensionElement">
<xs:annotation>
<xs:documentation xml:lang="en">
Defines the actual game world characteristics.
</xs:documentation>
</xs:annotation>
<xs:sequence minOccurs="1" maxOccurs="1">
<xs:element name="center" type="positionElement" minOccurs="1" maxOccurs="1" />
<xs:element name="generate" type="generateElement" minOccurs="0" maxOccurs="unbounded" />
<!-- <xs:element name="effects" type="effectElement" minOccurs="0" maxOccurs="unbounded" /> @TODO Not implemented -->
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="isProvidedByWarpDrive" type="xs:boolean" use="optional" default="false" />
<xs:attribute name="isBreathable" type="xs:boolean" use="required" />
<xs:attribute name="gravity" use="required">
<xs:simpleType>
<xs:union memberTypes="xs:double">
<xs:simpleType>
<xs:restriction base="xs:NMTOKEN">
<xs:enumeration value="none" />
<xs:enumeration value="legacySpace" />
<xs:enumeration value="legacyHyperspace" />
<xs:enumeration value="normal" />
</xs:restriction>
</xs:simpleType>
</xs:union>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
<xs:complexType name="positionElement">
<xs:attribute name="x" type="worldPositionType" use="required" />
<xs:attribute name="z" type="worldPositionType" use="required" />
</xs:complexType>
<xs:complexType name="renderElement">
<xs:attribute name="red" type="xs:float" use="required" />
<xs:attribute name="green" type="xs:float" use="required" />
<xs:attribute name="blue" type="xs:float" use="required" />
<xs:attribute name="alpha" type="xs:float" use="required" />
<xs:attribute name="texture" type="xs:string" use="optional" />
<xs:attribute name="periodU" type="xs:double" use="optional" default="0.0" />
<xs:attribute name="periodV" type="xs:double" use="optional" default="0.0" />
<xs:attribute name="additive" type="xs:boolean" use="optional" default="false" />
</xs:complexType>
<xs:complexType name="generateElement">
<xs:attribute name="group" type="xs:string" use="required" />
<xs:attribute name="ratio" type="xs:string" use="required" />
</xs:complexType>
<xs:simpleType name="worldSizeType">
<xs:restriction base="xs:unsignedInt">
<xs:maxInclusive value="2000000" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="worldPositionType">
<xs:annotation>
<xs:documentation xml:lang="en">
Vanilla block positions is limited to +/- 30000 km.
Vanilla entities positions are computed on simple float precision, which is 6 digits precision => 1000 km.
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:int">
<xs:minInclusive value="-1000000" />
<xs:maxInclusive value="1000000" />
</xs:restriction>
</xs:simpleType>
<xs:element name="worldGeneration">
<xs:complexType>
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element name="for" type="forElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="fillerSet" type="fillerSetElement" maxOccurs="unbounded" minOccurs="0" />
<xs:element name="structure" type="structureElement" maxOccurs="unbounded" minOccurs="0" />
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="for" type="forElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="fillerSet" type="fillerSetElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="structure" type="structureElement" minOccurs="0" maxOccurs="unbounded" />
<xs:element name="celestialObject" type="celestialObjectElement" minOccurs="0" maxOccurs="1" />
</xs:choice>
<xs:attribute type="xs:string" name="version" use="required" />
<xs:attribute type="xs:string" name="mods" use="optional" />
<xs:attribute name="version" type="xs:string" use="required" fixed="2" />
<xs:attribute name="mods" type="xs:string" use="optional" />
</xs:complexType>
</xs:element>
</xs:schema>

View file

@ -0,0 +1,131 @@
<?xml version="1.0" encoding="utf-8"?>
<worldGeneration version="2"
xmlns="WarpDrive"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="WarpDrive WarpDrive.xsd">
<!--
An astronomical object or celestial object is a naturally occurring physical entity, association, or structure in the observable universe.
They can be a planet, a more abstract construct like solar system (space dimension) or the all mighty hyperspace.
Hyperspace is a dimension which is its own parent. In other words, hyperspace.dimensionId = hyperspace.parentDimensionId. There can be only one.
A Space is a dimension with Hyperspace as its parent.
In theory, multiple planets can exists in the same minecraft world.
-->
<!-- Top level is hyperspace, typically a galaxy. -->
<celestialObject group="milkyWay" name="hyperspace">
<!--
side defines the world border size, measured in blocks. This is also the size of the orbit area in space, so don't go too big.
-->
<size x="100000" z="100000" />
<!--
dimension defines an actual game world. If it's missing, that celestialObject remains visible but you can't "land" on it.
dimension.id: this is the id of the dimension. 0 is the Overworld, -1 is the Nether, 1 is the End.
dimension.isBreathable: this is a boolean flag defining if ambient atmosphere is breathable.
dimension.gravity: this is the gravity simulation type. 0 is vanilla, 1 is space, 2 is hyperspace.
dimension.center.x, dimension.center.z: those are the center coordinate of that dimension world border, measured in blocks. For convenience, it's usually 0, 0.
dimension.isProvidedByWarpDrive (optional): this is a boolean flag defining if WarpDrive provides this dimension or not.
Currently only Space and Hyperspace can be provided: use other mods to generate planet world.
-->
<dimension id="-3" isBreathable="false" gravity="legacyHyperspace" isProvidedByWarpDrive="true">
<center x="0" z="0" />
</dimension>
<!-- Second level is space, typically a star system. -->
<celestialObject group="milkyWay" name="solarSystem">
<!--
parent defines the relation with a bigger enveloping celestial object.
parent.group, parent.name (optional): when using multiple files, you can attach to a parent by its group and name.
parent.center.x, parent.center.z: this is the center coordinates in the parent dimension, measured in blocks.
-->
<parent>
<center x="0" z="0" />
</parent>
<size x="200000" z="200000" />
<dimension id="-2" isBreathable="false" gravity="legacySpace" isProvidedByWarpDrive="true">
<center x="0" z="0" />
<!--
generate defines the chance of different structures to generate
generate.group, generate.name: identify the structure from the related XML files
Those only works in WarpDrive dimensions, they're ignored otherwise.
-->
<generate group="moon" ratio="0.008" />
<generate group="asteroid" ratio="0.0015" />
<generate group="asteroidField" ratio="0.006" />
</dimension>
<!-- Sun is just displayed, there's no actual game dimension -->
<celestialObject group="solarSystem" name="sun">
<parent>
<!-- sun is at the center of the solarSystem -->
<center x="0" z="0" />
</parent>
<size x="10000" z="10000" />
<!--
render defines several layers from surface to high atmosphere representing the planet.
red, green, blue: color mixing from 0.00 (black) to 1.00 (white)
alpha: transparency factor from 0.00 (invisible) to 1.00 (opaque)
texture: optional texture to use, can come from resource pack, vanilla or the mod itself
periodU, periodV: optional rotation period, measured in seconds, defaults to 0 (disabled)
additive: optional blending function, defaults to false (multiplicative)
-->
<render red="0.80" green="0.50" blue="0.20" alpha="1.00" texture="" />
<render red="0.80" green="0.70" blue="0.30" alpha="0.40" texture="warpdrive:textures/celestial/planet_icy.png" periodU="-20" periodV="104" additive="true" />
<render red="0.80" green="0.55" blue="0.10" alpha="0.48" texture="warpdrive:textures/celestial/planet_metallic.png" periodU="-40" periodV="140" additive="true" />
<render red="0.80" green="0.45" blue="0.30" alpha="0.34" texture="warpdrive:textures/celestial/planet_magma.png" periodU="24" periodV="-35" additive="true" />
<render red="0.80" green="0.50" blue="0.20" alpha="0.08" texture="" />
<render red="0.75" green="0.48" blue="0.20" alpha="0.08" texture="" />
<render red="0.70" green="0.55" blue="0.20" alpha="0.08" texture="" />
</celestialObject>
<!-- Earth is the overworld (dimension.id is 0) -->
<celestialObject group="solarSystem" name="earth">
<parent>
<!-- coordinates in the solar system, measured in blocks -->
<center x="-40000" z="20000" />
</parent>
<size x="4000" z="4000" />
<dimension id="0" isBreathable="true" gravity="normal">
<center x="0" z="0" />
</dimension>
<render red="0.70" green="0.70" blue="0.70" alpha="1.00" texture="warpdrive:textures/celestial/planet_temperate.png" />
<render red="0.90" green="0.95" blue="1.00" alpha="0.15" texture="warpdrive:textures/celestial/cloud_small.png" periodU="100" periodV="1100" additive="true" />
<render red="0.90" green="0.90" blue="1.00" alpha="0.15" texture="warpdrive:textures/celestial/cloud_medium.png" periodU="300" periodV="1500" additive="false" />
<render red="0.80" green="0.70" blue="1.00" alpha="0.15" texture="warpdrive:textures/celestial/cloud_large.png" periodU="500" periodV="2100" additive="false" />
<render red="0.50" green="0.50" blue="1.00" alpha="0.08" />
<render red="0.50" green="0.50" blue="1.00" alpha="0.08" />
<render red="0.50" green="0.50" blue="1.00" alpha="0.08" />
<!-- Hell is the nether. It's located below earth. In other words, falling below bedrock will drop you to the nether... -->
<celestialObject group="solarSystem" name="hell">
<parent>
<center x="0" z="0" />
</parent>
<size x="1000" z="1000" />
<dimension id="-1" isBreathable="true" gravity="normal">
<center x="0" z="0" />
</dimension>
</celestialObject>
</celestialObject>
<!-- Pluto is The End. It's a far planet. -->
<celestialObject group="solarSystem" name="pluto">
<parent>
<center x="90000" z="70000" />
</parent>
<size x="4000" z="4000" />
<dimension id="1" isBreathable="true" gravity="normal">
<center x="0" z="0" />
</dimension>
<render red="1.00" green="1.00" blue="1.00" alpha="1.00" texture="minecraft:textures/blocks/end_stone.png" periodU="150" />
<render red="0.50" green="0.50" blue="0.40" alpha="0.30" />
</celestialObject>
</celestialObject>
</celestialObject>
</worldGeneration>