package net.glowstone.util.config;
import net.glowstone.GlowServer;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.yaml.snakeyaml.error.YAMLException;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Utilities for handling the server configuration files.
*/
public final class WorldConfig {
/**
* The directory configurations are stored in.
*/
private final File configDir;
/**
* The main configuration file.
*/
private final File configFile;
/**
* The actual configuration data.
*/
private final YamlConfiguration config = new YamlConfiguration();
/**
* The cache for config settings within a map.
*/
private final Map<Key, Object> cache = new HashMap<>();
/**
* Initialize a new ServerConfig and associated settings.
*
* @param configDir The config directory, or null for default.
* @param configFile The config file, or null for default.
*/
public WorldConfig(File configDir, File configFile) {
checkNotNull(configDir);
checkNotNull(configFile);
checkNotNull(cache);
this.configDir = configDir;
this.configFile = configFile;
}
////////////////////////////////////////////////////////////////////////////
// Modification
/**
* Save the configuration back to file.
*/
public void save() {
try {
config.save(configFile);
} catch (IOException e) {
GlowServer.logger.log(Level.SEVERE, "Failed to write config: " + configFile, e);
}
}
/**
* Change a configuration value at runtime.
*
* @param key the config key to write the value to
* @param value value to write to config key
* @see #save()
*/
public void set(Key key, Object value) {
cache.replace(key, value);
config.set(key.path, value);
}
////////////////////////////////////////////////////////////////////////////
// Value getters
public String getString(Key key) {
if (cache.containsKey(key)) {
return cache.get(key).toString();
}
String string = config.getString(key.path, key.def.toString());
cache.put(key, string);
return string;
}
public int getInt(Key key) {
if (cache.containsKey(key)) {
return (Integer) cache.get(key);
}
int integer = config.getInt(key.path, (Integer) key.def);
cache.put(key, integer);
return integer;
}
public double getDouble(Key key) {
if (cache.containsKey(key)) {
return (Double) cache.get(key);
}
double doub = config.getDouble(key.path, (Double) key.def);
cache.put(key, doub);
return doub;
}
public boolean getBoolean(Key key) {
if (cache.containsKey(key)) {
return (Boolean) cache.get(key);
}
boolean bool = config.getBoolean(key.path, (Boolean) key.def);
cache.put(key, bool);
return bool;
}
////////////////////////////////////////////////////////////////////////////
// Fancy stuff
public File getDirectory() {
return configDir;
}
////////////////////////////////////////////////////////////////////////////
// Load and internals
public void load() {
boolean changed = false;
// create default file if needed
if (!configFile.exists()) {
GlowServer.logger.info("Creating default world config: " + configFile);
// create config directory
if (!configDir.isDirectory() && !configDir.mkdirs()) {
GlowServer.logger.severe("Cannot create directory: " + configDir);
return;
}
// load default config
for (Key key : Key.values()) {
config.set(key.path, key.def);
}
changed = true;
} else {
// load config
try {
config.load(configFile);
} catch (IOException e) {
GlowServer.logger.log(Level.SEVERE, "Failed to read config: " + configFile, e);
} catch (InvalidConfigurationException e) {
report(configFile, e);
}
// add missing keys to the current config
for (Key key : Key.values()) {
if (!config.contains(key.path)) {
config.set(key.path, key.def);
changed = true;
}
}
}
if (changed) {
save();
}
}
private void report(File file, InvalidConfigurationException e) {
if (e.getCause() instanceof YAMLException) {
GlowServer.logger.severe("Config file " + file + " isn't valid! " + e.getCause());
} else if (e.getCause() == null || e.getCause() instanceof ClassCastException) {
GlowServer.logger.severe("Config file " + file + " isn't valid!");
} else {
GlowServer.logger.log(Level.SEVERE, "Cannot load " + file + ": " + e.getCause().getClass(), e);
}
}
public YamlConfiguration getConfig() {
return config;
}
/**
* An enum containing configuration keys used by the server.
*/
public enum Key {
// World
SEA_LEVEL("general.sea_level", 64),
// Overworld generator
OVERWORLD_COORDINATE_SCALE("overworld.coordinate-scale", 684.412),
OVERWORLD_HEIGHT_SCALE("overworld.height.scale", 684.412),
OVERWORLD_HEIGHT_NOISE_SCALE_X("overworld.height.noise-scale.x", 200D),
OVERWORLD_HEIGHT_NOISE_SCALE_Z("overworld.height.noise-scale.z", 200D),
OVERWORLD_DETAIL_NOISE_SCALE_X("overworld.detail.noise-scale.x", 80D),
OVERWORLD_DETAIL_NOISE_SCALE_Y("overworld.detail.noise-scale.y", 160D),
OVERWORLD_DETAIL_NOISE_SCALE_Z("overworld.detail.noise-scale.z", 80D),
OVERWORLD_SURFACE_SCALE("overworld.surface-scale", 0.0625),
OVERWORLD_BASE_SIZE("overworld.base-size", 8.5),
OVERWORLD_STRETCH_Y("overworld.stretch-y", 12D),
OVERWORLD_BIOME_HEIGHT_OFFSET("overworld.biome.height-offset", 0D),
OVERWORLD_BIOME_HEIGHT_WEIGHT("overworld.biome.height-weight", 1D),
OVERWORLD_BIOME_SCALE_OFFSET("overworld.biome.scale-offset", 0D),
OVERWORLD_BIOME_SCALE_WEIGHT("overworld.biome.scale-weight", 1D),
OVERWORLD_DENSITY_FILL_MODE("overworld.density.fill.mode", 0),
OVERWORLD_DENSITY_FILL_SEA_MODE("overworld.density.fill.sea-mode", 0),
OVERWORLD_DENSITY_FILL_OFFSET("overworld.density.fill.offset", 0D),
// Overworld biome heights
BIOME_HEIGHT_DEFAULT("overworld.biome.height.default", 0.1),
BIOME_SCALE_DEFAULT("overworld.biome.scale.default", 0.2),
BIOME_HEIGHT_FLAT_SHORE("overworld.biome.height.flat-shore", 0D),
BIOME_SCALE_FLAT_SHORE("overworld.biome.scale.flat-shore", 0.025),
BIOME_HEIGHT_HIGH_PLATEAU("overworld.biome.height.high-plateau", 1.5),
BIOME_SCALE_HIGH_PLATEAU("overworld.biome.scale.high-plateau", 0.025),
BIOME_HEIGHT_FLATLANDS("overworld.biome.height.flatlands", 0.125),
BIOME_SCALE_FLATLANDS("overworld.biome.scale.flatlands", 0.05),
BIOME_HEIGHT_SWAMPLAND("overworld.biome.height.swampland", -0.2),
BIOME_SCALE_SWAMPLAND("overworld.biome.scale.swampland", 0.1),
BIOME_HEIGHT_MID_PLAINS("overworld.biome.height.mid-plains", 0.2),
BIOME_SCALE_MID_PLAINS("overworld.biome.scale.mid-plains", 0.2),
BIOME_HEIGHT_FLATLANDS_HILLS("overworld.biome.height.flatlands-hills", 0.275),
BIOME_SCALE_FLATLANDS_HILLS("overworld.biome.scale.flatlands-hills", 0.25),
BIOME_HEIGHT_SWAMPLAND_HILLS("overworld.biome.height.swampland-hills", -0.1),
BIOME_SCALE_SWAMPLAND_HILLS("overworld.biome.scale.swampland-hills", 0.3),
BIOME_HEIGHT_LOW_HILLS("overworld.biome.height.low-hills", 0.2),
BIOME_SCALE_LOW_HILLS("overworld.biome.scale.low-hills", 0.3),
BIOME_HEIGHT_HILLS("overworld.biome.height.hills", 0.45),
BIOME_SCALE_HILLS("overworld.biome.scale.hills", 0.3),
BIOME_HEIGHT_MID_HILLS2("overworld.biome.height.mid-hills2", 0.1),
BIOME_SCALE_MID_HILLS2("overworld.biome.scale.mid-hills2", 0.4),
BIOME_HEIGHT_DEFAULT_HILLS("overworld.biome.height.default-hills", 0.2),
BIOME_SCALE_DEFAULT_HILLS("overworld.biome.scale.default-hills", 0.4),
BIOME_HEIGHT_MID_HILLS("overworld.biome.height.mid-hills", 0.3),
BIOME_SCALE_MID_HILLS("overworld.biome.scale.mid-hills", 0.4),
BIOME_HEIGHT_BIG_HILLS("overworld.biome.height.big-hills", 0.525),
BIOME_SCALE_BIG_HILLS("overworld.biome.scale.big-hills", 0.55),
BIOME_HEIGHT_BIG_HILLS2("overworld.biome.height.big-hills2", 0.55),
BIOME_SCALE_BIG_HILLS2("overworld.biome.scale.big-hills2", 0.5),
BIOME_HEIGHT_EXTREME_HILLS("overworld.biome.height.extreme-hills", 1D),
BIOME_SCALE_EXTREME_HILLS("overworld.biome.scale.extreme-hills", 0.5),
BIOME_HEIGHT_ROCKY_SHORE("overworld.biome.height.rocky-shore", 0.1),
BIOME_SCALE_ROCKY_SHORE("overworld.biome.scale.rocky-shore", 0.8),
BIOME_HEIGHT_LOW_SPIKES("overworld.biome.height.low-spikes", 0.4125),
BIOME_SCALE_LOW_SPIKES("overworld.biome.scale.low-spikes", 1.325),
BIOME_HEIGHT_HIGH_SPIKES("overworld.biome.height.high-spikes", 1.1),
BIOME_SCALE_HIGH_SPIKES("overworld.biome.scale.high-spikes", 1.3125),
BIOME_HEIGHT_RIVER("overworld.biome.height.river", -0.5),
BIOME_SCALE_RIVER("overworld.biome.scale.river", 0D),
BIOME_HEIGHT_OCEAN("overworld.biome.height.ocean", -1D),
BIOME_SCALE_OCEAN("overworld.biome.scale.ocean", 0.1),
BIOME_HEIGHT_DEEP_OCEAN("overworld.biome.height.deep-ocean", -1.8),
BIOME_SCALE_DEEP_OCEAN("overworld.biome.scale.deep-ocean", 0.1),
// Nether generator
NETHER_COORDINATE_SCALE("nether.coordinate-scale", 684.412),
NETHER_HEIGHT_SCALE("nether.height.scale", 2053.236),
NETHER_HEIGHT_NOISE_SCALE_X("nether.height.noise-scale.x", 100D),
NETHER_HEIGHT_NOISE_SCALE_Z("nether.height.noise-scale.z", 100D),
NETHER_DETAIL_NOISE_SCALE_X("nether.detail.noise-scale.x", 80D),
NETHER_DETAIL_NOISE_SCALE_Y("nether.detail.noise-scale.y", 60D),
NETHER_DETAIL_NOISE_SCALE_Z("nether.detail.noise-scale.z", 80D),
NETHER_SURFACE_SCALE("nether.surface-scale", 0.0625),
// The End generator
END_COORDINATE_SCALE("end.coordinate-scale", 684.412),
END_HEIGHT_SCALE("end.height.scale", 1368.824),
END_DETAIL_NOISE_SCALE_X("end.detail.noise-scale.x", 80D),
END_DETAIL_NOISE_SCALE_Y("end.detail.noise-scale.y", 160D),
END_DETAIL_NOISE_SCALE_Z("end.detail.noise-scale.z", 80D),
;
private final String path;
private final Object def;
Key(String path, Object def) {
this.path = path;
this.def = def;
}
@Override
public String toString() {
return name() + "(" + path + ", " + def + ")";
}
}
}