package com.destroystokyo.paper;
import com.google.common.base.Throwables;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.regex.Pattern;
import com.google.common.collect.Lists;
import com.koloboke.collect.map.hash.HashObjObjMaps;
import net.minecraft.server.MinecraftServer;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.spigotmc.SpigotConfig;
import co.aikar.timings.Timings;
import co.aikar.timings.TimingsManager;
public class PaperConfig {
private static File CONFIG_FILE;
private static final String HEADER = "This is the main configuration file for Paper.\n"
+ "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
+ "with caution, and make sure you know what each option does before configuring.\n"
+ "\n"
+ "If you need help with the configuration or have any questions related to Paper,\n"
+ "join us in our IRC channel.\n"
+ "\n"
+ "IRC: #paper @ irc.spi.gt ( http://irc.spi.gt/iris/?channels=paper )\n"
+ "Wiki: https://paper.readthedocs.org/ \n"
+ "Paper Forums: https://aquifermc.org/ \n";
/*========================================================================*/
public static YamlConfiguration config;
static int version;
static Map<String, Command> commands;
private static boolean verbose;
/*========================================================================*/
private static Metrics metrics;
public static void init(File configFile) {
CONFIG_FILE = configFile;
config = new YamlConfiguration();
try {
config.load(CONFIG_FILE);
} catch (IOException ex) {
} catch (InvalidConfigurationException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not load paper.yml, please correct your syntax errors", ex);
throw Throwables.propagate(ex);
}
config.options().header(HEADER);
config.options().copyDefaults(true);
verbose = getBoolean("verbose", false);
commands = HashObjObjMaps.newMutableMap();
commands.put("paper", new PaperCommand("paper"));
version = getInt("config-version", 12);
set("config-version", 12);
readConfig(PaperConfig.class, null);
}
protected static void log(String s) {
if (verbose && SpigotConfig.debug) {
Bukkit.getLogger().info(s);
}
}
public static void registerCommands() {
for (Map.Entry<String, Command> entry : commands.entrySet()) {
MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Paper", entry.getValue());
}
if (metrics == null) {
metrics = new Metrics();
}
}
static void readConfig(Class<?> clazz, Object instance) {
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isPrivate(method.getModifiers())) {
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
}
}
}
}
try {
config.save(CONFIG_FILE);
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
}
}
private static final Pattern SPACE = Pattern.compile(" ");
private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]");
public static int getSeconds(String str) {
str = SPACE.matcher(str).replaceAll("");
final char unit = str.charAt(str.length() - 1);
str = NOT_NUMERIC.matcher(str).replaceAll("");
double num;
try {
num = Double.parseDouble(str);
} catch (Exception e) {
num = 0D;
}
switch (unit) {
case 'd': num *= (double) 60*60*24; break;
case 'h': num *= (double) 60*60; break;
case 'm': num *= 60; break;
default: case 's': break;
}
return (int) num;
}
protected static String timeSummary(int seconds) {
String time = "";
if (seconds > 60 * 60 * 24) {
time += TimeUnit.SECONDS.toDays(seconds) + "d";
seconds %= 60 * 60 * 24;
}
if (seconds > 60 * 60) {
time += TimeUnit.SECONDS.toHours(seconds) + "h";
seconds %= 60 * 60;
}
if (seconds > 0) {
time += TimeUnit.SECONDS.toMinutes(seconds) + "m";
}
return time;
}
private static void set(String path, Object val) {
config.set(path, val);
}
private static boolean getBoolean(String path, boolean def) {
config.addDefault(path, def);
return config.getBoolean(path, config.getBoolean(path));
}
private static double getDouble(String path, double def) {
config.addDefault(path, def);
return config.getDouble(path, config.getDouble(path));
}
private static float getFloat(String path, float def) {
// TODO: Figure out why getFloat() always returns the default value.
return (float) getDouble(path, def);
}
private static int getInt(String path, int def) {
config.addDefault(path, def);
return config.getInt(path, config.getInt(path));
}
private static <T> List getList(String path, T def) {
config.addDefault(path, def);
return config.getList(path, config.getList(path));
}
private static String getString(String path, String def) {
config.addDefault(path, def);
return config.getString(path, config.getString(path));
}
private static void timings() {
boolean timings = getBoolean("timings.enabled", true);
boolean verboseTimings = getBoolean("timings.verbose", true);
TimingsManager.privacy = getBoolean("timings.server-name-privacy", false);
TimingsManager.hiddenConfigs = getList("timings.hidden-config-entries", Lists.newArrayList("database", "settings.bungeecord-addresses"));
int timingHistoryInterval = getInt("timings.history-interval", 300);
int timingHistoryLength = getInt("timings.history-length", 3600);
Timings.setVerboseTimingsEnabled(verboseTimings);
Timings.setTimingsEnabled(timings);
Timings.setHistoryInterval(timingHistoryInterval * 20);
Timings.setHistoryLength(timingHistoryLength * 20);
log("Timings: " + timings +
" - Verbose: " + verboseTimings +
" - Interval: " + timeSummary(Timings.getHistoryInterval() / 20) +
" - Length: " + timeSummary(Timings.getHistoryLength() / 20));
}
public static int minChunkLoadThreads = 2;
private static void chunkLoadThreads() {
minChunkLoadThreads = Math.min(6, getInt("settings.min-chunk-load-threads", 2)); // Keep people from doing stupid things with max of 6
}
public static boolean enableFileIOThreadSleep;
private static void enableFileIOThreadSleep() {
enableFileIOThreadSleep = getBoolean("settings.sleep-between-chunk-saves", false);
if (enableFileIOThreadSleep) Bukkit.getLogger().info("Enabled sleeping between chunk saves, beware of memory issues");
}
public static boolean loadPermsBeforePlugins = true;
private static void loadPermsBeforePlugins() {
loadPermsBeforePlugins = getBoolean("settings.load-permissions-yml-before-plugins", true);
}
public static int regionFileCacheSize = 256;
private static void regionFileCacheSize() {
regionFileCacheSize = getInt("settings.region-file-cache-size", 256);
}
public static boolean enablePlayerCollisions = true;
private static void enablePlayerCollisions() {
enablePlayerCollisions = getBoolean("settings.enable-player-collisions", true);
}
public static boolean saveEmptyScoreboardTeams = false;
private static void saveEmptyScoreboardTeams() {
saveEmptyScoreboardTeams = getBoolean("settings.save-empty-scoreboard-teams", false);
}
public static boolean bungeeOnlineMode = true;
private static void bungeeOnlineMode() {
bungeeOnlineMode = getBoolean("settings.bungee-online-mode", true);
}
public static int packetInSpamThreshold = 300;
private static void packetInSpamThreshold() {
if (version < 11) {
int oldValue = getInt("settings.play-in-use-item-spam-threshold", 300);
set("settings.incoming-packet-spam-threshold", oldValue);
}
packetInSpamThreshold = getInt("settings.incoming-packet-spam-threshold", 300);
}
public static String flyingKickPlayerMessage = "Flying is not enabled on this server";
public static String flyingKickVehicleMessage = "Flying is not enabled on this server";
private static void flyingKickMessages() {
flyingKickPlayerMessage = getString("messages.kick.flying-player", flyingKickPlayerMessage);
flyingKickVehicleMessage = getString("messages.kick.flying-vehicle", flyingKickVehicleMessage);
}
public static int playerAutoSaveRate = -1;
private static void playerAutoSaveRate() {
playerAutoSaveRate = getInt("settings.player-auto-save-rate", -1);
}
public static boolean removeInvalidStatistics = false;
private static void removeInvalidStatistics() {
if (version < 12) {
boolean oldValue = getBoolean("remove-invalid-statistics", false);
set("settings.remove-invalid-statistics", oldValue);
}
removeInvalidStatistics = getBoolean("settings.remove-invalid-statistics", false);
}
}