Initialization cleanup: use provider classes for Settings and DataSource

- Separate provider methods from other tasks performed on startup
  - No longer requires BukkitService to be instantiated manually
- Merge MetricsManager and Initializer into OnStartUpTasks
This commit is contained in:
ljacqu 2016-11-06 18:51:07 +01:00
parent f2cafe4f25
commit 72853a382f
11 changed files with 340 additions and 255 deletions

View File

@ -5,13 +5,14 @@ import ch.jalu.injector.InjectorBuilder;
import com.google.common.annotations.VisibleForTesting;
import fr.xephi.authme.api.API;
import fr.xephi.authme.api.NewAPI;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.command.CommandHandler;
import fr.xephi.authme.data.auth.PlayerCache;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.initialization.DataFolder;
import fr.xephi.authme.initialization.Initializer;
import fr.xephi.authme.initialization.MetricsManager;
import fr.xephi.authme.initialization.DataSourceProvider;
import fr.xephi.authme.initialization.OnShutdownPlayerSaver;
import fr.xephi.authme.initialization.OnStartupTasks;
import fr.xephi.authme.initialization.SettingsProvider;
import fr.xephi.authme.initialization.TaskCloser;
import fr.xephi.authme.listener.BlockListener;
import fr.xephi.authme.listener.EntityListener;
@ -20,20 +21,19 @@ import fr.xephi.authme.listener.PlayerListener16;
import fr.xephi.authme.listener.PlayerListener18;
import fr.xephi.authme.listener.PlayerListener19;
import fr.xephi.authme.listener.ServerListener;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.permission.PermissionsManager;
import fr.xephi.authme.permission.PermissionsSystemType;
import fr.xephi.authme.security.crypts.SHA256;
import fr.xephi.authme.service.BackupService;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.GeoIpService;
import fr.xephi.authme.service.MigrationService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.RestrictionSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.task.CleanupTask;
import fr.xephi.authme.task.purge.PurgeService;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.MigrationService;
import fr.xephi.authme.util.PlayerUtils;
import org.bukkit.Server;
import org.bukkit.command.Command;
@ -68,7 +68,6 @@ public class AuthMe extends JavaPlugin {
private CommandHandler commandHandler;
private PermissionsManager permsMan;
private Settings settings;
private Messages messages;
private DataSource database;
private BukkitService bukkitService;
private Injector injector;
@ -156,7 +155,7 @@ public class AuthMe extends JavaPlugin {
new BackupService(this, settings).doBackup(BackupService.BackupCause.START);
// Set up Metrics
MetricsManager.sendMetrics(this, settings);
OnStartupTasks.sendMetrics(this, settings);
// Sponsor messages
ConsoleLogger.info("Development builds are available on our jenkins, thanks to f14stelt.");
@ -202,37 +201,27 @@ public class AuthMe extends JavaPlugin {
// Create plugin folder
getDataFolder().mkdir();
// Load settings and set up the console and console filter
settings = Initializer.createSettings(this);
bukkitService = new BukkitService(this, settings);
Initializer initializer = new Initializer(this, bukkitService);
ConsoleLogger.setLoggingOptions(settings);
initializer.setupConsoleFilter(settings, getLogger());
// Connect to the database and set up tables
database = initializer.setupDatabase(settings);
// Convert deprecated PLAINTEXT hash entries
MigrationService.changePlainTextToSha256(settings, database, new SHA256());
// Injector initialization
// Create injector, provide elements from the Bukkit environment and register providers
injector = new InjectorBuilder().addDefaultHandlers("fr.xephi.authme").create();
// Register elements of the Bukkit / JavaPlugin environment
injector.register(AuthMe.class, this);
injector.register(Server.class, getServer());
injector.register(PluginManager.class, getServer().getPluginManager());
injector.register(BukkitScheduler.class, getServer().getScheduler());
injector.provide(DataFolder.class, getDataFolder());
injector.registerProvider(Settings.class, SettingsProvider.class);
injector.registerProvider(DataSource.class, DataSourceProvider.class);
// Register elements we instantiate manually
injector.register(Settings.class, settings);
injector.register(DataSource.class, database);
injector.register(BukkitService.class, bukkitService);
// Get settings and set up logger
settings = injector.getSingleton(Settings.class);
ConsoleLogger.setLoggingOptions(settings);
OnStartupTasks.setupConsoleFilter(settings, getLogger());
// Set all service fields on the AuthMe class
instantiateServices(injector);
// Convert deprecated PLAINTEXT hash entries
MigrationService.changePlainTextToSha256(settings, database, new SHA256());
// TODO: does this still make sense? -sgdc3
// If the server is empty (fresh start) just set all the players as unlogged
if (bukkitService.getOnlinePlayers().size() == 0) {
@ -243,7 +232,8 @@ public class AuthMe extends JavaPlugin {
registerEventListeners(injector);
// Start Email recall task if needed
initializer.scheduleRecallEmailTask(settings, database, messages);
OnStartupTasks onStartupTasks = injector.newInstance(OnStartupTasks.class);
onStartupTasks.scheduleRecallEmailTask();
}
/**
@ -256,7 +246,7 @@ public class AuthMe extends JavaPlugin {
playerCache = PlayerCache.getInstance();
injector.register(PlayerCache.class, playerCache);
messages = injector.getSingleton(Messages.class);
database = injector.getSingleton(DataSource.class);
permsMan = injector.getSingleton(PermissionsManager.class);
bukkitService = injector.getSingleton(BukkitService.class);
commandHandler = injector.getSingleton(CommandHandler.class);

View File

@ -0,0 +1,123 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.FlatFile;
import fr.xephi.authme.datasource.MySQL;
import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.datasource.converter.ForceFlatToSqlite;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import javax.inject.Inject;
import javax.inject.Provider;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
/**
* Creates the AuthMe data source.
*/
public class DataSourceProvider implements Provider<DataSource> {
private static final String FLATFILE_FILENAME = "auths.db";
private static final int SQLITE_MAX_SIZE = 4000;
@Inject
@DataFolder
private File dataFolder;
@Inject
private Settings settings;
@Inject
private BukkitService bukkitService;
DataSourceProvider() {
}
@Override
public DataSource get() {
try {
return createDataSource();
} catch (Exception e) {
ConsoleLogger.logException("Could not create data source:", e);
throw new IllegalStateException("Error during initialization of data source", e);
}
}
/**
* Sets up the data source.
*
* @return the constructed datasource
* @throws ClassNotFoundException if no driver could be found for the datasource
* @throws SQLException when initialization of a SQL datasource failed
* @throws IOException if flat file cannot be read
*/
private DataSource createDataSource() throws ClassNotFoundException, SQLException, IOException {
DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
DataSource dataSource;
switch (dataSourceType) {
case FILE:
File source = new File(dataFolder, FLATFILE_FILENAME);
dataSource = new FlatFile(source);
break;
case MYSQL:
dataSource = new MySQL(settings);
break;
case SQLITE:
dataSource = new SQLite(settings);
break;
default:
throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'");
}
dataSource = convertFlatfileToSqlite(dataSource);
if (settings.getProperty(DatabaseSettings.USE_CACHING)) {
dataSource = new CacheDataSource(dataSource);
}
if (DataSourceType.SQLITE.equals(dataSourceType)) {
checkDataSourceSize(dataSource, bukkitService);
}
return dataSource;
}
private void checkDataSourceSize(final DataSource dataSource, BukkitService bukkitService) {
bukkitService.runTaskAsynchronously(() -> {
int accounts = dataSource.getAccountsRegistered();
if (accounts >= SQLITE_MAX_SIZE) {
ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH "
+ accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!");
}
});
}
/**
* Converts the data source from the deprecated FLATFILE type to SQLITE.
*
* @param dataSource the data source to convert if necessary
* @return the data source to use: the converted datasource (SQLite),
* or the same data source if no conversion was performed
*/
private DataSource convertFlatfileToSqlite(DataSource dataSource) {
if (DataSourceType.FILE == settings.getProperty(DatabaseSettings.BACKEND)) {
ConsoleLogger.warning("FlatFile backend has been detected and is now deprecated; it will be changed "
+ "to SQLite... Connection will be impossible until conversion is done!");
FlatFile flatFile = (FlatFile) dataSource;
try {
SQLite sqlite = new SQLite(settings);
ForceFlatToSqlite converter = new ForceFlatToSqlite(flatFile, sqlite);
converter.execute(null);
settings.setProperty(DatabaseSettings.BACKEND, DataSourceType.SQLITE);
settings.save();
return sqlite;
} catch (Exception e) {
ConsoleLogger.logException("Error during conversion from Flatfile to SQLite", e);
throw new IllegalStateException(e);
}
}
return dataSource;
}
}

View File

@ -3,6 +3,8 @@ package fr.xephi.authme.initialization;
/**
* Common interface for types which have data that becomes outdated
* and that can be cleaned up periodically.
*
* @see fr.xephi.authme.task.CleanupTask
*/
public interface HasCleanup {

View File

@ -1,176 +0,0 @@
package fr.xephi.authme.initialization;
import com.github.authme.configme.knownproperties.ConfigurationData;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.CacheDataSource;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.datasource.DataSourceType;
import fr.xephi.authme.datasource.FlatFile;
import fr.xephi.authme.datasource.MySQL;
import fr.xephi.authme.datasource.SQLite;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.service.MigrationService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.FileUtils;
import fr.xephi.authme.util.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Logger;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
/**
* Initializes various services.
*/
public class Initializer {
private static final String FLATFILE_FILENAME = "auths.db";
private static final int SQLITE_MAX_SIZE = 4000;
private AuthMe authMe;
private BukkitService bukkitService;
public Initializer(AuthMe authMe, BukkitService bukkitService) {
this.authMe = authMe;
this.bukkitService = bukkitService;
}
/**
* Loads the plugin's settings.
*
* @param authMe the plugin instance
* @return the settings instance, or null if it could not be constructed
*/
public static Settings createSettings(AuthMe authMe) throws Exception {
File configFile = new File(authMe.getDataFolder(), "config.yml");
if(!configFile.exists()) {
configFile.createNewFile();
}
PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder());
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
return new Settings(authMe.getDataFolder(), resource, migrationService, configurationData);
}
/**
* Sets up the data source.
*
* @param settings the settings
* @return the constructed datasource
* @throws ClassNotFoundException if no driver could be found for the datasource
* @throws SQLException when initialization of a SQL datasource failed
* @throws IOException if flat file cannot be read
*/
public DataSource setupDatabase(Settings settings) throws ClassNotFoundException, SQLException, IOException {
DataSourceType dataSourceType = settings.getProperty(DatabaseSettings.BACKEND);
DataSource dataSource;
switch (dataSourceType) {
case FILE:
File source = new File(authMe.getDataFolder(), FLATFILE_FILENAME);
dataSource = new FlatFile(source);
break;
case MYSQL:
dataSource = new MySQL(settings);
break;
case SQLITE:
dataSource = new SQLite(settings);
break;
default:
throw new UnsupportedOperationException("Unknown data source type '" + dataSourceType + "'");
}
DataSource convertedSource = MigrationService.convertFlatfileToSqlite(settings, dataSource);
dataSource = convertedSource == null ? dataSource : convertedSource;
if (settings.getProperty(DatabaseSettings.USE_CACHING)) {
dataSource = new CacheDataSource(dataSource);
}
if (DataSourceType.SQLITE.equals(dataSourceType)) {
checkDataSourceSize(dataSource);
}
return dataSource;
}
private void checkDataSourceSize(final DataSource dataSource) {
bukkitService.runTaskAsynchronously(new Runnable() {
@Override
public void run() {
int accounts = dataSource.getAccountsRegistered();
if (accounts >= SQLITE_MAX_SIZE) {
ConsoleLogger.warning("YOU'RE USING THE SQLITE DATABASE WITH "
+ accounts + "+ ACCOUNTS; FOR BETTER PERFORMANCE, PLEASE UPGRADE TO MYSQL!!");
}
}
});
}
/**
* Sets up the console filter if enabled.
*
* @param settings the settings
* @param logger the plugin logger
*/
public void setupConsoleFilter(Settings settings, Logger logger) {
if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) {
return;
}
// Try to set the log4j filter
try {
Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter");
setLog4JFilter();
} catch (ClassNotFoundException | NoClassDefFoundError e) {
// log4j is not available
ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled");
ConsoleFilter filter = new ConsoleFilter();
logger.setFilter(filter);
Bukkit.getLogger().setFilter(filter);
Logger.getLogger("Minecraft").setFilter(filter);
}
}
// Set the console filter to remove the passwords
private static void setLog4JFilter() {
org.apache.logging.log4j.core.Logger logger;
logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
logger.addFilter(new Log4JFilter());
}
public void scheduleRecallEmailTask(Settings settings, final DataSource dataSource, final Messages messages) {
if (!settings.getProperty(RECALL_PLAYERS)) {
return;
}
bukkitService.runTaskTimerAsynchronously(new Runnable() {
@Override
public void run() {
for (PlayerAuth auth : dataSource.getLoggedPlayers()) {
String email = auth.getEmail();
if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) {
Player player = bukkitService.getPlayerExact(auth.getRealName());
if (player != null) {
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
}
}
}
}, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL));
}
}

View File

@ -1,47 +0,0 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import org.mcstats.Metrics;
import org.mcstats.Metrics.Graph;
import java.io.IOException;
public class MetricsManager {
private MetricsManager() {
}
public static void sendMetrics(AuthMe plugin, Settings settings) {
try {
final Metrics metrics = new Metrics(plugin);
final Graph languageGraph = metrics.createGraph("Messages Language");
final String messagesLanguage = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
languageGraph.addPlotter(new Metrics.Plotter(messagesLanguage) {
@Override
public int getValue() {
return 1;
}
});
final Graph databaseBackend = metrics.createGraph("Database Backend");
final String dataSource = settings.getProperty(DatabaseSettings.BACKEND).toString();
databaseBackend.addPlotter(new Metrics.Plotter(dataSource) {
@Override
public int getValue() {
return 1;
}
});
// Submit metrics
metrics.start();
} catch (IOException e) {
// Failed to submit the metrics data
ConsoleLogger.logException("Can't send Metrics data! The plugin will work anyway...", e);
}
}
}

View File

@ -0,0 +1,127 @@
package fr.xephi.authme.initialization;
import fr.xephi.authme.AuthMe;
import fr.xephi.authme.ConsoleLogger;
import fr.xephi.authme.data.auth.PlayerAuth;
import fr.xephi.authme.datasource.DataSource;
import fr.xephi.authme.message.MessageKey;
import fr.xephi.authme.message.Messages;
import fr.xephi.authme.output.ConsoleFilter;
import fr.xephi.authme.output.Log4JFilter;
import fr.xephi.authme.service.BukkitService;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.properties.DatabaseSettings;
import fr.xephi.authme.settings.properties.EmailSettings;
import fr.xephi.authme.settings.properties.PluginSettings;
import fr.xephi.authme.settings.properties.SecuritySettings;
import fr.xephi.authme.util.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.mcstats.Metrics;
import javax.inject.Inject;
import java.io.IOException;
import java.util.logging.Logger;
import static fr.xephi.authme.service.BukkitService.TICKS_PER_MINUTE;
import static fr.xephi.authme.settings.properties.EmailSettings.RECALL_PLAYERS;
/**
* Contains actions such as migrations that should be performed on startup.
*/
public class OnStartupTasks {
@Inject
private DataSource dataSource;
@Inject
private Settings settings;
@Inject
private BukkitService bukkitService;
@Inject
private Messages messages;
OnStartupTasks() {
}
public static void sendMetrics(AuthMe plugin, Settings settings) {
try {
final Metrics metrics = new Metrics(plugin);
final Metrics.Graph languageGraph = metrics.createGraph("Messages Language");
final String messagesLanguage = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE);
languageGraph.addPlotter(new Metrics.Plotter(messagesLanguage) {
@Override
public int getValue() {
return 1;
}
});
final Metrics.Graph databaseBackend = metrics.createGraph("Database Backend");
final String dataSource = settings.getProperty(DatabaseSettings.BACKEND).toString();
databaseBackend.addPlotter(new Metrics.Plotter(dataSource) {
@Override
public int getValue() {
return 1;
}
});
// Submit metrics
metrics.start();
} catch (IOException e) {
// Failed to submit the metrics data
ConsoleLogger.logException("Can't send Metrics data! The plugin will work anyway...", e);
}
}
/**
* Sets up the console filter if enabled.
*
* @param settings the settings
* @param logger the plugin logger
*/
public static void setupConsoleFilter(Settings settings, Logger logger) {
if (!settings.getProperty(SecuritySettings.REMOVE_PASSWORD_FROM_CONSOLE)) {
return;
}
// Try to set the log4j filter
try {
Class.forName("org.apache.logging.log4j.core.filter.AbstractFilter");
setLog4JFilter();
} catch (ClassNotFoundException | NoClassDefFoundError e) {
// log4j is not available
ConsoleLogger.info("You're using Minecraft 1.6.x or older, Log4J support will be disabled");
ConsoleFilter filter = new ConsoleFilter();
logger.setFilter(filter);
Bukkit.getLogger().setFilter(filter);
Logger.getLogger("Minecraft").setFilter(filter);
}
}
// Set the console filter to remove the passwords
private static void setLog4JFilter() {
org.apache.logging.log4j.core.Logger logger;
logger = (org.apache.logging.log4j.core.Logger) LogManager.getRootLogger();
logger.addFilter(new Log4JFilter());
}
public void scheduleRecallEmailTask() {
if (!settings.getProperty(RECALL_PLAYERS)) {
return;
}
bukkitService.runTaskTimerAsynchronously(new Runnable() {
@Override
public void run() {
for (PlayerAuth auth : dataSource.getLoggedPlayers()) {
String email = auth.getEmail();
if (StringUtils.isEmpty(email) || "your@email.com".equalsIgnoreCase(email)) {
Player player = bukkitService.getPlayerExact(auth.getRealName());
if (player != null) {
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
}
}
}
}
}, 1, TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL));
}
}

