From 5dec5a54cbca361c3d76571ae96d6065d8ec173a Mon Sep 17 00:00:00 2001 From: Luck Date: Fri, 2 Sep 2016 19:15:31 +0100 Subject: [PATCH] Add YAML storage support --- bukkit/src/main/resources/config.yml | 2 +- bungee/src/main/resources/config.yml | 2 +- common/pom.xml | 7 + .../luckperms/storage/StorageFactory.java | 6 +- .../storage/methods/FlatfileDatastore.java | 477 +---------------- .../storage/methods/JSONDatastore.java | 498 ++++++++++++++++++ .../storage/methods/YAMLDatastore.java | 378 +++++++++++++ sponge/src/main/resources/luckperms.conf | 2 +- 8 files changed, 907 insertions(+), 465 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java create mode 100644 common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java diff --git a/bukkit/src/main/resources/config.yml b/bukkit/src/main/resources/config.yml index 655b3255a..80a1483f8 100644 --- a/bukkit/src/main/resources/config.yml +++ b/bukkit/src/main/resources/config.yml @@ -43,7 +43,7 @@ apply-shorthand: true log-notify: true # Which storage method the plugin should use. -# Currently supported: mysql, sqlite, h2, flatfile, mongodb +# Currently supported: mysql, sqlite, h2, json, yaml, mongodb # Fill out connection info below if you're using MySQL or MongoDB storage-method: h2 diff --git a/bungee/src/main/resources/config.yml b/bungee/src/main/resources/config.yml index fa69ea7b0..83e117229 100644 --- a/bungee/src/main/resources/config.yml +++ b/bungee/src/main/resources/config.yml @@ -43,7 +43,7 @@ apply-shorthand: true log-notify: true # Which storage method the plugin should use. -# Currently supported: mysql, sqlite, h2, flatfile, mongodb +# Currently supported: mysql, sqlite, h2, json, yaml, mongodb # Fill out connection info below if you're using MySQL or MongoDB storage-method: h2 diff --git a/common/pom.xml b/common/pom.xml index 2c8e8fbe2..0f2275382 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -67,6 +67,13 @@ 1.7.9 compile + + + org.yaml + snakeyaml + 1.14 + provided + com.google.code.gson diff --git a/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java b/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java index d0223675c..4ee739184 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java +++ b/common/src/main/java/me/lucko/luckperms/storage/StorageFactory.java @@ -35,7 +35,7 @@ import java.util.Set; @UtilityClass public class StorageFactory { - private static final Set TYPES = ImmutableSet.of("flatfile", "mongodb", "mysql", "sqlite", "h2"); + private static final Set TYPES = ImmutableSet.of("json", "yaml", "flatfile", "mongodb", "mysql", "sqlite", "h2"); @SuppressWarnings("unchecked") public static Datastore getDatastore(LuckPermsPlugin plugin, String defaultMethod) { @@ -93,8 +93,10 @@ public class StorageFactory { return new H2Datastore(plugin, new File(plugin.getDataFolder(), "luckperms.db")); case "mongodb": return new MongoDBDatastore(plugin, plugin.getConfiguration().getDatabaseValues()); + case "yaml": + return new YAMLDatastore(plugin, plugin.getDataFolder()); default: - return new FlatfileDatastore(plugin, plugin.getDataFolder()); + return new JSONDatastore(plugin, plugin.getDataFolder()); } } } diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java index e267a1fae..4bb62d098 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java @@ -22,75 +22,37 @@ package me.lucko.luckperms.storage.methods; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.LogEntry; import me.lucko.luckperms.constants.Constants; import me.lucko.luckperms.data.Log; -import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.storage.Datastore; -import me.lucko.luckperms.tracks.Track; -import me.lucko.luckperms.users.User; -import me.lucko.luckperms.utils.Node; import java.io.*; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.*; import java.util.logging.Formatter; -import java.util.stream.Collectors; -import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; - -@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"}) -public class FlatfileDatastore extends Datastore { +abstract class FlatfileDatastore extends Datastore { private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s"; private final Logger actionLogger = Logger.getLogger("lp_actions"); private Map uuidCache = new ConcurrentHashMap<>(); - private final File pluginDir; - private File usersDir; - private File groupsDir; - private File tracksDir; - private File uuidData; - private File actionLog; + final File pluginDir; + File usersDir; + File groupsDir; + File tracksDir; + File uuidData; + File actionLog; - public FlatfileDatastore(LuckPermsPlugin plugin, File pluginDir) { - super(plugin, "Flatfile - JSON"); + FlatfileDatastore(LuckPermsPlugin plugin, String name, File pluginDir) { + super(plugin, name); this.pluginDir = pluginDir; } - private boolean doWrite(File file, WriteOperation writeOperation) { - boolean success = false; - try { - @Cleanup FileWriter fileWriter = new FileWriter(file); - @Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); - @Cleanup JsonWriter jsonWriter = new JsonWriter(bufferedWriter); - jsonWriter.setIndent(" "); - success = writeOperation.onRun(jsonWriter); - jsonWriter.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - return success; - } - - private boolean doRead(File file, ReadOperation readOperation) { - boolean success = false; - try { - @Cleanup FileReader fileReader = new FileReader(file); - @Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader); - @Cleanup JsonReader jsonReader = new JsonReader(bufferedReader); - success = readOperation.onRun(jsonReader); - } catch (IOException e) { - e.printStackTrace(); - } - return success; - } - @Override public void init() { try { @@ -120,9 +82,17 @@ public class FlatfileDatastore extends Datastore { cleanupUsers(); + try { + childInit(); + } catch (Exception e) { + return; + } + setAcceptingLogins(true); } + protected abstract void childInit() throws Exception; + private void makeFiles() throws IOException { File data = new File(pluginDir, "data"); data.mkdirs(); @@ -167,411 +137,6 @@ public class FlatfileDatastore extends Datastore { return Log.builder().build(); } - @Override - public boolean loadUser(UUID uuid, String username) { - User user = plugin.getUserManager().make(uuid, username); - boolean success = false; - - File userFile = new File(usersDir, uuid.toString() + ".json"); - if (userFile.exists()) { - final String[] name = new String[1]; - success = doRead(userFile, reader -> { - reader.beginObject(); - reader.nextName(); // uuid record - reader.nextString(); // uuid - reader.nextName(); // name record - name[0] = reader.nextString(); // name - reader.nextName(); // primaryGroup record - user.setPrimaryGroup(reader.nextString()); // primaryGroup - reader.nextName(); //perms - reader.beginObject(); - while (reader.hasNext()) { - String node = reader.nextName(); - boolean b = reader.nextBoolean(); - user.getNodes().add(Node.fromSerialisedNode(node, b)); - } - - reader.endObject(); - reader.endObject(); - return true; - }); - - if (user.getName().equalsIgnoreCase("null")) { - user.setName(name[0]); - } else { - if (!name[0].equals(user.getName())) { - doWrite(userFile, writer -> { - writer.beginObject(); - writer.name("uuid").value(user.getUuid().toString()); - writer.name("name").value(user.getName()); - writer.name("primaryGroup").value(user.getPrimaryGroup()); - writer.name("perms"); - writer.beginObject(); - for (Map.Entry e : exportToLegacy(user.getNodes()).entrySet()) { - writer.name(e.getKey()).value(e.getValue().booleanValue()); - } - writer.endObject(); - writer.endObject(); - return true; - }); - } - } - - } else { - success = true; - } - - if (success) plugin.getUserManager().updateOrSet(user); - return success; - } - - @Override - public boolean saveUser(User user) { - File userFile = new File(usersDir, user.getUuid().toString() + ".json"); - if (!plugin.getUserManager().shouldSave(user)) { - if (userFile.exists()) { - userFile.delete(); - } - return true; - } - - if (!userFile.exists()) { - try { - userFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - boolean success = doWrite(userFile, writer -> { - writer.beginObject(); - writer.name("uuid").value(user.getUuid().toString()); - writer.name("name").value(user.getName()); - writer.name("primaryGroup").value(user.getPrimaryGroup()); - writer.name("perms"); - writer.beginObject(); - for (Map.Entry e : exportToLegacy(user.getNodes()).entrySet()) { - writer.name(e.getKey()).value(e.getValue().booleanValue()); - } - writer.endObject(); - writer.endObject(); - return true; - }); - return success; - } - - @Override - public boolean cleanupUsers() { - File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json")); - if (files == null) return false; - - for (File file : files) { - Map nodes = new HashMap<>(); - doRead(file, reader -> { - reader.beginObject(); - reader.nextName(); // uuid record - reader.nextString(); // uuid - reader.nextName(); // name record - reader.nextString(); // name - reader.nextName(); // primaryGroup record - reader.nextString(); // primaryGroup - reader.nextName(); //perms - reader.beginObject(); - while (reader.hasNext()) { - String node = reader.nextName(); - boolean b = reader.nextBoolean(); - nodes.put(node, b); - } - - reader.endObject(); - reader.endObject(); - return true; - }); - - boolean shouldDelete = false; - if (nodes.size() == 1) { - for (Map.Entry e : nodes.entrySet()) { - // There's only one - shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue(); - } - } - - if (shouldDelete) { - file.delete(); - } - } - return true; - } - - @Override - public Set getUniqueUsers() { - String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".json")); - if (fileNames == null) return null; - return Arrays.stream(fileNames) - .map(s -> s.substring(0, s.length() - 5)) - .map(UUID::fromString) - .collect(Collectors.toSet()); - } - - @Override - public boolean createAndLoadGroup(String name) { - Group group = plugin.getGroupManager().make(name); - - File groupFile = new File(groupsDir, name + ".json"); - if (!groupFile.exists()) { - try { - groupFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - boolean success = doWrite(groupFile, writer -> { - writer.beginObject(); - writer.name("name").value(group.getName()); - writer.name("perms"); - writer.beginObject(); - for (Map.Entry e : exportToLegacy(group.getNodes()).entrySet()) { - writer.name(e.getKey()).value(e.getValue().booleanValue()); - } - writer.endObject(); - writer.endObject(); - return true; - }); - - if (!success) return false; - } - - boolean success = doRead(groupFile, reader -> { - reader.beginObject(); - reader.nextName(); // name record - reader.nextString(); // name - reader.nextName(); //perms - reader.beginObject(); - while (reader.hasNext()) { - String node = reader.nextName(); - boolean b = reader.nextBoolean(); - group.getNodes().add(Node.fromSerialisedNode(node, b)); - } - - reader.endObject(); - reader.endObject(); - return true; - }); - - if (success) plugin.getGroupManager().updateOrSet(group); - return success; - } - - @Override - public boolean loadGroup(String name) { - Group group = plugin.getGroupManager().make(name); - - File groupFile = new File(groupsDir, name + ".json"); - if (!groupFile.exists()) { - return false; - } - - boolean success = doRead(groupFile, reader -> { - reader.beginObject(); - reader.nextName(); // name record - reader.nextString(); // name - reader.nextName(); //perms - reader.beginObject(); - while (reader.hasNext()) { - String node = reader.nextName(); - boolean b = reader.nextBoolean(); - group.getNodes().add(Node.fromSerialisedNode(node, b)); - } - - reader.endObject(); - reader.endObject(); - return true; - }); - - if (success) plugin.getGroupManager().updateOrSet(group); - return success; - } - - @Override - public boolean loadAllGroups() { - String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".json")); - if (fileNames == null) return false; - List groups = Arrays.stream(fileNames) - .map(s -> s.substring(0, s.length() - 5)) - .collect(Collectors.toList()); - - plugin.getGroupManager().unloadAll(); - groups.forEach(this::loadGroup); - return true; - } - - @Override - public boolean saveGroup(Group group) { - File groupFile = new File(groupsDir, group.getName() + ".json"); - if (!groupFile.exists()) { - try { - groupFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - boolean success = doWrite(groupFile, writer -> { - writer.beginObject(); - writer.name("name").value(group.getName()); - writer.name("perms"); - writer.beginObject(); - for (Map.Entry e : exportToLegacy(group.getNodes()).entrySet()) { - writer.name(e.getKey()).value(e.getValue().booleanValue()); - } - writer.endObject(); - writer.endObject(); - return true; - }); - - return success; - } - - @Override - public boolean deleteGroup(Group group) { - File groupFile = new File(groupsDir, group.getName() + ".json"); - if (groupFile.exists()) { - groupFile.delete(); - } - return true; - } - - @Override - public boolean createAndLoadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - List groups = new ArrayList<>(); - - File trackFile = new File(tracksDir, name + ".json"); - if (!trackFile.exists()) { - try { - trackFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - boolean success = doWrite(trackFile, writer -> { - writer.beginObject(); - writer.name("name").value(track.getName()); - writer.name("groups"); - writer.beginArray(); - for (String s : track.getGroups()) { - writer.value(s); - } - writer.endArray(); - writer.endObject(); - return true; - }); - - if (!success) return false; - } - - boolean success = doRead(trackFile, reader -> { - reader.beginObject(); - reader.nextName(); // name record - reader.nextString(); // name - reader.nextName(); // groups record - reader.beginArray(); - while (reader.hasNext()) { - groups.add(reader.nextString()); - } - reader.endArray(); - reader.endObject(); - return true; - }); - - track.setGroups(groups); - if (success) plugin.getTrackManager().updateOrSet(track); - return success; - } - - @Override - public boolean loadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - List groups = new ArrayList<>(); - - File trackFile = new File(tracksDir, name + ".json"); - if (!trackFile.exists()) { - return false; - } - - boolean success = doRead(trackFile, reader -> { - reader.beginObject(); - reader.nextName(); // name record - reader.nextString(); // name - reader.nextName(); // groups record - reader.beginArray(); - while (reader.hasNext()) { - groups.add(reader.nextString()); - } - reader.endArray(); - reader.endObject(); - return true; - }); - - track.setGroups(groups); - if (success) plugin.getTrackManager().updateOrSet(track); - return success; - } - - @Override - public boolean loadAllTracks() { - String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".json")); - if (fileNames == null) return false; - List tracks = Arrays.stream(fileNames) - .map(s -> s.substring(0, s.length() - 5)) - .collect(Collectors.toList()); - - plugin.getTrackManager().unloadAll(); - tracks.forEach(this::loadTrack); - return true; - } - - @Override - public boolean saveTrack(Track track) { - File trackFile = new File(tracksDir, track.getName() + ".json"); - if (!trackFile.exists()) { - try { - trackFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - boolean success = doWrite(trackFile, writer -> { - writer.beginObject(); - writer.name("name").value(track.getName()); - writer.name("groups"); - writer.beginArray(); - for (String s : track.getGroups()) { - writer.value(s); - } - writer.endArray(); - writer.endObject(); - return true; - }); - - return success; - } - - @Override - public boolean deleteTrack(Track track) { - File trackFile = new File(tracksDir, track.getName() + ".json"); - if (trackFile.exists()) { - trackFile.delete(); - } - return true; - } - private Map getUUIDCache() { Map cache = new HashMap<>(); @@ -628,12 +193,4 @@ public class FlatfileDatastore extends Datastore { } return null; } - - interface WriteOperation { - boolean onRun(JsonWriter writer) throws IOException; - } - - interface ReadOperation { - boolean onRun(JsonReader reader) throws IOException; - } } diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java new file mode 100644 index 000000000..2c00477a8 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.storage.methods; + +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import lombok.Cleanup; +import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.api.LogEntry; +import me.lucko.luckperms.constants.Constants; +import me.lucko.luckperms.data.Log; +import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.storage.Datastore; +import me.lucko.luckperms.tracks.Track; +import me.lucko.luckperms.users.User; +import me.lucko.luckperms.utils.Node; + +import java.io.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.*; +import java.util.logging.Formatter; +import java.util.stream.Collectors; + +import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; + +@SuppressWarnings({"ResultOfMethodCallIgnored", "UnnecessaryLocalVariable"}) +public class JSONDatastore extends FlatfileDatastore { + public JSONDatastore(LuckPermsPlugin plugin, File pluginDir) { + super(plugin, "Flatfile - JSON", pluginDir); + } + + private boolean doWrite(File file, WriteOperation writeOperation) { + boolean success = false; + try { + @Cleanup FileWriter fileWriter = new FileWriter(file); + @Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + @Cleanup JsonWriter jsonWriter = new JsonWriter(bufferedWriter); + jsonWriter.setIndent(" "); + success = writeOperation.onRun(jsonWriter); + jsonWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + return success; + } + + private boolean doRead(File file, ReadOperation readOperation) { + boolean success = false; + try { + @Cleanup FileReader fileReader = new FileReader(file); + @Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader); + @Cleanup JsonReader jsonReader = new JsonReader(bufferedReader); + success = readOperation.onRun(jsonReader); + } catch (IOException e) { + e.printStackTrace(); + } + return success; + } + + @Override + protected void childInit() throws Exception { + + } + + @Override + public boolean loadUser(UUID uuid, String username) { + User user = plugin.getUserManager().make(uuid, username); + boolean success = false; + + File userFile = new File(usersDir, uuid.toString() + ".json"); + if (userFile.exists()) { + final String[] name = new String[1]; + success = doRead(userFile, reader -> { + reader.beginObject(); + reader.nextName(); // uuid record + reader.nextString(); // uuid + reader.nextName(); // name record + name[0] = reader.nextString(); // name + reader.nextName(); // primaryGroup record + user.setPrimaryGroup(reader.nextString()); // primaryGroup + reader.nextName(); //perms + reader.beginObject(); + while (reader.hasNext()) { + String node = reader.nextName(); + boolean b = reader.nextBoolean(); + user.getNodes().add(Node.fromSerialisedNode(node, b)); + } + + reader.endObject(); + reader.endObject(); + return true; + }); + + if (user.getName().equalsIgnoreCase("null")) { + user.setName(name[0]); + } else { + if (!name[0].equals(user.getName())) { + doWrite(userFile, writer -> { + writer.beginObject(); + writer.name("uuid").value(user.getUuid().toString()); + writer.name("name").value(user.getName()); + writer.name("primaryGroup").value(user.getPrimaryGroup()); + writer.name("perms"); + writer.beginObject(); + for (Map.Entry e : exportToLegacy(user.getNodes()).entrySet()) { + writer.name(e.getKey()).value(e.getValue().booleanValue()); + } + writer.endObject(); + writer.endObject(); + return true; + }); + } + } + + } else { + success = true; + } + + if (success) plugin.getUserManager().updateOrSet(user); + return success; + } + + @Override + public boolean saveUser(User user) { + File userFile = new File(usersDir, user.getUuid().toString() + ".json"); + if (!plugin.getUserManager().shouldSave(user)) { + if (userFile.exists()) { + userFile.delete(); + } + return true; + } + + if (!userFile.exists()) { + try { + userFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + boolean success = doWrite(userFile, writer -> { + writer.beginObject(); + writer.name("uuid").value(user.getUuid().toString()); + writer.name("name").value(user.getName()); + writer.name("primaryGroup").value(user.getPrimaryGroup()); + writer.name("perms"); + writer.beginObject(); + for (Map.Entry e : exportToLegacy(user.getNodes()).entrySet()) { + writer.name(e.getKey()).value(e.getValue().booleanValue()); + } + writer.endObject(); + writer.endObject(); + return true; + }); + return success; + } + + @Override + public boolean cleanupUsers() { + File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json")); + if (files == null) return false; + + for (File file : files) { + Map nodes = new HashMap<>(); + doRead(file, reader -> { + reader.beginObject(); + reader.nextName(); // uuid record + reader.nextString(); // uuid + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); // primaryGroup record + reader.nextString(); // primaryGroup + reader.nextName(); //perms + reader.beginObject(); + while (reader.hasNext()) { + String node = reader.nextName(); + boolean b = reader.nextBoolean(); + nodes.put(node, b); + } + + reader.endObject(); + reader.endObject(); + return true; + }); + + boolean shouldDelete = false; + if (nodes.size() == 1) { + for (Map.Entry e : nodes.entrySet()) { + // There's only one + shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue(); + } + } + + if (shouldDelete) { + file.delete(); + } + } + return true; + } + + @Override + public Set getUniqueUsers() { + String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".json")); + if (fileNames == null) return null; + return Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 5)) + .map(UUID::fromString) + .collect(Collectors.toSet()); + } + + @Override + public boolean createAndLoadGroup(String name) { + Group group = plugin.getGroupManager().make(name); + + File groupFile = new File(groupsDir, name + ".json"); + if (!groupFile.exists()) { + try { + groupFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + boolean success = doWrite(groupFile, writer -> { + writer.beginObject(); + writer.name("name").value(group.getName()); + writer.name("perms"); + writer.beginObject(); + for (Map.Entry e : exportToLegacy(group.getNodes()).entrySet()) { + writer.name(e.getKey()).value(e.getValue().booleanValue()); + } + writer.endObject(); + writer.endObject(); + return true; + }); + + if (!success) return false; + } + + boolean success = doRead(groupFile, reader -> { + reader.beginObject(); + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); //perms + reader.beginObject(); + while (reader.hasNext()) { + String node = reader.nextName(); + boolean b = reader.nextBoolean(); + group.getNodes().add(Node.fromSerialisedNode(node, b)); + } + + reader.endObject(); + reader.endObject(); + return true; + }); + + if (success) plugin.getGroupManager().updateOrSet(group); + return success; + } + + @Override + public boolean loadGroup(String name) { + Group group = plugin.getGroupManager().make(name); + + File groupFile = new File(groupsDir, name + ".json"); + if (!groupFile.exists()) { + return false; + } + + boolean success = doRead(groupFile, reader -> { + reader.beginObject(); + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); //perms + reader.beginObject(); + while (reader.hasNext()) { + String node = reader.nextName(); + boolean b = reader.nextBoolean(); + group.getNodes().add(Node.fromSerialisedNode(node, b)); + } + + reader.endObject(); + reader.endObject(); + return true; + }); + + if (success) plugin.getGroupManager().updateOrSet(group); + return success; + } + + @Override + public boolean loadAllGroups() { + String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".json")); + if (fileNames == null) return false; + List groups = Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 5)) + .collect(Collectors.toList()); + + plugin.getGroupManager().unloadAll(); + groups.forEach(this::loadGroup); + return true; + } + + @Override + public boolean saveGroup(Group group) { + File groupFile = new File(groupsDir, group.getName() + ".json"); + if (!groupFile.exists()) { + try { + groupFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + boolean success = doWrite(groupFile, writer -> { + writer.beginObject(); + writer.name("name").value(group.getName()); + writer.name("perms"); + writer.beginObject(); + for (Map.Entry e : exportToLegacy(group.getNodes()).entrySet()) { + writer.name(e.getKey()).value(e.getValue().booleanValue()); + } + writer.endObject(); + writer.endObject(); + return true; + }); + + return success; + } + + @Override + public boolean deleteGroup(Group group) { + File groupFile = new File(groupsDir, group.getName() + ".json"); + if (groupFile.exists()) { + groupFile.delete(); + } + return true; + } + + @Override + public boolean createAndLoadTrack(String name) { + Track track = plugin.getTrackManager().make(name); + List groups = new ArrayList<>(); + + File trackFile = new File(tracksDir, name + ".json"); + if (!trackFile.exists()) { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + boolean success = doWrite(trackFile, writer -> { + writer.beginObject(); + writer.name("name").value(track.getName()); + writer.name("groups"); + writer.beginArray(); + for (String s : track.getGroups()) { + writer.value(s); + } + writer.endArray(); + writer.endObject(); + return true; + }); + + if (!success) return false; + } + + boolean success = doRead(trackFile, reader -> { + reader.beginObject(); + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); // groups record + reader.beginArray(); + while (reader.hasNext()) { + groups.add(reader.nextString()); + } + reader.endArray(); + reader.endObject(); + return true; + }); + + track.setGroups(groups); + if (success) plugin.getTrackManager().updateOrSet(track); + return success; + } + + @Override + public boolean loadTrack(String name) { + Track track = plugin.getTrackManager().make(name); + List groups = new ArrayList<>(); + + File trackFile = new File(tracksDir, name + ".json"); + if (!trackFile.exists()) { + return false; + } + + boolean success = doRead(trackFile, reader -> { + reader.beginObject(); + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); // groups record + reader.beginArray(); + while (reader.hasNext()) { + groups.add(reader.nextString()); + } + reader.endArray(); + reader.endObject(); + return true; + }); + + track.setGroups(groups); + if (success) plugin.getTrackManager().updateOrSet(track); + return success; + } + + @Override + public boolean loadAllTracks() { + String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".json")); + if (fileNames == null) return false; + List tracks = Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 5)) + .collect(Collectors.toList()); + + plugin.getTrackManager().unloadAll(); + tracks.forEach(this::loadTrack); + return true; + } + + @Override + public boolean saveTrack(Track track) { + File trackFile = new File(tracksDir, track.getName() + ".json"); + if (!trackFile.exists()) { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + boolean success = doWrite(trackFile, writer -> { + writer.beginObject(); + writer.name("name").value(track.getName()); + writer.name("groups"); + writer.beginArray(); + for (String s : track.getGroups()) { + writer.value(s); + } + writer.endArray(); + writer.endObject(); + return true; + }); + + return success; + } + + @Override + public boolean deleteTrack(Track track) { + File trackFile = new File(tracksDir, track.getName() + ".json"); + if (trackFile.exists()) { + trackFile.delete(); + } + return true; + } + + interface WriteOperation { + boolean onRun(JsonWriter writer) throws IOException; + } + + interface ReadOperation { + boolean onRun(JsonReader reader) throws IOException; + } +} diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java new file mode 100644 index 000000000..305b6545d --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * 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.storage.methods; + +import lombok.Cleanup; +import me.lucko.luckperms.LuckPermsPlugin; +import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.tracks.Track; +import me.lucko.luckperms.users.User; +import me.lucko.luckperms.utils.Node; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.BaseConstructor; + +import java.io.*; +import java.util.*; +import java.util.stream.Collectors; + +import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; + +@SuppressWarnings({"unchecked", "ResultOfMethodCallIgnored"}) +public class YAMLDatastore extends FlatfileDatastore { + public YAMLDatastore(LuckPermsPlugin plugin, File pluginDir) { + super(plugin, "Flatfile - YAML", pluginDir); + } + + private Yaml getYaml() { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + return new Yaml(options); + } + + private boolean doRead(File file, ReadOperation readOperation) { + boolean success = false; + try { + @Cleanup FileReader fileReader = new FileReader(file); + @Cleanup BufferedReader bufferedReader = new BufferedReader(fileReader); + success = readOperation.onRun((Map) getYaml().load(bufferedReader)); + } catch (Throwable t) { + t.printStackTrace(); + } + return success; + } + + private boolean doWrite(File file, Map values) { + try { + @Cleanup FileWriter fileWriter = new FileWriter(file); + @Cleanup BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); + getYaml().dump(values, bufferedWriter); + bufferedWriter.flush(); + return true; + } catch (Throwable t) { + t.printStackTrace(); + return false; + } + } + + @Override + protected void childInit() throws Exception { + + } + + @Override + public boolean loadUser(UUID uuid, String username) { + User user = plugin.getUserManager().make(uuid, username); + boolean success = false; + + File userFile = new File(usersDir, uuid.toString() + ".yml"); + if (userFile.exists()) { + final String[] name = {null}; + success = doRead(userFile, values -> { + name[0] = (String) values.get("name"); + user.setPrimaryGroup((String) values.get("primary-group")); + Map perms = (Map) values.get("perms"); + for (Map.Entry e : perms.entrySet()) { + user.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); + } + return true; + }); + + if (user.getName().equalsIgnoreCase("null")) { + user.setName(name[0]); + } else { + if (!name[0].equals(user.getName())) { + Map values = new HashMap<>(); + values.put("uuid", user.getUuid().toString()); + values.put("name", user.getName()); + values.put("primary-group", user.getPrimaryGroup()); + values.put("perms", exportToLegacy(user.getNodes())); + doWrite(userFile, values); + } + } + + } else { + success = true; + } + + if (success) plugin.getUserManager().updateOrSet(user); + return success; + } + + @Override + public boolean saveUser(User user) { + File userFile = new File(usersDir, user.getUuid().toString() + ".yml"); + if (!plugin.getUserManager().shouldSave(user)) { + if (userFile.exists()) { + userFile.delete(); + } + return true; + } + + if (!userFile.exists()) { + try { + userFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + Map values = new HashMap<>(); + values.put("uuid", user.getUuid().toString()); + values.put("name", user.getName()); + values.put("primary-group", user.getPrimaryGroup()); + values.put("perms", exportToLegacy(user.getNodes())); + return doWrite(userFile, values); + } + + @Override + public boolean cleanupUsers() { + File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".yml")); + if (files == null) return false; + + for (File file : files) { + Map nodes = new HashMap<>(); + doRead(file, values -> { + Map perms = (Map) values.get("perms"); + nodes.putAll(perms); + return true; + }); + + boolean shouldDelete = false; + if (nodes.size() == 1) { + for (Map.Entry e : nodes.entrySet()) { + // There's only one + shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue(); + } + } + + if (shouldDelete) { + file.delete(); + } + } + return true; + } + + @Override + public Set getUniqueUsers() { + String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".yml")); + if (fileNames == null) return null; + return Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 4)) + .map(UUID::fromString) + .collect(Collectors.toSet()); + } + + @Override + public boolean createAndLoadGroup(String name) { + Group group = plugin.getGroupManager().make(name); + + File groupFile = new File(groupsDir, name + ".yml"); + if (!groupFile.exists()) { + try { + groupFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + Map values = new HashMap<>(); + values.put("name", group.getName()); + values.put("perms", exportToLegacy(group.getNodes())); + + if (!doWrite(groupFile, values)) { + return false; + } + } + + boolean success = doRead(groupFile, values -> { + Map perms = (Map) values.get("perms"); + for (Map.Entry e : perms.entrySet()) { + group.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); + } + return true; + }); + + if (success) plugin.getGroupManager().updateOrSet(group); + return success; + } + + @Override + public boolean loadGroup(String name) { + Group group = plugin.getGroupManager().make(name); + + File groupFile = new File(groupsDir, name + ".yml"); + if (!groupFile.exists()) { + return false; + } + + boolean success = doRead(groupFile, values -> { + Map perms = (Map) values.get("perms"); + for (Map.Entry e : perms.entrySet()) { + group.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); + } + return true; + }); + + if (success) plugin.getGroupManager().updateOrSet(group); + return success; + } + + @Override + public boolean loadAllGroups() { + String[] fileNames = groupsDir.list((dir, name) -> name.endsWith(".yml")); + if (fileNames == null) return false; + List groups = Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 4)) + .collect(Collectors.toList()); + + plugin.getGroupManager().unloadAll(); + groups.forEach(this::loadGroup); + return true; + } + + @Override + public boolean saveGroup(Group group) { + File groupFile = new File(groupsDir, group.getName() + ".yml"); + if (!groupFile.exists()) { + try { + groupFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + Map values = new HashMap<>(); + values.put("name", group.getName()); + values.put("perms", exportToLegacy(group.getNodes())); + return doWrite(groupFile, values); + } + + @Override + public boolean deleteGroup(Group group) { + File groupFile = new File(groupsDir, group.getName() + ".yml"); + if (groupFile.exists()) { + groupFile.delete(); + } + return true; + } + + @Override + public boolean createAndLoadTrack(String name) { + Track track = plugin.getTrackManager().make(name); + List groups = new ArrayList<>(); + + File trackFile = new File(tracksDir, name + ".yml"); + if (!trackFile.exists()) { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + Map values = new HashMap<>(); + values.put("name", track.getName()); + values.put("groups", track.getGroups()); + + if (!doWrite(trackFile, values)) { + return false; + } + } + + boolean success = doRead(trackFile, values -> { + groups.addAll((List) values.get("groups")); + return true; + }); + + track.setGroups(groups); + if (success) plugin.getTrackManager().updateOrSet(track); + return success; + } + + @Override + public boolean loadTrack(String name) { + Track track = plugin.getTrackManager().make(name); + List groups = new ArrayList<>(); + + File trackFile = new File(tracksDir, name + ".yml"); + if (!trackFile.exists()) { + return false; + } + + boolean success = doRead(trackFile, values -> { + groups.addAll((List) values.get("groups")); + return true; + }); + + track.setGroups(groups); + if (success) plugin.getTrackManager().updateOrSet(track); + return success; + } + + @Override + public boolean loadAllTracks() { + String[] fileNames = tracksDir.list((dir, name) -> name.endsWith(".yml")); + if (fileNames == null) return false; + List tracks = Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 4)) + .collect(Collectors.toList()); + + plugin.getTrackManager().unloadAll(); + tracks.forEach(this::loadTrack); + return true; + } + + @Override + public boolean saveTrack(Track track) { + File trackFile = new File(tracksDir, track.getName() + ".yml"); + if (!trackFile.exists()) { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + + Map values = new HashMap<>(); + values.put("name", track.getName()); + values.put("groups", track.getGroups()); + return doWrite(trackFile, values); + } + + @Override + public boolean deleteTrack(Track track) { + File trackFile = new File(tracksDir, track.getName() + ".yml"); + if (trackFile.exists()) { + trackFile.delete(); + } + return true; + } + + interface ReadOperation { + boolean onRun(Map values) throws IOException; + } +} diff --git a/sponge/src/main/resources/luckperms.conf b/sponge/src/main/resources/luckperms.conf index 6190cc9fc..1c2de149f 100644 --- a/sponge/src/main/resources/luckperms.conf +++ b/sponge/src/main/resources/luckperms.conf @@ -43,7 +43,7 @@ apply-shorthand=true log-notify=true # Which storage method the plugin should use. -# Currently supported: mysql, sqlite, h2, flatfile, mongodb +# Currently supported: mysql, sqlite, h2, json, yaml, mongodb # Fill out connection info below if you're using MySQL or MongoDB storage-method="h2"