diff --git a/src/main/java/biomesoplenty/api/config/IConfigObj.java b/src/main/java/biomesoplenty/api/config/IConfigObj.java index 0309a0ad5..69fe540ac 100644 --- a/src/main/java/biomesoplenty/api/config/IConfigObj.java +++ b/src/main/java/biomesoplenty/api/config/IConfigObj.java @@ -12,11 +12,13 @@ import java.util.List; import java.util.Set; import biomesoplenty.api.block.IBlockPosQuery; +import com.google.gson.JsonElement; import net.minecraft.block.state.IBlockState; import net.minecraft.util.ResourceLocation; public interface IConfigObj { + public JsonElement serializeDefaults(); public void addMessage(String message); public void addMessage(String extraPrefix, String message); public List flushMessages(); diff --git a/src/main/java/biomesoplenty/api/generation/IGenerationManager.java b/src/main/java/biomesoplenty/api/generation/IGenerationManager.java index b2c31a5d8..4037c1030 100644 --- a/src/main/java/biomesoplenty/api/generation/IGenerationManager.java +++ b/src/main/java/biomesoplenty/api/generation/IGenerationManager.java @@ -17,5 +17,5 @@ public interface IGenerationManager ImmutableCollection getGeneratorsForStage(GeneratorStage stage); void removeGenerator(String name); IGenerator getGenerator(String name); - void configureWith(String name, IConfigObj conf); + void configure(IConfigObj conf); } diff --git a/src/main/java/biomesoplenty/common/biome/BOPBiome.java b/src/main/java/biomesoplenty/common/biome/BOPBiome.java index 10fa1ca23..759ba6274 100644 --- a/src/main/java/biomesoplenty/common/biome/BOPBiome.java +++ b/src/main/java/biomesoplenty/common/biome/BOPBiome.java @@ -14,7 +14,6 @@ import biomesoplenty.api.config.IConfigObj; import biomesoplenty.api.enums.BOPClimates; import biomesoplenty.api.generation.GeneratorStage; import biomesoplenty.api.generation.IGenerator; -import biomesoplenty.common.biome.overworld.BOPOverworldBiome; import biomesoplenty.common.init.ModBiomes; import biomesoplenty.common.world.GenerationManager; import biomesoplenty.core.BiomesOPlenty; @@ -105,32 +104,23 @@ public abstract class BOPBiome extends Biome implements IExtendedBiome // Allow weights to be overridden IConfigObj confWeights = conf.getObject("weights"); - if (confWeights != null) + for (BOPClimates climate : BOPClimates.values()) { - for (BOPClimates climate : BOPClimates.values()) + Integer weight = confWeights.getInt(climate.name().toLowerCase(), this.weightMap.get(climate)); + if (weight == null) {continue;} + if (weight.intValue() < 1) { - Integer weight = confWeights.getInt(climate.name().toLowerCase(), null); - if (weight == null) {continue;} - if (weight.intValue() < 1) - { - this.weightMap.remove(climate); - } - else - { - this.weightMap.put(climate, weight); - } + this.weightMap.remove(climate); + } + else + { + this.weightMap.put(climate, weight); } } // Allow generators to be configured IConfigObj confGenerators = conf.getObject("generators"); - if (confGenerators != null) - { - for (String name : confGenerators.getKeys()) - { - this.generationManager.configureWith(name, confGenerators.getObject(name)); - } - } + this.generationManager.configure(confGenerators); // Allow spawnable entites to be configured ArrayList confEntities = conf.getObjectArray("entities"); diff --git a/src/main/java/biomesoplenty/common/biome/nether/BOPHellBiome.java b/src/main/java/biomesoplenty/common/biome/nether/BOPHellBiome.java index 7c83021c9..bc5e2cbce 100644 --- a/src/main/java/biomesoplenty/common/biome/nether/BOPHellBiome.java +++ b/src/main/java/biomesoplenty/common/biome/nether/BOPHellBiome.java @@ -17,6 +17,7 @@ import biomesoplenty.api.generation.GeneratorStage; import biomesoplenty.api.generation.IGenerationManager; import biomesoplenty.api.generation.IGenerator; import biomesoplenty.common.biome.BOPBiome; +import biomesoplenty.common.init.ModBiomes; import biomesoplenty.common.util.biome.GeneratorUtils; import biomesoplenty.common.world.generator.GeneratorHive; import biomesoplenty.common.world.generator.GeneratorSplatter; @@ -70,6 +71,9 @@ public class BOPHellBiome extends BOPBiome this.wallBlock = conf.getBlockState("wallBlock", this.wallBlock); this.roofTopBlock = conf.getBlockState("roofTopBlock", this.roofTopBlock); this.roofFillerBlock = conf.getBlockState("roofFillerBlock", this.roofFillerBlock); + + // write default values to a file + ModBiomes.writeDefaultConfigFile(ModBiomes.BOP_DEFAULTS_DIR, this.getResourceLocation().getResourcePath(), conf); } @Override diff --git a/src/main/java/biomesoplenty/common/biome/overworld/BOPOverworldBiome.java b/src/main/java/biomesoplenty/common/biome/overworld/BOPOverworldBiome.java index 879294efd..05f450394 100644 --- a/src/main/java/biomesoplenty/common/biome/overworld/BOPOverworldBiome.java +++ b/src/main/java/biomesoplenty/common/biome/overworld/BOPOverworldBiome.java @@ -34,6 +34,7 @@ import biomesoplenty.common.init.ModBiomes; import biomesoplenty.common.util.biome.BiomeUtils; import biomesoplenty.common.util.biome.GeneratorUtils.ScatterYMethod; import biomesoplenty.common.util.block.BlockQuery; +import biomesoplenty.common.util.config.BOPConfig; import biomesoplenty.common.world.GenerationManager; import biomesoplenty.common.world.TerrainSettings; import biomesoplenty.common.world.generator.GeneratorColumns; @@ -117,12 +118,14 @@ public class BOPOverworldBiome extends BOPBiome this.canGenerateRivers = conf.getBool("canGenerateRivers", this.canGenerateRivers); this.beachBiomeLocation = conf.getResourceLocation("beachBiomeLocation", this.beachBiomeLocation); + + // write default values to a file + ModBiomes.writeDefaultConfigFile(ModBiomes.BOP_DEFAULTS_DIR, this.getResourceLocation().getResourcePath(), conf); } @Override public void genTerrainBlocks(World world, Random rand, ChunkPrimer primer, int x, int z, double stoneNoiseVal) { - IBlockState topBlock = this.topBlock; IBlockState fillerBlock = this.fillerBlock; IBlockState seaFloorBlock = this.seaFloorBlock; diff --git a/src/main/java/biomesoplenty/common/biome/vanilla/ExtendedBiomeWrapper.java b/src/main/java/biomesoplenty/common/biome/vanilla/ExtendedBiomeWrapper.java index c2566a8e1..84f95c9ed 100644 --- a/src/main/java/biomesoplenty/common/biome/vanilla/ExtendedBiomeWrapper.java +++ b/src/main/java/biomesoplenty/common/biome/vanilla/ExtendedBiomeWrapper.java @@ -16,6 +16,7 @@ import biomesoplenty.api.enums.BOPPlants; import biomesoplenty.api.generation.GeneratorStage; import biomesoplenty.api.generation.IGenerator; import biomesoplenty.common.block.BlockBOPMushroom; +import biomesoplenty.common.init.ModBiomes; import biomesoplenty.common.util.biome.BiomeUtils; import biomesoplenty.common.util.biome.GeneratorUtils.ScatterYMethod; import biomesoplenty.common.util.block.BlockQuery; @@ -64,13 +65,10 @@ public class ExtendedBiomeWrapper implements IExtendedBiome // Allow generators to be configured IConfigObj confGenerators = conf.getObject("generators"); - if (confGenerators != null) - { - for (String name : confGenerators.getKeys()) - { - this.generationManager.configureWith(name, confGenerators.getObject(name)); - } - } + this.generationManager.configure(confGenerators); + + // write default values to a file + ModBiomes.writeDefaultConfigFile(ModBiomes.VANILLA_DEFAULTS_DIR, this.getResourceLocation().getResourcePath(), conf); } @Override diff --git a/src/main/java/biomesoplenty/common/init/ModBiomes.java b/src/main/java/biomesoplenty/common/init/ModBiomes.java index 023f755e7..7181c94f0 100644 --- a/src/main/java/biomesoplenty/common/init/ModBiomes.java +++ b/src/main/java/biomesoplenty/common/init/ModBiomes.java @@ -141,9 +141,15 @@ import net.minecraftforge.common.BiomeDictionary; import net.minecraftforge.common.BiomeDictionary.Type; import net.minecraftforge.common.BiomeManager; import net.minecraftforge.common.DimensionManager; +import org.apache.commons.io.FileUtils; public class ModBiomes implements BOPBiomes.IBiomeRegistry { + public static final File BIOMES_DIR = new File(BiomesOPlenty.configDirectory, "biomes"); + public static final File DEFAULTS_DIR = new File(BIOMES_DIR, "defaults"); + public static final File BOP_DEFAULTS_DIR = new File(DEFAULTS_DIR, "biomesoplenty"); + public static final File VANILLA_DEFAULTS_DIR = new File(DEFAULTS_DIR, "vanilla"); + public static WorldTypeBOP worldTypeBOP; private static int nextBiomeId = 40; @@ -181,19 +187,27 @@ public class ModBiomes implements BOPBiomes.IBiomeRegistry } //Create a folder and temp file to show people where to put biome config files - File biomesDir = new File(BiomesOPlenty.configDirectory, "biomes"); - - if (!biomesDir.exists()) + if (!BIOMES_DIR.exists()) { - biomesDir.mkdir(); + BIOMES_DIR.mkdir(); try { - (new File(biomesDir, "Put biome config files here")).createNewFile(); + (new File(BIOMES_DIR, "Put biome config files here")).createNewFile(); } catch (IOException e) {} } - + + // remove any existing default files and recreate the directory + try { + FileUtils.deleteDirectory(DEFAULTS_DIR); + } + catch (Exception e) + { + BiomesOPlenty.logger.error("Could not delete default biome config directory!"); + } + DEFAULTS_DIR.mkdir(); + initSubBiomes(); registerBiomes(); @@ -528,12 +542,26 @@ public class ModBiomes implements BOPBiomes.IBiomeRegistry { File configFile = new File(new File(BiomesOPlenty.configDirectory, "biomes"), idName + ".json"); IConfigObj conf = new BOPConfig.ConfigFileObj(configFile); - + // log any warnings from parsing the config file for (String msg : conf.flushMessages()) {BiomesOPlenty.logger.info(msg);} return conf; } + + public static void writeDefaultConfigFile(File basePath, String idName, IConfigObj conf) + { + File defaultFile = new File(basePath, idName + ".json"); + String json = BOPConfig.serializer.toJson(conf.serializeDefaults()); + + try { + FileUtils.write(defaultFile, json); + } + catch (Exception e) + { + BiomesOPlenty.logger.error("Could not write default biome config file " + defaultFile.getName()); + } + } private static void setSubBiome(Optional parent, Optional... subBiomes) { diff --git a/src/main/java/biomesoplenty/common/util/config/BOPConfig.java b/src/main/java/biomesoplenty/common/util/config/BOPConfig.java index fc096e4fc..67731e2ba 100644 --- a/src/main/java/biomesoplenty/common/util/config/BOPConfig.java +++ b/src/main/java/biomesoplenty/common/util/config/BOPConfig.java @@ -17,15 +17,10 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import com.google.common.collect.Maps; +import com.google.gson.*; import org.apache.commons.io.FileUtils; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - import biomesoplenty.api.block.IBlockPosQuery; import biomesoplenty.api.config.IConfigObj; import biomesoplenty.common.util.block.BlockQuery; @@ -59,9 +54,17 @@ public class BOPConfig public static abstract class ConfigObjBase implements IConfigObj { protected Map members; + protected Map defaults; + protected Map childObjs; protected List messages = new ArrayList(); protected String prefix = ""; - + + private ConfigObjBase() + { + this.defaults = Maps.newHashMap(); + this.childObjs = Maps.newHashMap(); + } + public void parse(String jsonString) { this.members = new HashMap(); @@ -86,8 +89,31 @@ public class BOPConfig this.addMessage("Error parsing config: "+e.getMessage()); } } - - + + @Override + public JsonElement serializeDefaults() + { + if (this.defaults.isEmpty()) + { + this.addMessage("No defaults found!"); + } + + // add top-level properties + JsonObject rootObject = new JsonObject(); + for (Entry entry : defaults.entrySet()) + { + rootObject.add(entry.getKey(), entry.getValue()); + } + + // serialize children + for (Entry entry : this.childObjs.entrySet()) + { + rootObject.add(entry.getKey(), entry.getValue().serializeDefaults()); + } + + return rootObject; + } + @Override public void addMessage(String message) { @@ -135,16 +161,27 @@ public class BOPConfig { this.addMessage(name, "Error - missing value"); } - return null; } + + // attempt to return cached child first + if (this.childObjs.containsKey(name)) + { + return this.childObjs.get(name); + } + + JsonObject obj = new JsonObject(); + try { - JsonObject obj = this.members.get(name).getAsJsonObject(); - return new ConfigChildObj(this.prefix + "." + name, obj); + obj = this.members.get(name).getAsJsonObject(); } catch (Exception e) { this.addMessage("Error fetching object " + name + ": " + e.getMessage()); - return null; } + + ConfigChildObj childObj = new ConfigChildObj(this.prefix + "." + name, obj); + // store the child for later serialization + this.childObjs.put(name, childObj); + return childObj; } @Override @@ -255,9 +292,9 @@ public class BOPConfig @Override public ArrayList getEnumArray(String name, Class clazz) {return this.getEnumArray(name, null, true, clazz);} - - - + + + protected ArrayList getEnumArray(String name, ArrayList defaultVal, boolean warnIfMissing, Class clazz) { if (!this.has(name)) @@ -299,6 +336,14 @@ public class BOPConfig private T get(String name, T defaultVal, boolean warnIfMissing, Types type) { + // if a default value is set, store it for later use + if (defaultVal != null) + { + JsonElement element = this.from(defaultVal, type); + // don't store if conversion to a JsonElement failed + if (element != null) this.defaults.put(name, element); + } + // check if the property exists, if not, use the default if (!this.has(name)) { if (warnIfMissing) @@ -308,6 +353,11 @@ public class BOPConfig return defaultVal; } T out = this.as(this.members.get(name), type, name); + // prevent people from trying to copy-paste default configs + if (out != null && out.equals(defaultVal)) + { + throw new RuntimeException("NOTE: This is YOUR fault, DO NOT report this to any developers.\n You can't set a property to its default value, only changed properties can be included in config files. \n Property: " + name + " Value: " + out + " Location: " + this.prefix); + } return out == null ? defaultVal : out; } @@ -351,12 +401,13 @@ public class BOPConfig return (T)this.asBlockState(ele, extraPrefix); case BLOCKPOSQUERY: return (T)this.asBlockPosQuery(ele, extraPrefix); + case RESOURCELOCATION: + return (T)this.asResourceLocation(ele, extraPrefix); default: return null; } } - protected E asEnum(JsonElement ele, Classclazz, String extraPrefix) { try @@ -449,7 +500,7 @@ public class BOPConfig try { JsonObject obj = ele.getAsJsonObject(); - + // attempt to load the specified block if (!obj.has("block")) { @@ -521,7 +572,52 @@ public class BOPConfig return null; } } - + + private JsonElement from(Object value, Types type) + { + switch (type) { + case BOOLEAN: + return new JsonPrimitive((Boolean)value); + case STRING: + return new JsonPrimitive((String)value); + case INTEGER: + return new JsonPrimitive((Integer)value); + case FLOAT: + return new JsonPrimitive((Float)value); + case BLOCKSTATE: + return fromState((IBlockState)value); + case BLOCKPOSQUERY: + return null; // ignore for now + case RESOURCELOCATION: + return fromResourceLocation((ResourceLocation)value); + default: + BiomesOPlenty.logger.error("Undefined type " + type); + return null; + } + } + + private JsonElement fromState(IBlockState state) + { + JsonObject root = new JsonObject(); + + // add block name + root.add("block", new JsonPrimitive(state.getBlock().getRegistryName().toString())); + JsonObject properties = new JsonObject(); + root.add("properties", properties); + + // add all property values + for (Entry, Comparable> entry : state.getProperties().entrySet()) + { + properties.add(entry.getKey().getName(), new JsonPrimitive(entry.getValue().toString())); + } + + return root; + } + + private JsonElement fromResourceLocation(ResourceLocation location) + { + return new JsonPrimitive(location.toString()); + } } // Concrete class for a config object created by supplying the JSON in a string diff --git a/src/main/java/biomesoplenty/common/world/GenerationManager.java b/src/main/java/biomesoplenty/common/world/GenerationManager.java index 3cdba0a21..2deb36b11 100644 --- a/src/main/java/biomesoplenty/common/world/GenerationManager.java +++ b/src/main/java/biomesoplenty/common/world/GenerationManager.java @@ -58,30 +58,31 @@ public class GenerationManager implements IGenerationManager return this.generators.get(name); } - public void configureWith(String name, IConfigObj conf) + public void configure(IConfigObj generatorsObj) { - if (this.generators.containsKey(name)) + // iterate over all registered generators + for (String name : generators.keySet()) { - if (conf.getBool("enable", true)) + IConfigObj currentObj = generatorsObj.getObject(name); + + // there was previously no generator of this name - attempt to add it + if (generatorsObj.has(name)) { - // configure the existing generator - this.generators.get(name).configure(conf); + IGenerator generator = GeneratorRegistry.createGenerator(currentObj); + if (generator != null) { + this.generators.put(name, generator); + } } - else - { + + // configure the generator + // always attempt to do this so defaults are generated + if (currentObj.getBool("enable", true)) { + this.generators.get(name).configure(currentObj); + } else { // remove this generator this.generators.remove(name); } } - else - { - // there was previously no generator of this name - attempt to add it - IGenerator generator = GeneratorRegistry.createGenerator(conf); - if (generator != null) - { - this.generators.put(name, generator); - } - } } } \ No newline at end of file