View File

@ -2,6 +2,8 @@ package fr.xephi.authme.initialization;
/**
* Interface for reloadable entities.
*
* @see fr.xephi.authme.command.executable.authme.ReloadCommand
*/
public interface Reloadable {

View File

@ -4,6 +4,8 @@ import fr.xephi.authme.settings.Settings;
/**
* Interface for classes that keep a local copy of certain settings.
*
* @see fr.xephi.authme.command.executable.authme.ReloadCommand
*/
public interface SettingsDependent {

View File

@ -0,0 +1,44 @@
package fr.xephi.authme.initialization;
import com.github.authme.configme.knownproperties.ConfigurationData;
import com.github.authme.configme.resource.PropertyResource;
import com.github.authme.configme.resource.YamlFileResource;
import fr.xephi.authme.settings.Settings;
import fr.xephi.authme.settings.SettingsMigrationService;
import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever;
import fr.xephi.authme.util.FileUtils;
import javax.inject.Inject;
import javax.inject.Provider;
import java.io.File;
/**
* Initializes the settings.
*/
public class SettingsProvider implements Provider<Settings> {
@Inject
@DataFolder
private File dataFolder;
SettingsProvider() {
}
/**
* Loads the plugin's settings.
*
* @return the settings instance, or null if it could not be constructed
*/
@Override
public Settings get() {
File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) {
FileUtils.create(configFile);
}
PropertyResource resource = new YamlFileResource(configFile);
SettingsMigrationService migrationService = new SettingsMigrationService(dataFolder);
ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData();
return new Settings(dataFolder, resource, migrationService, configurationData);
}
}

View File

@ -17,6 +17,7 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;
import javax.inject.Inject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
@ -40,7 +41,8 @@ public class BukkitService implements SettingsDependent {
private Method getOnlinePlayers;
private boolean useAsyncTasks;
public BukkitService(AuthMe authMe, Settings settings) {
@Inject
BukkitService(AuthMe authMe, Settings settings) {
this.authMe = authMe;
getOnlinePlayersIsCollection = initializeOnlinePlayersIsCollectionField();
reload(settings);

View File

@ -98,6 +98,22 @@ public final class FileUtils {
}
}
/**
* Creates the given file or throws an exception.
*
* @param file the file to create
*/
public static void create(File file) {
try {
boolean result = file.createNewFile();
if (!result) {
throw new IllegalStateException("Could not create file '" + file + "'");
}
} catch (IOException e) {
throw new IllegalStateException("Error while creating file '" + file + "'", e);
}
}
/**
* Construct a file path from the given elements, i.e. separate the given elements by the file separator.
*