Implement the option to combine yaml/json/hocon storage files into one
This commit is contained in:
parent
4e87489dc1
commit
328353d053
@ -36,15 +36,13 @@ import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on Bukkit.
|
||||
*/
|
||||
@ -197,8 +195,8 @@ public class LPBukkitBootstrap extends JavaPlugin implements LuckPermsBootstrap
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataDirectory() {
|
||||
return getDataFolder();
|
||||
public Path getDataDirectory() {
|
||||
return getDataFolder().toPath().toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,15 +35,13 @@ import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on BungeeCord.
|
||||
*/
|
||||
@ -157,8 +155,8 @@ public class LPBungeeBootstrap extends Plugin implements LuckPermsBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataDirectory() {
|
||||
return getDataFolder();
|
||||
public Path getDataDirectory() {
|
||||
return getDataFolder().toPath().toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -36,7 +36,6 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@ -57,16 +56,14 @@ public class ExportCommand extends SingleCommand {
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
File f = new File(plugin.getBootstrap().getDataDirectory(), args.get(0));
|
||||
if (f.exists()) {
|
||||
Message.LOG_EXPORT_ALREADY_EXISTS.send(sender, f.getAbsolutePath());
|
||||
Path path = plugin.getBootstrap().getDataDirectory().resolve(args.get(0));
|
||||
if (Files.exists(path)) {
|
||||
Message.LOG_EXPORT_ALREADY_EXISTS.send(sender, path.toString());
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
Path path = f.toPath();
|
||||
|
||||
try {
|
||||
f.createNewFile();
|
||||
Files.createFile(path);
|
||||
} catch (IOException e) {
|
||||
Message.LOG_EXPORT_FAILURE.send(sender);
|
||||
e.printStackTrace();
|
||||
@ -74,7 +71,7 @@ public class ExportCommand extends SingleCommand {
|
||||
}
|
||||
|
||||
if (!Files.isWritable(path)) {
|
||||
Message.LOG_EXPORT_NOT_WRITABLE.send(sender, f.getAbsolutePath());
|
||||
Message.LOG_EXPORT_NOT_WRITABLE.send(sender, path.toString());
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,6 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.sender.Sender;
|
||||
import me.lucko.luckperms.common.utils.Predicates;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
@ -58,16 +57,14 @@ public class ImportCommand extends SingleCommand {
|
||||
return CommandResult.STATE_ERROR;
|
||||
}
|
||||
|
||||
File f = new File(plugin.getBootstrap().getDataDirectory(), args.get(0));
|
||||
if (!f.exists()) {
|
||||
Message.IMPORT_LOG_DOESNT_EXIST.send(sender, f.getAbsolutePath());
|
||||
Path path = plugin.getBootstrap().getDataDirectory().resolve(args.get(0));
|
||||
if (!Files.exists(path)) {
|
||||
Message.IMPORT_LOG_DOESNT_EXIST.send(sender, path.toString());
|
||||
return CommandResult.INVALID_ARGS;
|
||||
}
|
||||
|
||||
Path path = f.toPath();
|
||||
|
||||
if (!Files.isReadable(path)) {
|
||||
Message.IMPORT_LOG_NOT_READABLE.send(sender, f.getAbsolutePath());
|
||||
Message.IMPORT_LOG_NOT_READABLE.send(sender, path.toString());
|
||||
return CommandResult.FAILURE;
|
||||
}
|
||||
|
||||
|
@ -34,10 +34,10 @@ import me.lucko.luckperms.common.contexts.ContextSetJsonSerializer;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* A wrapper for the 'contexts.json' file.
|
||||
@ -53,19 +53,23 @@ public class ContextsFile {
|
||||
}
|
||||
|
||||
public void load() {
|
||||
File file = new File(this.configuration.getPlugin().getBootstrap().getDataDirectory(), "contexts.json");
|
||||
File oldFile = new File(this.configuration.getPlugin().getBootstrap().getDataDirectory(), "static-contexts.json");
|
||||
if (oldFile.exists()) {
|
||||
oldFile.renameTo(file);
|
||||
Path file = this.configuration.getPlugin().getBootstrap().getDataDirectory().resolve("contexts.json");
|
||||
Path oldFile = this.configuration.getPlugin().getBootstrap().getDataDirectory().resolve("static-contexts.json");
|
||||
if (Files.exists(oldFile)) {
|
||||
try {
|
||||
Files.move(oldFile, file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
if (!Files.exists(file)) {
|
||||
save();
|
||||
return;
|
||||
}
|
||||
|
||||
boolean save = false;
|
||||
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
|
||||
JsonObject data = new Gson().fromJson(reader, JsonObject.class);
|
||||
|
||||
if (data.has("context")) {
|
||||
@ -91,10 +95,9 @@ public class ContextsFile {
|
||||
}
|
||||
|
||||
public void save() {
|
||||
File file = new File(this.configuration.getPlugin().getBootstrap().getDataDirectory(), "contexts.json");
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
Path file = this.configuration.getPlugin().getBootstrap().getDataDirectory().resolve("contexts.json");
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
|
||||
JsonObject data = new JsonObject();
|
||||
data.add("static-contexts", ContextSetJsonSerializer.serializeContextSet(this.staticContexts));
|
||||
data.add("default-contexts", ContextSetJsonSerializer.serializeContextSet(this.defaultContexts));
|
||||
|
@ -34,11 +34,12 @@ import me.lucko.luckperms.common.dependencies.relocation.RelocationHandler;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.StorageType;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
@ -57,7 +58,7 @@ public class DependencyManager {
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final MessageDigest digest;
|
||||
private final DependencyRegistry registry;
|
||||
private final EnumMap<Dependency, File> loaded = new EnumMap<>(Dependency.class);
|
||||
private final EnumMap<Dependency, Path> loaded = new EnumMap<>(Dependency.class);
|
||||
private final Map<ImmutableSet<Dependency>, IsolatedClassLoader> loaders = new HashMap<>();
|
||||
private RelocationHandler relocationHandler = null;
|
||||
|
||||
@ -78,12 +79,13 @@ public class DependencyManager {
|
||||
return this.relocationHandler;
|
||||
}
|
||||
|
||||
private File getSaveDirectory() {
|
||||
File saveDirectory = new File(this.plugin.getBootstrap().getDataDirectory(), "lib");
|
||||
if (!(saveDirectory.exists() || saveDirectory.mkdirs())) {
|
||||
throw new RuntimeException("Unable to create lib dir - " + saveDirectory.getPath());
|
||||
private Path getSaveDirectory() {
|
||||
Path saveDirectory = this.plugin.getBootstrap().getDataDirectory().resolve("lib");
|
||||
try {
|
||||
Files.createDirectories(saveDirectory);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to create lib directory", e);
|
||||
}
|
||||
|
||||
return saveDirectory;
|
||||
}
|
||||
|
||||
@ -106,7 +108,7 @@ public class DependencyManager {
|
||||
.map(this.loaded::get)
|
||||
.map(file -> {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
return file.toUri().toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -124,7 +126,7 @@ public class DependencyManager {
|
||||
}
|
||||
|
||||
public void loadDependencies(Set<Dependency> dependencies) {
|
||||
File saveDirectory = getSaveDirectory();
|
||||
Path saveDirectory = getSaveDirectory();
|
||||
|
||||
// create a list of file sources
|
||||
List<Source> sources = new ArrayList<>();
|
||||
@ -136,7 +138,7 @@ public class DependencyManager {
|
||||
}
|
||||
|
||||
try {
|
||||
File file = downloadDependency(saveDirectory, dependency);
|
||||
Path file = downloadDependency(saveDirectory, dependency);
|
||||
sources.add(new Source(dependency, file));
|
||||
} catch (Throwable e) {
|
||||
this.plugin.getLogger().severe("Exception whilst downloading dependency " + dependency.name());
|
||||
@ -156,11 +158,11 @@ public class DependencyManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
File input = source.file;
|
||||
File output = new File(input.getParentFile(), "remapped-" + input.getName());
|
||||
Path input = source.file;
|
||||
Path output = input.getParent().resolve("remapped-" + input.getFileName().toString());
|
||||
|
||||
// if the remapped file exists already, just use that.
|
||||
if (output.exists()) {
|
||||
if (Files.exists(output)) {
|
||||
remappedJars.add(new Source(source.dependency, output));
|
||||
continue;
|
||||
}
|
||||
@ -169,7 +171,7 @@ public class DependencyManager {
|
||||
RelocationHandler relocationHandler = getRelocationHandler();
|
||||
|
||||
// attempt to remap the jar.
|
||||
this.plugin.getLogger().info("Attempting to apply relocations to " + input.getName() + "...");
|
||||
this.plugin.getLogger().info("Attempting to apply relocations to " + input.getFileName().toString() + "...");
|
||||
relocationHandler.remap(input, output, relocations);
|
||||
|
||||
remappedJars.add(new Source(source.dependency, output));
|
||||
@ -190,18 +192,18 @@ public class DependencyManager {
|
||||
this.plugin.getBootstrap().getPluginClassLoader().loadJar(source.file);
|
||||
this.loaded.put(source.dependency, source.file);
|
||||
} catch (Throwable e) {
|
||||
this.plugin.getLogger().severe("Failed to load dependency jar '" + source.file.getName() + "'.");
|
||||
this.plugin.getLogger().severe("Failed to load dependency jar '" + source.file.getFileName().toString() + "'.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File downloadDependency(File saveDirectory, Dependency dependency) throws Exception {
|
||||
private Path downloadDependency(Path saveDirectory, Dependency dependency) throws Exception {
|
||||
String fileName = dependency.name().toLowerCase() + "-" + dependency.getVersion() + ".jar";
|
||||
File file = new File(saveDirectory, fileName);
|
||||
Path file = saveDirectory.resolve(fileName);
|
||||
|
||||
// if the file already exists, don't attempt to re-download it.
|
||||
if (file.exists()) {
|
||||
if (Files.exists(file)) {
|
||||
return file;
|
||||
}
|
||||
|
||||
@ -226,11 +228,11 @@ public class DependencyManager {
|
||||
}
|
||||
|
||||
// if the checksum matches, save the content to disk
|
||||
Files.write(file.toPath(), bytes);
|
||||
Files.write(file, bytes);
|
||||
}
|
||||
|
||||
// ensure the file saved correctly
|
||||
if (!file.exists()) {
|
||||
if (!Files.exists(file)) {
|
||||
throw new IllegalStateException("File not present. - " + file.toString());
|
||||
} else {
|
||||
return file;
|
||||
@ -239,9 +241,9 @@ public class DependencyManager {
|
||||
|
||||
private static final class Source {
|
||||
private final Dependency dependency;
|
||||
private final File file;
|
||||
private final Path file;
|
||||
|
||||
private Source(Dependency dependency, File file) {
|
||||
private Source(Dependency dependency, Path file) {
|
||||
this.dependency = dependency;
|
||||
this.file = file;
|
||||
}
|
||||
|
@ -47,9 +47,12 @@ public class DependencyRegistry {
|
||||
));
|
||||
|
||||
private static final Map<StorageType, List<Dependency>> STORAGE_DEPENDENCIES = ImmutableMap.<StorageType, List<Dependency>>builder()
|
||||
.put(StorageType.JSON, ImmutableList.of(Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON))
|
||||
.put(StorageType.YAML, ImmutableList.of(Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML))
|
||||
.put(StorageType.JSON, ImmutableList.of(Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON))
|
||||
.put(StorageType.HOCON, ImmutableList.of(Dependency.HOCON_CONFIG, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON))
|
||||
.put(StorageType.YAML_COMBINED, ImmutableList.of(Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_YAML))
|
||||
.put(StorageType.JSON_COMBINED, ImmutableList.of(Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_GSON))
|
||||
.put(StorageType.HOCON_COMBINED, ImmutableList.of(Dependency.HOCON_CONFIG, Dependency.CONFIGURATE_CORE, Dependency.CONFIGURATE_HOCON))
|
||||
.put(StorageType.MONGODB, ImmutableList.of(Dependency.MONGODB_DRIVER))
|
||||
.put(StorageType.MARIADB, ImmutableList.of(Dependency.MARIADB_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI))
|
||||
.put(StorageType.MYSQL, ImmutableList.of(Dependency.MYSQL_DRIVER, Dependency.SLF4J_API, Dependency.SLF4J_SIMPLE, Dependency.HIKARI))
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
package me.lucko.luckperms.common.dependencies.classloader;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Represents the plugins classloader
|
||||
@ -35,6 +35,6 @@ public interface PluginClassLoader {
|
||||
|
||||
void loadJar(URL url);
|
||||
|
||||
void loadJar(File file);
|
||||
void loadJar(Path file);
|
||||
|
||||
}
|
||||
|
@ -25,12 +25,12 @@
|
||||
|
||||
package me.lucko.luckperms.common.dependencies.classloader;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ReflectionClassLoader implements PluginClassLoader {
|
||||
private static final Method ADD_URL_METHOD;
|
||||
@ -65,9 +65,9 @@ public class ReflectionClassLoader implements PluginClassLoader {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadJar(File file) {
|
||||
public void loadJar(Path file) {
|
||||
try {
|
||||
loadJar(file.toURI().toURL());
|
||||
loadJar(file.toUri().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import me.lucko.luckperms.common.dependencies.classloader.IsolatedClassLoader;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Path;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@ -70,14 +71,14 @@ public class RelocationHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public void remap(File input, File output, List<Relocation> relocations) throws Exception {
|
||||
public void remap(Path input, Path output, List<Relocation> relocations) throws Exception {
|
||||
Map<String, String> mappings = new HashMap<>();
|
||||
for (Relocation relocation : relocations) {
|
||||
mappings.put(relocation.getPattern(), relocation.getRelocatedPattern());
|
||||
}
|
||||
|
||||
// create and invoke a new relocator
|
||||
Object relocator = this.jarRelocatorConstructor.newInstance(input, output, mappings);
|
||||
Object relocator = this.jarRelocatorConstructor.newInstance(input.toFile(), output.toFile(), mappings);
|
||||
this.jarRelocatorRunMethod.invoke(relocator);
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import me.lucko.luckperms.common.locale.command.CommandSpecData;
|
||||
import me.lucko.luckperms.common.locale.message.Message;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Manages translations
|
||||
@ -43,7 +43,7 @@ public interface LocaleManager {
|
||||
* @param plugin the plugin to log to
|
||||
* @param file the file to load from
|
||||
*/
|
||||
void tryLoad(LuckPermsPlugin plugin, File file);
|
||||
void tryLoad(LuckPermsPlugin plugin, Path file);
|
||||
|
||||
/**
|
||||
* Loads a locale file
|
||||
@ -51,7 +51,7 @@ public interface LocaleManager {
|
||||
* @param file the file to load from
|
||||
* @throws Exception if the process fails
|
||||
*/
|
||||
void loadFromFile(File file) throws Exception;
|
||||
void loadFromFile(Path file) throws Exception;
|
||||
|
||||
/**
|
||||
* Gets the size of loaded translations
|
||||
|
@ -35,9 +35,9 @@ import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
|
||||
public class SimpleLocaleManager implements LocaleManager {
|
||||
@ -46,8 +46,8 @@ public class SimpleLocaleManager implements LocaleManager {
|
||||
private Map<CommandSpec, CommandSpecData> commands = ImmutableMap.of();
|
||||
|
||||
@Override
|
||||
public void tryLoad(LuckPermsPlugin plugin, File file) {
|
||||
if (file.exists()) {
|
||||
public void tryLoad(LuckPermsPlugin plugin, Path file) {
|
||||
if (Files.exists(file)) {
|
||||
plugin.getLogger().info("Found lang.yml - loading messages...");
|
||||
try {
|
||||
loadFromFile(file);
|
||||
@ -59,8 +59,8 @@ public class SimpleLocaleManager implements LocaleManager {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void loadFromFile(File file) throws Exception {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
public void loadFromFile(Path file) throws Exception {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
|
||||
ImmutableMap.Builder<Message, String> messages = ImmutableMap.builder();
|
||||
ImmutableMap.Builder<CommandSpec, CommandSpecData> commands = ImmutableMap.builder();
|
||||
|
||||
|
@ -59,7 +59,7 @@ import me.lucko.luckperms.common.tasks.UpdateTask;
|
||||
import me.lucko.luckperms.common.treeview.PermissionRegistry;
|
||||
import me.lucko.luckperms.common.verbose.VerboseHandler;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@ -114,7 +114,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
|
||||
// load locale
|
||||
this.localeManager = new SimpleLocaleManager();
|
||||
this.localeManager.tryLoad(this, new File(getBootstrap().getConfigDirectory(), "lang.yml"));
|
||||
this.localeManager.tryLoad(this, getBootstrap().getConfigDirectory().resolve("lang.yml"));
|
||||
|
||||
// now the configuration is loaded, we can create a storage factory and load initial dependencies
|
||||
StorageFactory storageFactory = new StorageFactory(this);
|
||||
@ -127,8 +127,11 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin {
|
||||
// initialise the storage
|
||||
// first, setup the file watcher, if enabled
|
||||
if (getConfiguration().get(ConfigKeys.WATCH_FILES)) {
|
||||
this.fileWatcher = new FileWatcher(this);
|
||||
getBootstrap().getScheduler().asyncRepeating(this.fileWatcher, 30L);
|
||||
try {
|
||||
this.fileWatcher = new FileWatcher(this, getBootstrap().getDataDirectory());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// initialise storage
|
||||
|
@ -29,8 +29,8 @@ import me.lucko.luckperms.api.platform.PlatformType;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.PluginClassLoader;
|
||||
import me.lucko.luckperms.common.plugin.SchedulerAdapter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@ -132,14 +132,14 @@ public interface LuckPermsBootstrap {
|
||||
*
|
||||
* @return the platforms data folder
|
||||
*/
|
||||
File getDataDirectory();
|
||||
Path getDataDirectory();
|
||||
|
||||
/**
|
||||
* Gets the plugins configuration directory
|
||||
*
|
||||
* @return the config directory
|
||||
*/
|
||||
default File getConfigDirectory() {
|
||||
default Path getConfigDirectory() {
|
||||
return getDataDirectory();
|
||||
}
|
||||
|
||||
|
@ -32,9 +32,11 @@ import me.lucko.luckperms.common.config.ConfigKeys;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.dao.AbstractDao;
|
||||
import me.lucko.luckperms.common.storage.dao.SplitStorageDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.HoconDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.JsonDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.YamlDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.CombinedConfigurateDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.SeparatedConfigurateDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.HoconLoader;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.JsonLoader;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.YamlLoader;
|
||||
import me.lucko.luckperms.common.storage.dao.mongodb.MongoDao;
|
||||
import me.lucko.luckperms.common.storage.dao.sql.SqlDao;
|
||||
import me.lucko.luckperms.common.storage.dao.sql.connection.file.H2ConnectionFactory;
|
||||
@ -45,7 +47,6 @@ import me.lucko.luckperms.common.storage.dao.sql.connection.hikari.PostgreConnec
|
||||
import me.lucko.luckperms.common.storage.provider.StorageProviders;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -139,13 +140,13 @@ public class StorageFactory {
|
||||
case SQLITE:
|
||||
return new SqlDao(
|
||||
this.plugin,
|
||||
new SQLiteConnectionFactory(this.plugin, new File(this.plugin.getBootstrap().getDataDirectory(), "luckperms-sqlite.db")),
|
||||
new SQLiteConnectionFactory(this.plugin, this.plugin.getBootstrap().getDataDirectory().resolve("luckperms-sqlite.db")),
|
||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||
);
|
||||
case H2:
|
||||
return new SqlDao(
|
||||
this.plugin,
|
||||
new H2ConnectionFactory(this.plugin, new File(this.plugin.getBootstrap().getDataDirectory(), "luckperms-h2")),
|
||||
new H2ConnectionFactory(this.plugin, this.plugin.getBootstrap().getDataDirectory().resolve("luckperms-h2")),
|
||||
this.plugin.getConfiguration().get(ConfigKeys.SQL_TABLE_PREFIX)
|
||||
);
|
||||
case POSTGRESQL:
|
||||
@ -162,11 +163,19 @@ public class StorageFactory {
|
||||
this.plugin.getConfiguration().get(ConfigKeys.MONGODB_CONNECTION_URI)
|
||||
);
|
||||
case YAML:
|
||||
return new YamlDao(this.plugin, "yaml-storage");
|
||||
return new SeparatedConfigurateDao(this.plugin, new YamlLoader(), "YAML", ".yml", "yaml-storage");
|
||||
case JSON:
|
||||
return new SeparatedConfigurateDao(this.plugin, new JsonLoader(), "JSON", ".json", "json-storage");
|
||||
case HOCON:
|
||||
return new HoconDao(this.plugin, "hocon-storage");
|
||||
return new SeparatedConfigurateDao(this.plugin, new HoconLoader(), "HOCON", ".conf", "hocon-storage");
|
||||
case YAML_COMBINED:
|
||||
return new CombinedConfigurateDao(this.plugin, new YamlLoader(), "YAML Combined", ".yml", "yaml-storage");
|
||||
case JSON_COMBINED:
|
||||
return new CombinedConfigurateDao(this.plugin, new JsonLoader(), "JSON Combined", ".json", "json-storage");
|
||||
case HOCON_COMBINED:
|
||||
return new CombinedConfigurateDao(this.plugin, new HoconLoader(), "HOCON Combined", ".conf", "hocon-storage");
|
||||
default:
|
||||
return new JsonDao(this.plugin, "json-storage");
|
||||
throw new RuntimeException("Unknown method: " + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,15 +31,25 @@ import java.util.List;
|
||||
|
||||
public enum StorageType {
|
||||
|
||||
JSON("JSON", "json", "flatfile"),
|
||||
// Config file based
|
||||
YAML("YAML", "yaml", "yml"),
|
||||
JSON("JSON", "json", "flatfile"),
|
||||
HOCON("HOCON", "hocon"),
|
||||
YAML_COMBINED("YAML Combined", "yaml-combined"),
|
||||
JSON_COMBINED("JSON Combined", "json-combined"),
|
||||
HOCON_COMBINED("HOCON Combined", "hocon-combined"),
|
||||
|
||||
// Remote databases
|
||||
MONGODB("MongoDB", "mongodb"),
|
||||
MARIADB("MariaDB", "mariadb"),
|
||||
MYSQL("MySQL", "mysql"),
|
||||
POSTGRESQL("PostgreSQL", "postgresql"),
|
||||
|
||||
// Local databases
|
||||
SQLITE("SQLite", "sqlite"),
|
||||
H2("H2", "h2"),
|
||||
|
||||
// Custom
|
||||
CUSTOM("Custom", "custom");
|
||||
|
||||
private final String name;
|
||||
|
@ -62,7 +62,7 @@ public abstract class AbstractDao {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public abstract void init();
|
||||
public abstract void init() throws Exception;
|
||||
|
||||
public abstract void shutdown();
|
||||
|
||||
|
@ -68,7 +68,7 @@ public class SplitStorageDao extends AbstractDao {
|
||||
}
|
||||
}
|
||||
if (failed) {
|
||||
throw new RuntimeException("One of the backing failed to init");
|
||||
throw new RuntimeException("One of the backings failed to init");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,44 +25,40 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import me.lucko.luckperms.api.ChatMetaType;
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.api.Node;
|
||||
import me.lucko.luckperms.api.context.ImmutableContextSet;
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.contexts.ContextSetConfigurateSerializer;
|
||||
import me.lucko.luckperms.common.managers.group.GroupManager;
|
||||
import me.lucko.luckperms.common.managers.track.TrackManager;
|
||||
import me.lucko.luckperms.common.model.Group;
|
||||
import me.lucko.luckperms.common.model.Track;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.MetaType;
|
||||
import me.lucko.luckperms.common.node.NodeFactory;
|
||||
import me.lucko.luckperms.common.node.NodeHeldPermission;
|
||||
import me.lucko.luckperms.common.node.NodeModel;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.references.UserIdentifier;
|
||||
import me.lucko.luckperms.common.storage.PlayerSaveResult;
|
||||
import me.lucko.luckperms.common.storage.dao.AbstractDao;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.ConfigurateLoader;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.JsonLoader;
|
||||
import me.lucko.luckperms.common.utils.ImmutableCollectors;
|
||||
import me.lucko.luckperms.common.utils.Uuids;
|
||||
import me.lucko.luckperms.common.utils.MoreFiles;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.SimpleConfigurationNode;
|
||||
import ninja.leaping.configurate.Types;
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@ -76,143 +72,70 @@ import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class ConfigurateDao extends AbstractDao {
|
||||
/**
|
||||
* Abstract implementation using configurate {@link ConfigurationNode}s to serialize and deserialize
|
||||
* data.
|
||||
*/
|
||||
public abstract class AbstractConfigurateDao extends AbstractDao {
|
||||
|
||||
// the loader responsible for i/o
|
||||
protected final ConfigurateLoader loader;
|
||||
|
||||
// the name of the data directory
|
||||
private final String dataDirectoryName;
|
||||
// the data directory
|
||||
protected Path dataDirectory;
|
||||
|
||||
// the uuid cache instance
|
||||
private final FileUuidCache uuidCache = new FileUuidCache();
|
||||
// the action logger instance
|
||||
private final FileActionLogger actionLogger = new FileActionLogger();
|
||||
|
||||
private final String fileExtension;
|
||||
private final String dataFolderName;
|
||||
// the file used to store uuid data
|
||||
private Path uuidDataFile;
|
||||
// the file used to store logged actions
|
||||
private Path actionLogFile;
|
||||
|
||||
private File uuidDataFile;
|
||||
private File actionLogFile;
|
||||
|
||||
private File usersDirectory;
|
||||
private File groupsDirectory;
|
||||
private File tracksDirectory;
|
||||
|
||||
protected ConfigurateDao(LuckPermsPlugin plugin, String name, String fileExtension, String dataFolderName) {
|
||||
protected AbstractConfigurateDao(LuckPermsPlugin plugin, ConfigurateLoader loader, String name, String dataDirectoryName) {
|
||||
super(plugin, name);
|
||||
this.fileExtension = fileExtension;
|
||||
this.dataFolderName = dataFolderName;
|
||||
this.loader = loader;
|
||||
this.dataDirectoryName = dataDirectoryName;
|
||||
}
|
||||
|
||||
public String getFileExtension() {
|
||||
return this.fileExtension;
|
||||
}
|
||||
/**
|
||||
* Reads a configuration node from the given location
|
||||
*
|
||||
* @param location the location
|
||||
* @param name the name of the object
|
||||
* @return the node
|
||||
* @throws IOException if an io error occurs
|
||||
*/
|
||||
protected abstract ConfigurationNode readFile(StorageLocation location, String name) throws IOException;
|
||||
|
||||
protected abstract ConfigurationLoader<? extends ConfigurationNode> loader(Path path);
|
||||
/**
|
||||
* Saves a configuration node to the given location
|
||||
*
|
||||
* @param location the location
|
||||
* @param name the name of the object
|
||||
* @param node the node
|
||||
* @throws IOException if an io error occurs
|
||||
*/
|
||||
protected abstract void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException;
|
||||
|
||||
private ConfigurationNode readFile(StorageLocation location, String name) throws IOException {
|
||||
File file = new File(getDirectory(location), name + this.fileExtension);
|
||||
registerFileAction(location, file);
|
||||
return readFile(file);
|
||||
}
|
||||
|
||||
private ConfigurationNode readFile(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return loader(file.toPath()).load();
|
||||
}
|
||||
|
||||
private void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException {
|
||||
File file = new File(getDirectory(location), name + this.fileExtension);
|
||||
registerFileAction(location, file);
|
||||
saveFile(file, node);
|
||||
}
|
||||
|
||||
private void saveFile(File file, ConfigurationNode node) throws IOException {
|
||||
if (node == null) {
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
loader(file.toPath()).save(node);
|
||||
}
|
||||
|
||||
private File getDirectory(StorageLocation location) {
|
||||
switch (location) {
|
||||
case USER:
|
||||
return this.usersDirectory;
|
||||
case GROUP:
|
||||
return this.groupsDirectory;
|
||||
case TRACK:
|
||||
return this.tracksDirectory;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private FilenameFilter getFileTypeFilter() {
|
||||
return (dir, name) -> name.endsWith(this.fileExtension);
|
||||
}
|
||||
|
||||
private Exception reportException(String file, Exception ex) throws Exception {
|
||||
// used to report i/o exceptions which took place in a specific file
|
||||
protected RuntimeException reportException(String file, Exception ex) throws RuntimeException {
|
||||
this.plugin.getLogger().warn("Exception thrown whilst performing i/o: " + file);
|
||||
ex.printStackTrace();
|
||||
throw ex;
|
||||
}
|
||||
|
||||
private void registerFileAction(StorageLocation type, File file) {
|
||||
this.plugin.getFileWatcher().ifPresent(fileWatcher -> fileWatcher.registerChange(type, file.getName()));
|
||||
throw Throwables.propagate(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
File data = FileUtils.mkdirs(new File(this.plugin.getBootstrap().getDataDirectory(), this.dataFolderName));
|
||||
public void init() throws IOException {
|
||||
this.dataDirectory = this.plugin.getBootstrap().getDataDirectory().resolve(this.dataDirectoryName);
|
||||
Files.createDirectories(this.dataDirectory);
|
||||
|
||||
this.usersDirectory = FileUtils.mkdir(new File(data, "users"));
|
||||
this.groupsDirectory = FileUtils.mkdir(new File(data, "groups"));
|
||||
this.tracksDirectory = FileUtils.mkdir(new File(data, "tracks"));
|
||||
this.uuidDataFile = FileUtils.createNewFile(new File(data, "uuidcache.txt"));
|
||||
this.actionLogFile = FileUtils.createNewFile(new File(data, "actions.log"));
|
||||
|
||||
// Listen for file changes.
|
||||
this.plugin.getFileWatcher().ifPresent(watcher -> {
|
||||
watcher.subscribe("user", this.usersDirectory.toPath(), s -> {
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String user = s.substring(0, s.length() - this.fileExtension.length());
|
||||
UUID uuid = Uuids.parseNullable(user);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
User u = this.plugin.getUserManager().getIfLoaded(uuid);
|
||||
if (u != null) {
|
||||
this.plugin.getLogger().info("[FileWatcher] Refreshing user " + u.getFriendlyName());
|
||||
this.plugin.getStorage().loadUser(uuid, null);
|
||||
}
|
||||
});
|
||||
watcher.subscribe("group", this.groupsDirectory.toPath(), s -> {
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String groupName = s.substring(0, s.length() - this.fileExtension.length());
|
||||
this.plugin.getLogger().info("[FileWatcher] Refreshing group " + groupName);
|
||||
this.plugin.getUpdateTaskBuffer().request();
|
||||
});
|
||||
watcher.subscribe("track", this.tracksDirectory.toPath(), s -> {
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String trackName = s.substring(0, s.length() - this.fileExtension.length());
|
||||
this.plugin.getLogger().info("[FileWatcher] Refreshing track " + trackName);
|
||||
this.plugin.getStorage().loadAllTracks();
|
||||
});
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
this.uuidDataFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("uuidcache.txt"));
|
||||
this.actionLogFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("actions.log"));
|
||||
|
||||
this.uuidCache.load(this.uuidDataFile);
|
||||
this.actionLogger.init(this.actionLogFile);
|
||||
@ -235,70 +158,30 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
return Log.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
File[] files = getDirectory(StorageLocation.USER).listFiles(getFileTypeFilter());
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Users directory matched no files.");
|
||||
}
|
||||
protected ConfigurationNode processBulkUpdate(BulkUpdate bulkUpdate, ConfigurationNode node) {
|
||||
Set<NodeModel> nodes = readNodes(node);
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(bulkUpdate::apply)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
for (File file : files) {
|
||||
try {
|
||||
registerFileAction(StorageLocation.USER, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(bulkUpdate::apply)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (!nodes.equals(results)) {
|
||||
writeNodes(object, results);
|
||||
saveFile(file, object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getName(), e);
|
||||
}
|
||||
}
|
||||
if (nodes.equals(results)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
File[] files = getDirectory(StorageLocation.GROUP).listFiles(getFileTypeFilter());
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Groups directory matched no files.");
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
try {
|
||||
registerFileAction(StorageLocation.GROUP, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
Set<NodeModel> results = nodes.stream()
|
||||
.map(bulkUpdate::apply)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (!nodes.equals(results)) {
|
||||
writeNodes(object, results);
|
||||
saveFile(file, object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getName(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
writeNodes(node, results);
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User loadUser(UUID uuid, String username) throws Exception {
|
||||
public User loadUser(UUID uuid, String username) {
|
||||
User user = this.plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
|
||||
user.getIoLock().lock();
|
||||
try {
|
||||
ConfigurationNode object = readFile(StorageLocation.USER, uuid.toString());
|
||||
if (object != null) {
|
||||
String name = object.getNode("name").getString();
|
||||
user.getPrimaryGroup().setStoredValue(object.getNode(this instanceof JsonDao ? "primaryGroup" : "primary-group").getString());
|
||||
user.getPrimaryGroup().setStoredValue(object.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").getString());
|
||||
|
||||
Set<Node> nodes = readNodes(object).stream().map(NodeModel::toNode).collect(Collectors.toSet());
|
||||
user.setEnduringNodes(nodes);
|
||||
@ -329,16 +212,18 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveUser(User user) throws Exception {
|
||||
public void saveUser(User user) {
|
||||
user.getIoLock().lock();
|
||||
try {
|
||||
if (!this.plugin.getUserManager().shouldSave(user)) {
|
||||
saveFile(StorageLocation.USER, user.getUuid().toString(), null);
|
||||
} else {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
data.getNode("uuid").setValue(user.getUuid().toString());
|
||||
if (this instanceof SeparatedConfigurateDao) {
|
||||
data.getNode("uuid").setValue(user.getUuid().toString());
|
||||
}
|
||||
data.getNode("name").setValue(user.getName().orElse("null"));
|
||||
data.getNode(this instanceof JsonDao ? "primaryGroup" : "primary-group").setValue(user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME));
|
||||
data.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").setValue(user.getPrimaryGroup().getStoredValue().orElse(NodeFactory.DEFAULT_GROUP_NAME));
|
||||
|
||||
Set<NodeModel> nodes = user.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
writeNodes(data, nodes);
|
||||
@ -353,44 +238,7 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers() {
|
||||
String[] fileNames = this.usersDirectory.list(getFileTypeFilter());
|
||||
if (fileNames == null) return null;
|
||||
return Arrays.stream(fileNames)
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
.map(UUID::fromString)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeldPermission<UUID>> getUsersWithPermission(String permission) throws Exception {
|
||||
List<HeldPermission<UUID>> held = new ArrayList<>();
|
||||
File[] files = getDirectory(StorageLocation.USER).listFiles(getFileTypeFilter());
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Users directory matched no files.");
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
try {
|
||||
registerFileAction(StorageLocation.USER, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
UUID holder = UUID.fromString(file.getName().substring(0, file.getName().length() - this.fileExtension.length()));
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
continue;
|
||||
}
|
||||
held.add(NodeHeldPermission.of(holder, e));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getName(), e);
|
||||
}
|
||||
}
|
||||
return held;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createAndLoadGroup(String name) throws Exception {
|
||||
public Group createAndLoadGroup(String name) {
|
||||
Group group = this.plugin.getGroupManager().getOrMake(name);
|
||||
group.getIoLock().lock();
|
||||
try {
|
||||
@ -401,7 +249,9 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
group.setEnduringNodes(nodes);
|
||||
} else {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
data.getNode("name").setValue(group.getName());
|
||||
if (this instanceof SeparatedConfigurateDao) {
|
||||
data.getNode("name").setValue(group.getName());
|
||||
}
|
||||
|
||||
Set<NodeModel> nodes = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
writeNodes(data, nodes);
|
||||
@ -418,7 +268,7 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Group> loadGroup(String name) throws Exception {
|
||||
public Optional<Group> loadGroup(String name) {
|
||||
Group group = this.plugin.getGroupManager().getIfLoaded(name);
|
||||
if (group != null) {
|
||||
group.getIoLock().lock();
|
||||
@ -452,41 +302,13 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAllGroups() throws IOException {
|
||||
String[] fileNames = this.groupsDirectory.list(getFileTypeFilter());
|
||||
if (fileNames == null) {
|
||||
throw new IOException("Not a directory");
|
||||
}
|
||||
List<String> groups = Arrays.stream(fileNames)
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
boolean success = true;
|
||||
for (String g : groups) {
|
||||
try {
|
||||
loadGroup(g);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Exception occurred whilst loading a group");
|
||||
}
|
||||
|
||||
GroupManager<?> gm = this.plugin.getGroupManager();
|
||||
gm.getAll().values().stream()
|
||||
.filter(g -> !groups.contains(g.getName()))
|
||||
.forEach(gm::unload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveGroup(Group group) throws Exception {
|
||||
public void saveGroup(Group group) {
|
||||
group.getIoLock().lock();
|
||||
try {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
data.getNode("name").setValue(group.getName());
|
||||
if (this instanceof SeparatedConfigurateDao) {
|
||||
data.getNode("name").setValue(group.getName());
|
||||
}
|
||||
|
||||
Set<NodeModel> nodes = group.getEnduringNodes().values().stream().map(NodeModel::fromNode).collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
writeNodes(data, nodes);
|
||||
@ -500,15 +322,10 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteGroup(Group group) throws Exception {
|
||||
public void deleteGroup(Group group) {
|
||||
group.getIoLock().lock();
|
||||
try {
|
||||
File groupFile = new File(this.groupsDirectory, group.getName() + this.fileExtension);
|
||||
registerFileAction(StorageLocation.GROUP, groupFile);
|
||||
|
||||
if (groupFile.exists()) {
|
||||
groupFile.delete();
|
||||
}
|
||||
saveFile(StorageLocation.GROUP, group.getName(), null);
|
||||
} catch (Exception e) {
|
||||
throw reportException(group.getName(), e);
|
||||
} finally {
|
||||
@ -518,34 +335,7 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeldPermission<String>> getGroupsWithPermission(String permission) throws Exception {
|
||||
List<HeldPermission<String>> held = new ArrayList<>();
|
||||
File[] files = getDirectory(StorageLocation.GROUP).listFiles(getFileTypeFilter());
|
||||
if (files == null) {
|
||||
throw new IllegalStateException("Groups directory matched no files.");
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
try {
|
||||
registerFileAction(StorageLocation.GROUP, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
String holder = file.getName().substring(0, file.getName().length() - this.fileExtension.length());
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
continue;
|
||||
}
|
||||
held.add(NodeHeldPermission.of(holder, e));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getName(), e);
|
||||
}
|
||||
}
|
||||
return held;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track createAndLoadTrack(String name) throws Exception {
|
||||
public Track createAndLoadTrack(String name) {
|
||||
Track track = this.plugin.getTrackManager().getOrMake(name);
|
||||
track.getIoLock().lock();
|
||||
try {
|
||||
@ -559,7 +349,9 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
track.setGroups(groups);
|
||||
} else {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
data.getNode("name").setValue(name);
|
||||
if (this instanceof SeparatedConfigurateDao) {
|
||||
data.getNode("name").setValue(name);
|
||||
}
|
||||
data.getNode("groups").setValue(track.getGroups());
|
||||
saveFile(StorageLocation.TRACK, name, data);
|
||||
}
|
||||
@ -573,7 +365,7 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Track> loadTrack(String name) throws Exception {
|
||||
public Optional<Track> loadTrack(String name) {
|
||||
Track track = this.plugin.getTrackManager().getIfLoaded(name);
|
||||
if (track != null) {
|
||||
track.getIoLock().lock();
|
||||
@ -608,41 +400,13 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAllTracks() throws IOException {
|
||||
String[] fileNames = this.tracksDirectory.list(getFileTypeFilter());
|
||||
if (fileNames == null) {
|
||||
throw new IOException("Not a directory");
|
||||
}
|
||||
List<String> tracks = Arrays.stream(fileNames)
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
boolean success = true;
|
||||
for (String t : tracks) {
|
||||
try {
|
||||
loadTrack(t);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Exception occurred whilst loading a track");
|
||||
}
|
||||
|
||||
TrackManager<?> tm = this.plugin.getTrackManager();
|
||||
tm.getAll().values().stream()
|
||||
.filter(t -> !tracks.contains(t.getName()))
|
||||
.forEach(tm::unload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTrack(Track track) throws Exception {
|
||||
public void saveTrack(Track track) {
|
||||
track.getIoLock().lock();
|
||||
try {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
data.getNode("name").setValue(track.getName());
|
||||
if (this instanceof SeparatedConfigurateDao) {
|
||||
data.getNode("name").setValue(track.getName());
|
||||
}
|
||||
data.getNode("groups").setValue(track.getGroups());
|
||||
saveFile(StorageLocation.TRACK, track.getName(), data);
|
||||
} catch (Exception e) {
|
||||
@ -653,15 +417,10 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTrack(Track track) throws Exception {
|
||||
public void deleteTrack(Track track) {
|
||||
track.getIoLock().lock();
|
||||
try {
|
||||
File trackFile = new File(this.tracksDirectory, track.getName() + this.fileExtension);
|
||||
registerFileAction(StorageLocation.TRACK, trackFile);
|
||||
|
||||
if (trackFile.exists()) {
|
||||
trackFile.delete();
|
||||
}
|
||||
saveFile(StorageLocation.TRACK, track.getName(), null);
|
||||
} catch (Exception e) {
|
||||
throw reportException(track.getName(), e);
|
||||
} finally {
|
||||
@ -736,7 +495,7 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
return Maps.immutableEntry(entry.getKey().toString(), entry.getValue());
|
||||
}
|
||||
|
||||
private static Set<NodeModel> readNodes(ConfigurationNode data) {
|
||||
protected static Set<NodeModel> readNodes(ConfigurationNode data) {
|
||||
Set<NodeModel> nodes = new HashSet<>();
|
||||
|
||||
if (data.getNode("permissions").hasListChildren()) {
|
||||
@ -924,5 +683,4 @@ public abstract class ConfigurateDao extends AbstractDao {
|
||||
to.getNode("meta").setValue(metaSection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,372 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.managers.group.GroupManager;
|
||||
import me.lucko.luckperms.common.managers.track.TrackManager;
|
||||
import me.lucko.luckperms.common.node.NodeHeldPermission;
|
||||
import me.lucko.luckperms.common.node.NodeModel;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.ConfigurateLoader;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CombinedConfigurateDao extends AbstractConfigurateDao {
|
||||
private final String fileExtension;
|
||||
|
||||
private Path usersFile;
|
||||
private Path groupsFile;
|
||||
private Path tracksFile;
|
||||
|
||||
private CachedLoader usersLoader;
|
||||
private CachedLoader groupsLoader;
|
||||
private CachedLoader tracksLoader;
|
||||
|
||||
private final class CachedLoader {
|
||||
private final Path path;
|
||||
|
||||
private final ConfigurationLoader<? extends ConfigurationNode> loader;
|
||||
private ConfigurationNode node = null;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
private CachedLoader(Path path) {
|
||||
this.path = path;
|
||||
this.loader = CombinedConfigurateDao.super.loader.loader(path);
|
||||
reload();
|
||||
}
|
||||
|
||||
private void recordChange() {
|
||||
if (CombinedConfigurateDao.this.watcher != null) {
|
||||
CombinedConfigurateDao.this.watcher.recordChange(this.path.getFileName().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurationNode getNode() throws IOException {
|
||||
this.lock.lock();
|
||||
try {
|
||||
if (this.node == null) {
|
||||
this.node = this.loader.load();
|
||||
}
|
||||
|
||||
return this.node;
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void apply(Consumer<ConfigurationNode> action) throws IOException {
|
||||
apply(false, false, action);
|
||||
}
|
||||
|
||||
public void apply(boolean save, boolean reload, Consumer<ConfigurationNode> action) throws IOException {
|
||||
this.lock.lock();
|
||||
try {
|
||||
if (this.node == null || reload) {
|
||||
recordChange();
|
||||
this.node = this.loader.load();
|
||||
}
|
||||
|
||||
action.accept(this.node);
|
||||
|
||||
if (save) {
|
||||
recordChange();
|
||||
this.loader.save(this.node);
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void save() throws IOException {
|
||||
this.lock.lock();
|
||||
try {
|
||||
recordChange();
|
||||
this.loader.save(this.node);
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
this.lock.lock();
|
||||
try {
|
||||
this.node = null;
|
||||
try {
|
||||
recordChange();
|
||||
this.node = this.loader.load();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
this.lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FileWatcher.WatchedLocation watcher = null;
|
||||
|
||||
/**
|
||||
* Creates a new configurate dao
|
||||
*
|
||||
* @param plugin the plugin instance
|
||||
* @param name the name of this dao
|
||||
* @param fileExtension the file extension used by this instance, including a "." at the start
|
||||
* @param dataFolderName the name of the folder used to store data
|
||||
*/
|
||||
public CombinedConfigurateDao(LuckPermsPlugin plugin, ConfigurateLoader loader, String name, String fileExtension, String dataFolderName) {
|
||||
super(plugin, loader, name, dataFolderName);
|
||||
this.fileExtension = fileExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationNode readFile(StorageLocation location, String name) throws IOException {
|
||||
ConfigurationNode root = getStorageLoader(location).getNode();
|
||||
ConfigurationNode ret = root.getNode(name);
|
||||
return ret.isVirtual() ? null : ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException {
|
||||
getStorageLoader(location).apply(true, false, root -> root.getNode(name).setValue(node));
|
||||
}
|
||||
|
||||
private CachedLoader getStorageLoader(StorageLocation location) {
|
||||
switch (location) {
|
||||
case USER:
|
||||
return this.usersLoader;
|
||||
case GROUP:
|
||||
return this.groupsLoader;
|
||||
case TRACK:
|
||||
return this.tracksLoader;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
super.init();
|
||||
|
||||
this.usersFile = super.dataDirectory.resolve("users" + this.fileExtension);
|
||||
this.groupsFile = super.dataDirectory.resolve("groups" + this.fileExtension);
|
||||
this.tracksFile = super.dataDirectory.resolve("tracks" + this.fileExtension);
|
||||
|
||||
this.usersLoader = new CachedLoader(this.usersFile);
|
||||
this.groupsLoader = new CachedLoader(this.groupsFile);
|
||||
this.tracksLoader = new CachedLoader(this.tracksFile);
|
||||
|
||||
// Listen for file changes.
|
||||
FileWatcher watcher = this.plugin.getFileWatcher().orElse(null);
|
||||
if (watcher != null) {
|
||||
this.watcher = watcher.getWatcher(super.dataDirectory);
|
||||
this.watcher.addListener(path -> {
|
||||
if (path.getFileName().equals(this.usersFile.getFileName())) {
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in users file - reloading...");
|
||||
this.usersLoader.reload();
|
||||
this.plugin.getUpdateTaskBuffer().request();
|
||||
} else if (path.getFileName().equals(this.groupsFile.getFileName())) {
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in groups file - reloading...");
|
||||
this.groupsLoader.reload();
|
||||
this.plugin.getUpdateTaskBuffer().request();
|
||||
} else if (path.getFileName().equals(this.tracksFile.getFileName())) {
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in tracks file - reloading...");
|
||||
this.tracksLoader.reload();
|
||||
this.plugin.getStorage().loadAllTracks();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
try {
|
||||
this.usersLoader.save();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
this.groupsLoader.save();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try {
|
||||
this.tracksLoader.save();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
super.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
this.usersLoader.apply(true, true, root -> {
|
||||
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
|
||||
processBulkUpdate(bulkUpdate, entry.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
this.groupsLoader.apply(true, true, root -> {
|
||||
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
|
||||
processBulkUpdate(bulkUpdate, entry.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers() throws IOException {
|
||||
return this.usersLoader.getNode().getChildrenMap().keySet().stream()
|
||||
.map(Object::toString)
|
||||
.map(UUID::fromString)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeldPermission<UUID>> getUsersWithPermission(String permission) throws Exception {
|
||||
List<HeldPermission<UUID>> held = new ArrayList<>();
|
||||
this.usersLoader.apply(false, true, root -> {
|
||||
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
|
||||
try {
|
||||
UUID holder = UUID.fromString(entry.getKey().toString());
|
||||
ConfigurationNode object = entry.getValue();
|
||||
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
continue;
|
||||
}
|
||||
held.add(NodeHeldPermission.of(holder, e));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return held;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAllGroups() throws IOException {
|
||||
List<String> groups = new ArrayList<>();
|
||||
|
||||
this.groupsLoader.apply(false, true, root -> {
|
||||
groups.addAll(root.getChildrenMap().keySet().stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.toList()));
|
||||
});
|
||||
|
||||
boolean success = true;
|
||||
for (String g : groups) {
|
||||
try {
|
||||
loadGroup(g);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Exception occurred whilst loading a group");
|
||||
}
|
||||
|
||||
GroupManager<?> gm = this.plugin.getGroupManager();
|
||||
gm.getAll().values().stream()
|
||||
.filter(g -> !groups.contains(g.getName()))
|
||||
.forEach(gm::unload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeldPermission<String>> getGroupsWithPermission(String permission) throws Exception {
|
||||
List<HeldPermission<String>> held = new ArrayList<>();
|
||||
this.groupsLoader.apply(false, true, root -> {
|
||||
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
|
||||
try {
|
||||
String holder = entry.getKey().toString();
|
||||
ConfigurationNode object = entry.getValue();
|
||||
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
continue;
|
||||
}
|
||||
held.add(NodeHeldPermission.of(holder, e));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return held;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAllTracks() throws IOException {
|
||||
List<String> tracks = new ArrayList<>();
|
||||
|
||||
this.tracksLoader.apply(false, true, root -> {
|
||||
tracks.addAll(root.getChildrenMap().keySet().stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.toList()));
|
||||
});
|
||||
|
||||
boolean success = true;
|
||||
for (String t : tracks) {
|
||||
try {
|
||||
loadTrack(t);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Exception occurred whilst loading a track");
|
||||
}
|
||||
|
||||
TrackManager<?> tm = this.plugin.getTrackManager();
|
||||
tm.getAll().values().stream()
|
||||
.filter(t -> !tracks.contains(t.getName()))
|
||||
.forEach(tm::unload);
|
||||
}
|
||||
|
||||
}
|
@ -28,7 +28,7 @@ package me.lucko.luckperms.common.storage.dao.file;
|
||||
import me.lucko.luckperms.api.LogEntry;
|
||||
import me.lucko.luckperms.common.command.CommandManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Date;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
@ -40,9 +40,9 @@ public class FileActionLogger {
|
||||
private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s";
|
||||
private final Logger actionLogger = Logger.getLogger("luckperms_actions");
|
||||
|
||||
public void init(File file) {
|
||||
public void init(Path file) {
|
||||
try {
|
||||
FileHandler fh = new FileHandler(file.getAbsolutePath(), 0, 1, true);
|
||||
FileHandler fh = new FileHandler(file.toString(), 0, 1, true);
|
||||
fh.setFormatter(new Formatter() {
|
||||
@Override
|
||||
public String format(LogRecord record) {
|
||||
|
@ -35,10 +35,10 @@ import me.lucko.luckperms.common.utils.Uuids;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
@ -182,12 +182,12 @@ public class FileUuidCache {
|
||||
}
|
||||
}
|
||||
|
||||
public void load(File file) {
|
||||
if (!file.exists()) {
|
||||
public void load(Path file) {
|
||||
if (!Files.exists(file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
|
||||
String entry;
|
||||
while ((entry = reader.readLine()) != null) {
|
||||
entry = entry.trim();
|
||||
@ -201,8 +201,8 @@ public class FileUuidCache {
|
||||
}
|
||||
}
|
||||
|
||||
public void save(File file) {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
public void save(Path file) {
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
|
||||
writer.write("# LuckPerms UUID lookup cache");
|
||||
writer.newLine();
|
||||
for (Map.Entry<UUID, String> ent : this.lookupMap.entrySet()) {
|
||||
|
@ -26,6 +26,7 @@
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.utils.Iterators;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
@ -38,54 +39,35 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FileWatcher implements Runnable {
|
||||
public class FileWatcher {
|
||||
private static final WatchEvent.Kind[] KINDS = new WatchEvent.Kind[]{
|
||||
StandardWatchEventKinds.ENTRY_CREATE,
|
||||
StandardWatchEventKinds.ENTRY_DELETE,
|
||||
StandardWatchEventKinds.ENTRY_MODIFY
|
||||
};
|
||||
|
||||
private final LuckPermsPlugin plugin;
|
||||
private final Path basePath;
|
||||
private final Map<Path, WatchedLocation> watchedLocations;
|
||||
|
||||
private final Map<String, WatchedLocation> keyMap;
|
||||
private final Map<String, Long> internalChanges;
|
||||
private WatchService watchService = null;
|
||||
// the watchservice instance
|
||||
private final WatchService watchService;
|
||||
|
||||
public FileWatcher(LuckPermsPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
this.keyMap = Collections.synchronizedMap(new HashMap<>());
|
||||
this.internalChanges = Collections.synchronizedMap(new HashMap<>());
|
||||
try {
|
||||
this.watchService = plugin.getBootstrap().getDataDirectory().toPath().getFileSystem().newWatchService();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
public FileWatcher(LuckPermsPlugin plugin, Path basePath) throws IOException {
|
||||
this.watchedLocations = Collections.synchronizedMap(new HashMap<>());
|
||||
this.basePath = basePath;
|
||||
this.watchService = basePath.getFileSystem().newWatchService();
|
||||
|
||||
plugin.getBootstrap().getScheduler().asyncLater(this::initLocations, 25L);
|
||||
plugin.getBootstrap().getScheduler().asyncRepeating(this::tick, 10L);
|
||||
}
|
||||
|
||||
public void subscribe(String id, Path path, Consumer<String> consumer) {
|
||||
if (this.watchService == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register with a delay to ignore changes made at startup
|
||||
this.plugin.getBootstrap().getScheduler().asyncLater(() -> {
|
||||
this.keyMap.computeIfAbsent(id, s -> {
|
||||
WatchKey key;
|
||||
try {
|
||||
key = path.register(this.watchService, KINDS);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return new WatchedLocation(path, key, consumer);
|
||||
});
|
||||
}, 40L);
|
||||
}
|
||||
|
||||
public void registerChange(StorageLocation location, String fileName) {
|
||||
this.internalChanges.put(location.name().toLowerCase() + "/" + fileName, System.currentTimeMillis());
|
||||
public WatchedLocation getWatcher(Path path) {
|
||||
Path relativePath = this.basePath.relativize(path);
|
||||
return this.watchedLocations.computeIfAbsent(relativePath, p -> new WatchedLocation(this, p));
|
||||
}
|
||||
|
||||
public void close() {
|
||||
@ -100,21 +82,78 @@ public class FileWatcher implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long expireTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(4);
|
||||
// was either processed last time, or recently modified by the system.
|
||||
this.internalChanges.values().removeIf(lastChange -> lastChange < expireTime);
|
||||
private void initLocations() {
|
||||
for (WatchedLocation loc : this.watchedLocations.values()) {
|
||||
try {
|
||||
loc.setup();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> expired = new ArrayList<>();
|
||||
private void tick() {
|
||||
List<Path> expired = new ArrayList<>();
|
||||
for (Map.Entry<Path, WatchedLocation> ent : this.watchedLocations.entrySet()) {
|
||||
boolean valid = ent.getValue().tick();
|
||||
if (!valid) {
|
||||
new RuntimeException("WatchKey no longer valid: " + ent.getKey().toString()).printStackTrace();
|
||||
expired.add(ent.getKey());
|
||||
}
|
||||
}
|
||||
expired.forEach(this.watchedLocations::remove);
|
||||
}
|
||||
|
||||
for (Map.Entry<String, WatchedLocation> ent : this.keyMap.entrySet()) {
|
||||
String id = ent.getKey();
|
||||
Path path = ent.getValue().getPath();
|
||||
WatchKey key = ent.getValue().getKey();
|
||||
/**
|
||||
* Encapsulates a "watcher" in a specific directory.
|
||||
*/
|
||||
public static final class WatchedLocation {
|
||||
// the parent watcher
|
||||
private final FileWatcher watcher;
|
||||
|
||||
List<WatchEvent<?>> watchEvents = key.pollEvents();
|
||||
// the relative path to the directory being watched
|
||||
private final Path relativePath;
|
||||
|
||||
// the absolute path to the directory being watched
|
||||
private final Path absolutePath;
|
||||
|
||||
// the times of recent changes
|
||||
private final Map<String, Long> lastChange = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
// if the key is registered
|
||||
private boolean ready = false;
|
||||
|
||||
// the watch key
|
||||
private WatchKey key = null;
|
||||
|
||||
// the callback functions
|
||||
private final List<Consumer<Path>> callbacks = new CopyOnWriteArrayList<>();
|
||||
|
||||
private WatchedLocation(FileWatcher watcher, Path relativePath) {
|
||||
this.watcher = watcher;
|
||||
this.relativePath = relativePath;
|
||||
this.absolutePath = this.watcher.basePath.resolve(this.relativePath);
|
||||
}
|
||||
|
||||
private synchronized void setup() throws IOException {
|
||||
if (this.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.key = this.absolutePath.register(this.watcher.watchService, KINDS);
|
||||
this.ready = true;
|
||||
}
|
||||
|
||||
private boolean tick() {
|
||||
if (!this.ready) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// remove old change entries.
|
||||
long expireTime = System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(4);
|
||||
this.lastChange.values().removeIf(lastChange -> lastChange < expireTime);
|
||||
|
||||
List<WatchEvent<?>> watchEvents = this.key.pollEvents();
|
||||
for (WatchEvent<?> event : watchEvents) {
|
||||
Path context = (Path) event.context();
|
||||
|
||||
@ -122,7 +161,6 @@ public class FileWatcher implements Runnable {
|
||||
continue;
|
||||
}
|
||||
|
||||
Path file = path.resolve(context);
|
||||
String fileName = context.toString();
|
||||
|
||||
// ignore temporary changes
|
||||
@ -130,50 +168,26 @@ public class FileWatcher implements Runnable {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.internalChanges.containsKey(id + "/" + fileName)) {
|
||||
// This file was modified by the system.
|
||||
// ignore changes already registered to the system
|
||||
if (this.lastChange.containsKey(fileName)) {
|
||||
continue;
|
||||
}
|
||||
this.lastChange.put(fileName, System.currentTimeMillis());
|
||||
|
||||
this.internalChanges.put(id + "/" + fileName, System.currentTimeMillis());
|
||||
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in file: " + file.toString());
|
||||
|
||||
// Process the change
|
||||
ent.getValue().getFileConsumer().accept(fileName);
|
||||
// process the change
|
||||
Iterators.iterate(this.callbacks, cb -> cb.accept(context));
|
||||
}
|
||||
|
||||
boolean valid = key.reset();
|
||||
if (!valid) {
|
||||
new RuntimeException("WatchKey no longer valid: " + key.toString()).printStackTrace();
|
||||
expired.add(id);
|
||||
}
|
||||
// reset the watch key.
|
||||
return this.key.reset();
|
||||
}
|
||||
|
||||
expired.forEach(this.keyMap::remove);
|
||||
}
|
||||
|
||||
private static class WatchedLocation {
|
||||
private final Path path;
|
||||
private final WatchKey key;
|
||||
private final Consumer<String> fileConsumer;
|
||||
|
||||
public WatchedLocation(Path path, WatchKey key, Consumer<String> fileConsumer) {
|
||||
this.path = path;
|
||||
this.key = key;
|
||||
this.fileConsumer = fileConsumer;
|
||||
public void recordChange(String fileName) {
|
||||
this.lastChange.put(fileName, System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public Path getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public WatchKey getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public Consumer<String> getFileConsumer() {
|
||||
return this.fileConsumer;
|
||||
public void addListener(Consumer<Path> updateConsumer) {
|
||||
this.callbacks.add(updateConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,366 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import me.lucko.luckperms.api.HeldPermission;
|
||||
import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
|
||||
import me.lucko.luckperms.common.managers.group.GroupManager;
|
||||
import me.lucko.luckperms.common.managers.track.TrackManager;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
import me.lucko.luckperms.common.node.NodeHeldPermission;
|
||||
import me.lucko.luckperms.common.node.NodeModel;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
import me.lucko.luckperms.common.storage.dao.file.loader.ConfigurateLoader;
|
||||
import me.lucko.luckperms.common.utils.Uuids;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class SeparatedConfigurateDao extends AbstractConfigurateDao {
|
||||
private final String fileExtension;
|
||||
|
||||
private Path usersDirectory;
|
||||
private Path groupsDirectory;
|
||||
private Path tracksDirectory;
|
||||
|
||||
private FileWatcher.WatchedLocation userWatcher = null;
|
||||
private FileWatcher.WatchedLocation groupWatcher = null;
|
||||
private FileWatcher.WatchedLocation trackWatcher = null;
|
||||
|
||||
/**
|
||||
* Creates a new configurate dao
|
||||
*
|
||||
* @param plugin the plugin instance
|
||||
* @param name the name of this dao
|
||||
* @param fileExtension the file extension used by this instance, including a "." at the start
|
||||
* @param dataFolderName the name of the folder used to store data
|
||||
*/
|
||||
public SeparatedConfigurateDao(LuckPermsPlugin plugin, ConfigurateLoader loader, String name, String fileExtension, String dataFolderName) {
|
||||
super(plugin, loader, name, dataFolderName);
|
||||
this.fileExtension = fileExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurationNode readFile(StorageLocation location, String name) throws IOException {
|
||||
Path file = getDirectory(location).resolve(name + this.fileExtension);
|
||||
registerFileAction(location, file);
|
||||
return readFile(file);
|
||||
}
|
||||
|
||||
private ConfigurationNode readFile(Path file) throws IOException {
|
||||
if (!Files.exists(file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.loader.loader(file).load();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException {
|
||||
Path file = getDirectory(location).resolve(name + this.fileExtension);
|
||||
registerFileAction(location, file);
|
||||
saveFile(file, node);
|
||||
}
|
||||
|
||||
private void saveFile(Path file, ConfigurationNode node) throws IOException {
|
||||
if (node == null) {
|
||||
Files.deleteIfExists(file);
|
||||
return;
|
||||
}
|
||||
|
||||
this.loader.loader(file).save(node);
|
||||
}
|
||||
|
||||
private Path getDirectory(StorageLocation location) {
|
||||
switch (location) {
|
||||
case USER:
|
||||
return this.usersDirectory;
|
||||
case GROUP:
|
||||
return this.groupsDirectory;
|
||||
case TRACK:
|
||||
return this.tracksDirectory;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private Predicate<Path> getFileTypeFilter() {
|
||||
return path -> path.getFileName().toString().endsWith(this.fileExtension);
|
||||
}
|
||||
|
||||
private void registerFileAction(StorageLocation type, Path file) {
|
||||
switch (type) {
|
||||
case USER:
|
||||
if (this.userWatcher != null) {
|
||||
this.userWatcher.recordChange(file.getFileName().toString());
|
||||
}
|
||||
break;
|
||||
case GROUP:
|
||||
if (this.groupWatcher != null) {
|
||||
this.groupWatcher.recordChange(file.getFileName().toString());
|
||||
}
|
||||
break;
|
||||
case TRACK:
|
||||
if (this.trackWatcher != null) {
|
||||
this.trackWatcher.recordChange(file.getFileName().toString());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws IOException {
|
||||
super.init();
|
||||
|
||||
this.usersDirectory = Files.createDirectory(super.dataDirectory.resolve("users"));
|
||||
this.groupsDirectory = Files.createDirectory(super.dataDirectory.resolve("groups"));
|
||||
this.tracksDirectory = Files.createDirectory(super.dataDirectory.resolve("tracks"));
|
||||
|
||||
// Listen for file changes.
|
||||
FileWatcher watcher = this.plugin.getFileWatcher().orElse(null);
|
||||
if (watcher != null) {
|
||||
this.userWatcher = watcher.getWatcher(this.usersDirectory);
|
||||
this.userWatcher.addListener(path -> {
|
||||
String s = path.getFileName().toString();
|
||||
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String user = s.substring(0, s.length() - this.fileExtension.length());
|
||||
UUID uuid = Uuids.parseNullable(user);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
User u = this.plugin.getUserManager().getIfLoaded(uuid);
|
||||
if (u != null) {
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in user file for " + u.getFriendlyName() + " - reloading...");
|
||||
this.plugin.getStorage().loadUser(uuid, null);
|
||||
}
|
||||
});
|
||||
|
||||
this.groupWatcher = watcher.getWatcher(this.groupsDirectory);
|
||||
this.groupWatcher.addListener(path -> {
|
||||
String s = path.getFileName().toString();
|
||||
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String groupName = s.substring(0, s.length() - this.fileExtension.length());
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in group file for " + groupName + " - reloading...");
|
||||
this.plugin.getUpdateTaskBuffer().request();
|
||||
});
|
||||
|
||||
this.trackWatcher = watcher.getWatcher(this.tracksDirectory);
|
||||
this.trackWatcher.addListener(path -> {
|
||||
String s = path.getFileName().toString();
|
||||
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String trackName = s.substring(0, s.length() - this.fileExtension.length());
|
||||
this.plugin.getLogger().info("[FileWatcher] Detected change in track file for " + trackName + " - reloading...");
|
||||
this.plugin.getStorage().loadAllTracks();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
|
||||
if (bulkUpdate.getDataType().isIncludingUsers()) {
|
||||
try (Stream<Path> s = Files.list(getDirectory(StorageLocation.USER))) {
|
||||
s.filter(getFileTypeFilter()).forEach(file -> {
|
||||
try {
|
||||
registerFileAction(StorageLocation.USER, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
ConfigurationNode results = processBulkUpdate(bulkUpdate, object);
|
||||
if (results != null) {
|
||||
saveFile(file, object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getFileName().toString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (bulkUpdate.getDataType().isIncludingGroups()) {
|
||||
try (Stream<Path> s = Files.list(getDirectory(StorageLocation.GROUP))) {
|
||||
s.filter(getFileTypeFilter()).forEach(file -> {
|
||||
try {
|
||||
registerFileAction(StorageLocation.GROUP, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
ConfigurationNode results = processBulkUpdate(bulkUpdate, object);
|
||||
if (results != null) {
|
||||
saveFile(file, object);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getFileName().toString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<UUID> getUniqueUsers() throws IOException {
|
||||
try (Stream<Path> stream = Files.list(this.usersDirectory)) {
|
||||
return stream.filter(getFileTypeFilter())
|
||||
.map(p -> p.getFileName().toString())
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
.map(UUID::fromString)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeldPermission<UUID>> getUsersWithPermission(String permission) throws Exception {
|
||||
List<HeldPermission<UUID>> held = new ArrayList<>();
|
||||
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.USER))) {
|
||||
stream.filter(getFileTypeFilter())
|
||||
.forEach(file -> {
|
||||
String fileName = file.getFileName().toString();
|
||||
try {
|
||||
registerFileAction(StorageLocation.USER, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
UUID holder = UUID.fromString(fileName.substring(0, fileName.length() - this.fileExtension.length()));
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
continue;
|
||||
}
|
||||
held.add(NodeHeldPermission.of(holder, e));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getFileName().toString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
return held;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAllGroups() throws IOException {
|
||||
List<String> groups;
|
||||
try (Stream<Path> stream = Files.list(this.groupsDirectory)) {
|
||||
groups = stream.filter(getFileTypeFilter())
|
||||
.map(p -> p.getFileName().toString())
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
for (String g : groups) {
|
||||
try {
|
||||
loadGroup(g);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Exception occurred whilst loading a group");
|
||||
}
|
||||
|
||||
GroupManager<?> gm = this.plugin.getGroupManager();
|
||||
gm.getAll().values().stream()
|
||||
.filter(g -> !groups.contains(g.getName()))
|
||||
.forEach(gm::unload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<HeldPermission<String>> getGroupsWithPermission(String permission) throws Exception {
|
||||
List<HeldPermission<String>> held = new ArrayList<>();
|
||||
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.USER))) {
|
||||
stream.filter(getFileTypeFilter())
|
||||
.forEach(file -> {
|
||||
String fileName = file.getFileName().toString();
|
||||
try {
|
||||
registerFileAction(StorageLocation.GROUP, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
String holder = fileName.substring(0, fileName.length() - this.fileExtension.length());
|
||||
Set<NodeModel> nodes = readNodes(object);
|
||||
for (NodeModel e : nodes) {
|
||||
if (!e.getPermission().equalsIgnoreCase(permission)) {
|
||||
continue;
|
||||
}
|
||||
held.add(NodeHeldPermission.of(holder, e));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(file.getFileName().toString(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
return held;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAllTracks() throws IOException {
|
||||
List<String> tracks;
|
||||
try (Stream<Path> stream = Files.list(this.tracksDirectory)) {
|
||||
tracks = stream.filter(getFileTypeFilter())
|
||||
.map(p -> p.getFileName().toString())
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
boolean success = true;
|
||||
for (String t : tracks) {
|
||||
try {
|
||||
loadTrack(t);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
throw new RuntimeException("Exception occurred whilst loading a track");
|
||||
}
|
||||
|
||||
TrackManager<?> tm = this.plugin.getTrackManager();
|
||||
tm.getAll().values().stream()
|
||||
.filter(t -> !tracks.contains(t.getName()))
|
||||
.forEach(tm::unload);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file.loader;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
* Wraps an object which can produce configurate {@link ConfigurationLoader}s.
|
||||
*/
|
||||
public interface ConfigurateLoader {
|
||||
|
||||
ConfigurationLoader<? extends ConfigurationNode> loader(Path path);
|
||||
|
||||
}
|
@ -23,9 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
package me.lucko.luckperms.common.storage.dao.file.loader;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.hocon.HoconConfigurationLoader;
|
||||
@ -35,13 +33,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class HoconDao extends ConfigurateDao {
|
||||
public HoconDao(LuckPermsPlugin plugin, String dataFolderName) {
|
||||
super(plugin, "HOCON", ".conf", dataFolderName);
|
||||
}
|
||||
public class HoconLoader implements ConfigurateLoader {
|
||||
|
||||
@Override
|
||||
protected ConfigurationLoader<? extends ConfigurationNode> loader(Path path) {
|
||||
public ConfigurationLoader<? extends ConfigurationNode> loader(Path path) {
|
||||
return HoconConfigurationLoader.builder()
|
||||
.setSource(() -> Files.newBufferedReader(path, StandardCharsets.UTF_8))
|
||||
.setSink(() -> Files.newBufferedWriter(path, StandardCharsets.UTF_8))
|
@ -23,9 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
package me.lucko.luckperms.common.storage.dao.file.loader;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.gson.GsonConfigurationLoader;
|
||||
@ -35,13 +33,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class JsonDao extends ConfigurateDao {
|
||||
public JsonDao(LuckPermsPlugin plugin, String dataFolderName) {
|
||||
super(plugin, "JSON", ".json", dataFolderName);
|
||||
}
|
||||
public class JsonLoader implements ConfigurateLoader {
|
||||
|
||||
@Override
|
||||
protected ConfigurationLoader<? extends ConfigurationNode> loader(Path path) {
|
||||
public ConfigurationLoader<? extends ConfigurationNode> loader(Path path) {
|
||||
return GsonConfigurationLoader.builder()
|
||||
.setIndent(2)
|
||||
.setSource(() -> Files.newBufferedReader(path, StandardCharsets.UTF_8))
|
@ -23,9 +23,7 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
package me.lucko.luckperms.common.storage.dao.file.loader;
|
||||
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
|
||||
@ -37,13 +35,10 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class YamlDao extends ConfigurateDao {
|
||||
public YamlDao(LuckPermsPlugin plugin, String dataFolderName) {
|
||||
super(plugin, "YAML", ".yml", dataFolderName);
|
||||
}
|
||||
public class YamlLoader implements ConfigurateLoader {
|
||||
|
||||
@Override
|
||||
protected ConfigurationLoader<? extends ConfigurationNode> loader(Path path) {
|
||||
public ConfigurationLoader<? extends ConfigurationNode> loader(Path path) {
|
||||
return YAMLConfigurationLoader.builder()
|
||||
.setFlowStyle(DumperOptions.FlowStyle.BLOCK)
|
||||
.setIndent(2)
|
@ -151,63 +151,56 @@ public class SqlDao extends AbstractDao {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
try {
|
||||
this.provider.init();
|
||||
public void init() throws Exception {
|
||||
this.provider.init();
|
||||
|
||||
// Init tables
|
||||
if (!tableExists(this.prefix.apply("{prefix}user_permissions"))) {
|
||||
String schemaFileName = "me/lucko/luckperms/schema/" + this.provider.getName().toLowerCase() + ".sql";
|
||||
try (InputStream is = this.plugin.getBootstrap().getResourceStream(schemaFileName)) {
|
||||
if (is == null) {
|
||||
throw new Exception("Couldn't locate schema file for " + this.provider.getName());
|
||||
}
|
||||
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||
try (Connection connection = this.provider.getConnection()) {
|
||||
try (Statement s = connection.createStatement()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("--") || line.startsWith("#")) continue;
|
||||
|
||||
sb.append(line);
|
||||
|
||||
// check for end of declaration
|
||||
if (line.endsWith(";")) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
|
||||
String result = this.prefix.apply(sb.toString().trim());
|
||||
if (!result.isEmpty()) s.addBatch(result);
|
||||
|
||||
// reset
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
}
|
||||
s.executeBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Init tables
|
||||
if (!tableExists(this.prefix.apply("{prefix}user_permissions"))) {
|
||||
String schemaFileName = "me/lucko/luckperms/schema/" + this.provider.getName().toLowerCase() + ".sql";
|
||||
try (InputStream is = this.plugin.getBootstrap().getResourceStream(schemaFileName)) {
|
||||
if (is == null) {
|
||||
throw new Exception("Couldn't locate schema file for " + this.provider.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// migrations
|
||||
try {
|
||||
if (!(this.provider instanceof SQLiteConnectionFactory) && !(this.provider instanceof PostgreConnectionFactory)) {
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
|
||||
try (Connection connection = this.provider.getConnection()) {
|
||||
try (Statement s = connection.createStatement()) {
|
||||
s.execute(this.prefix.apply("ALTER TABLE {prefix}actions MODIFY COLUMN actor_name VARCHAR(100)"));
|
||||
s.execute(this.prefix.apply("ALTER TABLE {prefix}actions MODIFY COLUMN action VARCHAR(300)"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.startsWith("--") || line.startsWith("#")) continue;
|
||||
|
||||
sb.append(line);
|
||||
|
||||
// check for end of declaration
|
||||
if (line.endsWith(";")) {
|
||||
sb.deleteCharAt(sb.length() - 1);
|
||||
|
||||
String result = this.prefix.apply(sb.toString().trim());
|
||||
if (!result.isEmpty()) s.addBatch(result);
|
||||
|
||||
// reset
|
||||
sb = new StringBuilder();
|
||||
}
|
||||
}
|
||||
s.executeBatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// migrations
|
||||
try {
|
||||
if (!(this.provider instanceof SQLiteConnectionFactory) && !(this.provider instanceof PostgreConnectionFactory)) {
|
||||
try (Connection connection = this.provider.getConnection()) {
|
||||
try (Statement s = connection.createStatement()) {
|
||||
s.execute(this.prefix.apply("ALTER TABLE {prefix}actions MODIFY COLUMN actor_name VARCHAR(100)"));
|
||||
s.execute(this.prefix.apply("ALTER TABLE {prefix}actions MODIFY COLUMN action VARCHAR(300)"));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.plugin.getLogger().severe("Error occurred whilst initialising the database.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ package me.lucko.luckperms.common.storage.dao.sql.connection.file;
|
||||
|
||||
import me.lucko.luckperms.common.storage.dao.sql.connection.AbstractConnectionFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
@ -35,9 +37,9 @@ import java.util.Map;
|
||||
abstract class FlatfileConnectionFactory extends AbstractConnectionFactory {
|
||||
protected static final DecimalFormat DF = new DecimalFormat("#.##");
|
||||
|
||||
protected final File file;
|
||||
protected final Path file;
|
||||
|
||||
FlatfileConnectionFactory(String name, File file) {
|
||||
FlatfileConnectionFactory(String name, Path file) {
|
||||
super(name);
|
||||
this.file = file;
|
||||
}
|
||||
@ -47,7 +49,7 @@ abstract class FlatfileConnectionFactory extends AbstractConnectionFactory {
|
||||
|
||||
}
|
||||
|
||||
protected File getWriteFile() {
|
||||
protected Path getWriteFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
@ -55,9 +57,16 @@ abstract class FlatfileConnectionFactory extends AbstractConnectionFactory {
|
||||
public Map<String, String> getMeta() {
|
||||
Map<String, String> ret = new LinkedHashMap<>();
|
||||
|
||||
File databaseFile = getWriteFile();
|
||||
if (databaseFile.exists()) {
|
||||
double size = databaseFile.length() / 1048576D;
|
||||
Path databaseFile = getWriteFile();
|
||||
if (Files.exists(databaseFile)) {
|
||||
long length;
|
||||
try {
|
||||
length = Files.size(databaseFile);
|
||||
} catch (IOException e) {
|
||||
length = 0;
|
||||
}
|
||||
|
||||
double size = length / 1048576D;
|
||||
ret.put("File Size", DF.format(size) + "MB");
|
||||
} else {
|
||||
ret.put("File Size", "0MB");
|
||||
|
@ -29,9 +29,11 @@ import me.lucko.luckperms.common.dependencies.Dependency;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.IsolatedClassLoader;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Driver;
|
||||
import java.sql.SQLException;
|
||||
@ -45,13 +47,17 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
||||
// the active connection
|
||||
private NonClosableConnection connection;
|
||||
|
||||
public H2ConnectionFactory(LuckPermsPlugin plugin, File file) {
|
||||
public H2ConnectionFactory(LuckPermsPlugin plugin, Path file) {
|
||||
super("H2", file);
|
||||
|
||||
// backwards compat
|
||||
File data = new File(file.getParent(), "luckperms.db.mv.db");
|
||||
if (data.exists()) {
|
||||
data.renameTo(new File(file.getParent(), file.getName() + ".mv.db"));
|
||||
Path data = file.getParent().resolve("luckperms.db.mv.db");
|
||||
if (Files.exists(data)) {
|
||||
try {
|
||||
Files.move(data, getWriteFile());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// setup the classloader
|
||||
@ -68,7 +74,7 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
||||
@Override
|
||||
public synchronized Connection getConnection() throws SQLException {
|
||||
if (this.connection == null || this.connection.isClosed()) {
|
||||
Connection connection = this.driver.connect("jdbc:h2:" + this.file.getAbsolutePath(), new Properties());
|
||||
Connection connection = this.driver.connect("jdbc:h2:" + this.file.toString(), new Properties());
|
||||
if (connection != null) {
|
||||
this.connection = NonClosableConnection.wrap(connection);
|
||||
}
|
||||
@ -89,8 +95,8 @@ public class H2ConnectionFactory extends FlatfileConnectionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getWriteFile() {
|
||||
protected Path getWriteFile() {
|
||||
// h2 appends this to the end of the database file
|
||||
return new File(super.file.getParent(), super.file.getName() + ".mv.db");
|
||||
return super.file.getParent().resolve(super.file.getFileName().toString() + ".mv.db");
|
||||
}
|
||||
}
|
||||
|
@ -29,9 +29,11 @@ import me.lucko.luckperms.common.dependencies.Dependency;
|
||||
import me.lucko.luckperms.common.dependencies.classloader.IsolatedClassLoader;
|
||||
import me.lucko.luckperms.common.plugin.LuckPermsPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.EnumSet;
|
||||
@ -44,13 +46,17 @@ public class SQLiteConnectionFactory extends FlatfileConnectionFactory {
|
||||
// the active connection
|
||||
private NonClosableConnection connection;
|
||||
|
||||
public SQLiteConnectionFactory(LuckPermsPlugin plugin, File file) {
|
||||
public SQLiteConnectionFactory(LuckPermsPlugin plugin, Path file) {
|
||||
super("SQLite", file);
|
||||
|
||||
// backwards compat
|
||||
File data = new File(file.getParent(), "luckperms.sqlite");
|
||||
if (data.exists()) {
|
||||
data.renameTo(file);
|
||||
Path data = file.getParent().resolve("luckperms.sqlite");
|
||||
if (Files.exists(data)) {
|
||||
try {
|
||||
Files.move(data, file);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// setup the classloader
|
||||
@ -79,7 +85,7 @@ public class SQLiteConnectionFactory extends FlatfileConnectionFactory {
|
||||
@Override
|
||||
public synchronized Connection getConnection() throws SQLException {
|
||||
if (this.connection == null || this.connection.isClosed()) {
|
||||
Connection connection = createConnection("jdbc:sqlite:" + this.file.getAbsolutePath());
|
||||
Connection connection = createConnection("jdbc:sqlite:" + this.file.toString());
|
||||
if (connection != null) {
|
||||
this.connection = NonClosableConnection.wrap(connection);
|
||||
}
|
||||
|
@ -23,43 +23,20 @@
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package me.lucko.luckperms.common.storage.dao.file;
|
||||
package me.lucko.luckperms.common.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public final class FileUtils {
|
||||
public final class MoreFiles {
|
||||
|
||||
public static File mkdir(File file) throws IOException {
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
public static Path createFileIfNotExists(Path path) throws IOException {
|
||||
if (!Files.exists(path)) {
|
||||
Files.createFile(path);
|
||||
}
|
||||
if (!file.mkdir()) {
|
||||
throw new IOException("Unable to create directory - " + file.getPath());
|
||||
}
|
||||
return file;
|
||||
return path;
|
||||
}
|
||||
|
||||
public static File mkdirs(File file) throws IOException {
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
if (!file.mkdirs()) {
|
||||
throw new IOException("Unable to create directory - " + file.getPath());
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
public static File createNewFile(File file) throws IOException {
|
||||
if (file.exists()) {
|
||||
return file;
|
||||
}
|
||||
if (!file.createNewFile()) {
|
||||
throw new IOException("Unable to create file - " + file.getPath());
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
private FileUtils() {}
|
||||
|
||||
private MoreFiles() {}
|
||||
}
|
@ -33,15 +33,13 @@ import me.lucko.luckperms.common.plugin.bootstrap.LuckPermsBootstrap;
|
||||
import cn.nukkit.Player;
|
||||
import cn.nukkit.plugin.PluginBase;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on Nukkit.
|
||||
*/
|
||||
@ -160,8 +158,8 @@ public class LPNukkitBootstrap extends PluginBase implements LuckPermsBootstrap
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataDirectory() {
|
||||
return getDataFolder();
|
||||
public Path getDataDirectory() {
|
||||
return getDataFolder().toPath().toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,7 +51,6 @@ import org.spongepowered.api.scheduler.Scheduler;
|
||||
import org.spongepowered.api.scheduler.SpongeExecutorService;
|
||||
import org.spongepowered.api.scheduler.SynchronousExecutor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
@ -61,8 +60,6 @@ import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Bootstrap plugin for LuckPerms running on Sponge.
|
||||
*/
|
||||
@ -242,19 +239,19 @@ public class LPSpongeBootstrap implements LuckPermsBootstrap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataDirectory() {
|
||||
public Path getDataDirectory() {
|
||||
Path dataDirectory = this.game.getGameDirectory().resolve("luckperms");
|
||||
try {
|
||||
Files.createDirectories(dataDirectory);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return dataDirectory.toFile();
|
||||
return dataDirectory.toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getConfigDirectory() {
|
||||
return this.configDirectory.toFile();
|
||||
public Path getConfigDirectory() {
|
||||
return this.configDirectory.toAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,7 +54,6 @@ import org.spongepowered.api.service.permission.PermissionService;
|
||||
import org.spongepowered.api.service.permission.Subject;
|
||||
import org.spongepowered.api.text.Text;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -110,7 +109,7 @@ public class LuckPermsService implements LPPermissionService {
|
||||
this.permissionDescriptions = new ConcurrentHashMap<>();
|
||||
|
||||
// init subject storage
|
||||
this.storage = new SubjectStorage(this, new File(plugin.getBootstrap().getDataDirectory(), "sponge-data"));
|
||||
this.storage = new SubjectStorage(this, plugin.getBootstrap().getDataDirectory().resolve("sponge-data"));
|
||||
|
||||
// load defaults collection
|
||||
this.defaultSubjects = new DefaultsCollection(this);
|
||||
|
@ -34,15 +34,16 @@ import me.lucko.luckperms.sponge.service.model.LPPermissionService;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Handles persisted Subject I/O and (de)serialization
|
||||
@ -62,9 +63,9 @@ public class SubjectStorage {
|
||||
/**
|
||||
* The root directory used to store files
|
||||
*/
|
||||
private final File container;
|
||||
private final Path container;
|
||||
|
||||
public SubjectStorage(LPPermissionService service, File container) {
|
||||
public SubjectStorage(LPPermissionService service, Path container) {
|
||||
this.service = service;
|
||||
this.gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
this.container = container;
|
||||
@ -72,7 +73,11 @@ public class SubjectStorage {
|
||||
}
|
||||
|
||||
private void checkContainer() {
|
||||
this.container.getParentFile().mkdirs();
|
||||
try {
|
||||
Files.createDirectories(this.container.getParent());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,12 +88,14 @@ public class SubjectStorage {
|
||||
public Set<String> getSavedCollections() {
|
||||
checkContainer();
|
||||
|
||||
File[] dirs = this.container.listFiles(File::isDirectory);
|
||||
if (dirs == null) {
|
||||
return Collections.emptySet();
|
||||
try (Stream<Path> s = Files.list(this.container)) {
|
||||
return s.filter(p -> Files.isDirectory(p))
|
||||
.map(p -> p.getFileName().toString())
|
||||
.collect(Collectors.toSet());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
|
||||
return ImmutableSet.copyOf(dirs).stream().map(File::getName).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -98,14 +105,16 @@ public class SubjectStorage {
|
||||
* @param subjectIdentifier the identifier of the subject
|
||||
* @return a file
|
||||
*/
|
||||
private File resolveFile(String collectionIdentifier, String subjectIdentifier) {
|
||||
private Path resolveFile(String collectionIdentifier, String subjectIdentifier) {
|
||||
checkContainer();
|
||||
File collection = new File(this.container, collectionIdentifier);
|
||||
if (!collection.exists()) {
|
||||
collection.mkdirs();
|
||||
Path collection = this.container.resolve(collectionIdentifier);
|
||||
try {
|
||||
Files.createDirectories(collection);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return new File(collection, subjectIdentifier + ".json");
|
||||
return collection.resolve(subjectIdentifier + ".json");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +124,7 @@ public class SubjectStorage {
|
||||
* @throws IOException if the write fails
|
||||
*/
|
||||
public void saveToFile(PersistedSubject subject) throws IOException {
|
||||
File subjectFile = resolveFile(subject.getParentCollection().getIdentifier(), subject.getIdentifier());
|
||||
Path subjectFile = resolveFile(subject.getParentCollection().getIdentifier(), subject.getIdentifier());
|
||||
saveToFile(SubjectDataContainer.copyOf(subject.getSubjectData()), subjectFile);
|
||||
}
|
||||
|
||||
@ -126,14 +135,9 @@ public class SubjectStorage {
|
||||
* @param file the file
|
||||
* @throws IOException if the write fails
|
||||
*/
|
||||
public void saveToFile(SubjectDataContainer container, File file) throws IOException {
|
||||
file.getParentFile().mkdirs();
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
file.createNewFile();
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
public void saveToFile(SubjectDataContainer container, Path file) throws IOException {
|
||||
Files.createDirectories(file.getParent());
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
|
||||
this.gson.toJson(container.serialize(), writer);
|
||||
writer.flush();
|
||||
}
|
||||
@ -147,28 +151,27 @@ public class SubjectStorage {
|
||||
*/
|
||||
public Map<String, SubjectDataContainer> loadAllFromFile(String collectionIdentifier) {
|
||||
checkContainer();
|
||||
File collection = new File(this.container, collectionIdentifier);
|
||||
if (!collection.exists()) {
|
||||
Path collection = this.container.resolve(collectionIdentifier);
|
||||
if (!Files.exists(collection)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
String[] fileNames = collection.list((dir, name) -> name.endsWith(".json"));
|
||||
if (fileNames == null) return Collections.emptyMap();
|
||||
|
||||
Map<String, SubjectDataContainer> holders = new HashMap<>();
|
||||
for (String name : fileNames) {
|
||||
File subjectFile = new File(collection, name);
|
||||
|
||||
try {
|
||||
LoadedSubject s = loadFromFile(subjectFile);
|
||||
if (s != null) {
|
||||
holders.put(s.identifier, s.data);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
try (Stream<Path> s = Files.list(collection)){
|
||||
s.filter(p -> p.getFileName().toString().endsWith(".json"))
|
||||
.forEach(subjectFile -> {
|
||||
try {
|
||||
LoadedSubject sub = loadFromFile(subjectFile);
|
||||
if (sub != null) {
|
||||
holders.put(sub.identifier, sub.data);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return holders;
|
||||
}
|
||||
|
||||
@ -182,12 +185,12 @@ public class SubjectStorage {
|
||||
*/
|
||||
public LoadedSubject loadFromFile(String collectionIdentifier, String subjectIdentifier) throws IOException {
|
||||
checkContainer();
|
||||
File collection = new File(this.container, collectionIdentifier);
|
||||
if (!collection.exists()) {
|
||||
Path collection = this.container.resolve(collectionIdentifier);
|
||||
if (!Files.exists(collection)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File subject = new File(collection, subjectIdentifier + ".json");
|
||||
Path subject = collection.resolve(subjectIdentifier + ".json");
|
||||
return new LoadedSubject(subjectIdentifier, loadFromFile(subject).data);
|
||||
}
|
||||
|
||||
@ -198,14 +201,15 @@ public class SubjectStorage {
|
||||
* @return a loaded subject
|
||||
* @throws IOException if the read fails
|
||||
*/
|
||||
public LoadedSubject loadFromFile(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
public LoadedSubject loadFromFile(Path file) throws IOException {
|
||||
if (!Files.exists(file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String subjectName = file.getName().substring(0, file.getName().length() - ".json".length());
|
||||
String fileName = file.getFileName().toString();
|
||||
String subjectName = fileName.substring(0, fileName.length() - ".json".length());
|
||||
|
||||
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
|
||||
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
|
||||
JsonObject data = this.gson.fromJson(reader, JsonObject.class);
|
||||
SubjectDataContainer model = SubjectDataContainer.derserialize(this.service, data);
|
||||
return new LoadedSubject(subjectName, model);
|
||||
|
Loading…
Reference in New Issue
Block a user