Add YAML storage support

This commit is contained in:
Luck 2016-09-02 19:15:31 +01:00
parent 5c760aabd4
commit 5dec5a54cb
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
8 changed files with 907 additions and 465 deletions

View File

@ -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

View File

@ -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

View File

@ -67,6 +67,13 @@
<version>1.7.9</version>
<scope>compile</scope>
</dependency>
<!-- SnakeYAML -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.14</version>
<scope>provided</scope>
</dependency>
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>

View File

@ -35,7 +35,7 @@ import java.util.Set;
@UtilityClass
public class StorageFactory {
private static final Set<String> TYPES = ImmutableSet.of("flatfile", "mongodb", "mysql", "sqlite", "h2");
private static final Set<String> 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());
}
}
}

View File

@ -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<String, String> 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<String, Boolean> 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<String, Boolean> 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<String, Boolean> 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<String, Boolean> e : nodes.entrySet()) {
// There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
}
}
return true;
}
@Override
public Set<UUID> 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<String, Boolean> 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<String> 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<String, Boolean> 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<String> 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<String> 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<String> 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<String, String> getUUIDCache() {
Map<String, String> 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;
}
}

View File

@ -0,0 +1,498 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<String, Boolean> 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<String, Boolean> 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<String, Boolean> 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<String, Boolean> e : nodes.entrySet()) {
// There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
}
}
return true;
}
@Override
public Set<UUID> 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<String, Boolean> 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<String> 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<String, Boolean> 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<String> 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<String> 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<String> 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;
}
}

View File

@ -0,0 +1,378 @@
/*
* Copyright (c) 2016 Lucko (Luck) <luck@lucko.me>
*
* 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<String, Object>) getYaml().load(bufferedReader));
} catch (Throwable t) {
t.printStackTrace();
}
return success;
}
private boolean doWrite(File file, Map<String, Object> 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<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> 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<String, Object> 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<String, Object> 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<String, Boolean> nodes = new HashMap<>();
doRead(file, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
nodes.putAll(perms);
return true;
});
boolean shouldDelete = false;
if (nodes.size() == 1) {
for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
// There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
}
}
return true;
}
@Override
public Set<UUID> 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<String, Object> 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<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> 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<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
for (Map.Entry<String, Boolean> 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<String> 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<String, Object> 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<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".yml");
if (!trackFile.exists()) {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
Map<String, Object> 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<String>) 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<String> groups = new ArrayList<>();
File trackFile = new File(tracksDir, name + ".yml");
if (!trackFile.exists()) {
return false;
}
boolean success = doRead(trackFile, values -> {
groups.addAll((List<String>) 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<String> 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<String, Object> 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<String, Object> values) throws IOException;
}
}

View File

@ -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"