From 4787361e667163c325b5073c68740075cfd18e6d Mon Sep 17 00:00:00 2001 From: Luck Date: Sat, 1 Oct 2016 19:03:05 +0100 Subject: [PATCH] Implement locks to hopefully resolve race conditions with I/O - experimental --- .../luckperms/users/BukkitUserManager.java | 17 +- .../luckperms/users/BungeeUserManager.java | 13 +- .../api/implementation/ApiProvider.java | 3 +- .../group/subcommands/GroupClone.java | 2 +- .../group/subcommands/GroupRename.java | 2 +- .../track/subcommands/TrackClone.java | 2 +- .../track/subcommands/TrackRename.java | 2 +- .../luckperms/core/PermissionHolder.java | 5 + .../lucko/luckperms/groups/GroupManager.java | 7 +- .../storage/methods/JSONDatastore.java | 660 ++++++++++-------- .../storage/methods/MongoDBDatastore.java | 309 ++++---- .../storage/methods/SQLDatastore.java | 332 +++++---- .../storage/methods/YAMLDatastore.java | 452 +++++++----- .../java/me/lucko/luckperms/tracks/Track.java | 5 + .../lucko/luckperms/tracks/TrackManager.java | 7 +- .../java/me/lucko/luckperms/users/User.java | 6 +- .../lucko/luckperms/users/UserIdentifier.java | 61 ++ .../me/lucko/luckperms/users/UserManager.java | 53 +- .../luckperms/utils/AbstractManager.java | 64 +- .../sponge/collections/UserCollection.java | 7 +- .../luckperms/users/SpongeUserManager.java | 13 +- .../users/StandaloneUserManager.java | 15 +- 22 files changed, 1159 insertions(+), 878 deletions(-) create mode 100644 common/src/main/java/me/lucko/luckperms/users/UserIdentifier.java diff --git a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java index 33cad045..c48142d6 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java @@ -47,7 +47,7 @@ public class BukkitUserManager extends UserManager implements ContextListener { Set players = plugin.getServer().getOnlinePlayers().stream() .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) diff --git a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java index 1281efab..669c752d 100644 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java +++ b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java @@ -45,13 +45,12 @@ public class BungeeUserManager extends UserManager implements ContextListener { return CommandResult.LOADING_ERROR; } - plugin.getGroupManager().copy(group, newGroup); + newGroup.setNodes(group.getNodes()); Message.CLONE_SUCCESS.send(sender, group.getName(), newGroup.getName()); LogEntry.build().actor(sender).acted(group).action("clone " + newGroup.getName()).build().submit(plugin, sender); diff --git a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupRename.java b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupRename.java index 2ed58223..2ed84d36 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupRename.java +++ b/common/src/main/java/me/lucko/luckperms/commands/group/subcommands/GroupRename.java @@ -68,7 +68,7 @@ public class GroupRename extends SubCommand { return CommandResult.FAILURE; } - plugin.getGroupManager().copy(group, newGroup); + newGroup.setNodes(group.getNodes()); Message.RENAME_SUCCESS.send(sender, group.getName(), newGroup.getName()); LogEntry.build().actor(sender).acted(group).action("rename " + newGroup.getName()).build().submit(plugin, sender); diff --git a/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackClone.java b/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackClone.java index 4f0edd86..b092b2e9 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackClone.java +++ b/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackClone.java @@ -63,7 +63,7 @@ public class TrackClone extends SubCommand { return CommandResult.LOADING_ERROR; } - plugin.getTrackManager().copy(track, newTrack); + newTrack.setGroups(track.getGroups()); Message.CLONE_SUCCESS.send(sender, track.getName(), newTrack.getName()); LogEntry.build().actor(sender).acted(track).action("clone " + newTrack.getName()).build().submit(plugin, sender); diff --git a/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackRename.java b/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackRename.java index eb355fdf..f9aadeb2 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackRename.java +++ b/common/src/main/java/me/lucko/luckperms/commands/track/subcommands/TrackRename.java @@ -68,7 +68,7 @@ public class TrackRename extends SubCommand { return CommandResult.FAILURE; } - plugin.getTrackManager().copy(track, newTrack); + newTrack.setGroups(track.getGroups()); Message.RENAME_SUCCESS.send(sender, track.getName(), newTrack.getName()); LogEntry.build().actor(sender).acted(track).action("rename " + newTrack.getName()).build().submit(plugin, sender); diff --git a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java index 68ea9059..ecb0c911 100644 --- a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java @@ -40,6 +40,8 @@ import me.lucko.luckperms.groups.Group; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** @@ -74,6 +76,9 @@ public abstract class PermissionHolder { @Getter private Set transientNodes = ConcurrentHashMap.newKeySet(); + @Getter + private final Lock ioLock = new ReentrantLock(); + /** * Returns a Set of nodes in priority order * @return the holders transient and permanent nodes diff --git a/common/src/main/java/me/lucko/luckperms/groups/GroupManager.java b/common/src/main/java/me/lucko/luckperms/groups/GroupManager.java index 2f374ebf..097e555c 100644 --- a/common/src/main/java/me/lucko/luckperms/groups/GroupManager.java +++ b/common/src/main/java/me/lucko/luckperms/groups/GroupManager.java @@ -30,18 +30,13 @@ import me.lucko.luckperms.utils.AbstractManager; public class GroupManager extends AbstractManager { private final LuckPermsPlugin plugin; - @Override - public void copy(Group from, Group to) { - to.setNodes(from.getNodes()); - } - /** * Makes a new group object * @param name The name of the group * @return a new {@link Group} object */ @Override - public Group make(String name) { + public Group apply(String name) { return new Group(name, plugin); } } \ No newline at end of file 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 index 0c516b3f..240cbe18 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/JSONDatastore.java @@ -28,11 +28,15 @@ import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.core.Node; import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.groups.GroupManager; import me.lucko.luckperms.tracks.Track; +import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.User; +import me.lucko.luckperms.users.UserIdentifier; import java.io.*; import java.util.*; +import java.util.concurrent.Callable; import java.util.stream.Collectors; import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; @@ -73,138 +77,157 @@ public class JSONDatastore extends FlatfileDatastore { @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()); + User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); + user.getIoLock().lock(); + try { + return call(() -> { + File userFile = new File(usersDir, uuid.toString() + ".json"); + if (userFile.exists()) { + return doRead(userFile, reader -> { + reader.beginObject(); + reader.nextName(); // uuid record + reader.nextString(); // uuid + reader.nextName(); // name record + String name1 = 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(); + + boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); + + if (user.getName().equalsIgnoreCase("null")) { + user.setName(name1); + } else { + if (!name1.equals(user.getName())) { + save = true; + } + } + + if (save) { + 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; + }); } - writer.endObject(); - writer.endObject(); return true; }); + } else { + if (plugin.getUserManager().shouldSave(user)) { + user.clearNodes(); + user.setPrimaryGroup(null); + plugin.getUserManager().giveDefaultIfNeeded(user, false); + } + return true; } - } - - } else { - success = true; + }, false); + } finally { + user.getIoLock().unlock(); } - - 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; - } + user.getIoLock().lock(); + try { + return call(() -> { + 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; - } - } + if (!userFile.exists()) { + try { + userFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } - return 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 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; + }); + }, false); + } finally { + user.getIoLock().unlock(); + } } @Override public boolean cleanupUsers() { - File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json")); - if (files == null) return false; + return call(() -> { + File[] files = usersDir.listFiles((dir, name1) -> name1.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); + 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(); + } } - 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(); } } - - if (shouldDelete) { - file.delete(); - } - } - return true; + return true; + }, false); } @Override @@ -219,82 +242,81 @@ public class JSONDatastore extends FlatfileDatastore { @Override public boolean createAndLoadGroup(String name) { - Group group = plugin.getGroupManager().make(name); + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, name + ".json"); + if (groupFile.exists()) { + return 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)); + } - File groupFile = new File(groupsDir, name + ".json"); - if (!groupFile.exists()) { - try { - groupFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } + reader.endObject(); + reader.endObject(); + return true; + }); + } else { + 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()); + return 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; + }); } - writer.endObject(); - writer.endObject(); - return true; - }); - - if (!success) return false; + }, false); + } finally { + group.getIoLock().unlock(); } - - 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; + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, name + ".json"); + return groupFile.exists() && 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; + }); + }, false); + } finally { + group.getIoLock().unlock(); } - - 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 @@ -305,122 +327,141 @@ public class JSONDatastore extends FlatfileDatastore { .map(s -> s.substring(0, s.length() - 5)) .collect(Collectors.toList()); - plugin.getGroupManager().unloadAll(); groups.forEach(this::loadGroup); + + GroupManager gm = plugin.getGroupManager(); + gm.getAll().values().stream() + .filter(g -> !groups.contains(g.getName())) + .forEach(gm::unload); 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; - } - } + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, group.getName() + ".json"); + if (!groupFile.exists()) { + try { + groupFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } - return 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 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; + }); + }, false); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean deleteGroup(Group group) { - File groupFile = new File(groupsDir, group.getName() + ".json"); - if (groupFile.exists()) { - groupFile.delete(); + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, group.getName() + ".json"); + if (groupFile.exists()) { + groupFile.delete(); + } + return true; + }, false); + } finally { + group.getIoLock().unlock(); } - return true; } @Override public boolean createAndLoadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - List groups = new ArrayList<>(); + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, name + ".json"); + if (trackFile.exists()) { + return doRead(trackFile, reader -> { + reader.beginObject(); + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); // groups record + reader.beginArray(); + List groups = new ArrayList<>(); + while (reader.hasNext()) { + groups.add(reader.nextString()); + } + track.setGroups(groups); + reader.endArray(); + reader.endObject(); + return true; + }); + } else { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } - 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); + return 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; + }); } - writer.endArray(); - writer.endObject(); - return true; - }); - - if (!success) return false; + }, false); + } finally { + track.getIoLock().unlock(); } - - 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<>(); + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, name + ".json"); + return trackFile.exists() && doRead(trackFile, reader -> { + reader.beginObject(); + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); // groups + reader.beginArray(); + List groups = new ArrayList<>(); + while (reader.hasNext()) { + groups.add(reader.nextString()); + } + track.setGroups(groups); + reader.endArray(); + reader.endObject(); + return true; + }); - File trackFile = new File(tracksDir, name + ".json"); - if (!trackFile.exists()) { - return false; + }, false); + } finally { + track.getIoLock().unlock(); } - - 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 @@ -431,44 +472,71 @@ public class JSONDatastore extends FlatfileDatastore { .map(s -> s.substring(0, s.length() - 5)) .collect(Collectors.toList()); - plugin.getTrackManager().unloadAll(); tracks.forEach(this::loadTrack); + + TrackManager tm = plugin.getTrackManager(); + tm.getAll().values().stream() + .filter(t -> !tracks.contains(t.getName())) + .forEach(tm::unload); 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; - } - } + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, track.getName() + ".json"); + if (!trackFile.exists()) { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } - return 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 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; + }); + }, false); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean deleteTrack(Track track) { - File trackFile = new File(tracksDir, track.getName() + ".json"); - if (trackFile.exists()) { - trackFile.delete(); + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, track.getName() + ".json"); + if (trackFile.exists()) { + trackFile.delete(); + } + return true; + }, false); + } finally { + track.getIoLock().unlock(); + } + } + + private static T call(Callable c, T def) { + try { + return c.call(); + } catch (Exception e) { + e.printStackTrace(); + return def; } - return true; } interface WriteOperation { diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java index 8f518d3d..93600a3f 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/MongoDBDatastore.java @@ -39,6 +39,7 @@ import me.lucko.luckperms.storage.DatastoreConfiguration; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.User; +import me.lucko.luckperms.users.UserIdentifier; import org.bson.Document; import java.util.*; @@ -140,52 +141,77 @@ public class MongoDBDatastore extends Datastore { @Override public boolean loadUser(UUID uuid, String username) { - User user = plugin.getUserManager().make(uuid, username); - boolean success = call(() -> { - MongoCollection c = database.getCollection("users"); + User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); + user.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("users"); - try (MongoCursor cursor = c.find(new Document("_id", user.getUuid())).iterator()) { - if (cursor.hasNext()) { - Document d = cursor.next(); - user.setPrimaryGroup(d.getString("primaryGroup")); - user.setNodes(revert((Map) d.get("perms"))); + try (MongoCursor cursor = c.find(new Document("_id", user.getUuid())).iterator()) { + if (cursor.hasNext()) { + // User exists, let's load. + Document d = cursor.next(); + user.setNodes(revert((Map) d.get("perms"))); + user.setPrimaryGroup(d.getString("primaryGroup")); - if (user.getName().equalsIgnoreCase("null")) { - user.setName(d.getString("name")); - } else { - if (!d.getString("name").equals(user.getName())) { + boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); + + if (user.getName().equalsIgnoreCase("null")) { + user.setName(d.getString("name")); + } else { + if (!d.getString("name").equals(user.getName())) { + save = true; + } + } + + if (save) { c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); } + } else { + if (plugin.getUserManager().shouldSave(user)) { + user.clearNodes(); + user.setPrimaryGroup(null); + plugin.getUserManager().giveDefaultIfNeeded(user, false); + } } } - } - return true; - }, false); - - if (success) plugin.getUserManager().updateOrSet(user); - return success; + return true; + }, false); + } finally { + user.getIoLock().unlock(); + } } @Override public boolean saveUser(User user) { if (!plugin.getUserManager().shouldSave(user)) { - return call(() -> { - MongoCollection c = database.getCollection("users"); - return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged(); - }, false); + user.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("users"); + return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged(); + }, false); + } finally { + user.getIoLock().unlock(); + } } - return call(() -> { - MongoCollection c = database.getCollection("users"); - try (MongoCursor cursor = c.find(new Document("_id", user.getUuid())).iterator()) { - if (!cursor.hasNext()) { - c.insertOne(fromUser(user)); - } else { - c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + user.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("users"); + try (MongoCursor cursor = c.find(new Document("_id", user.getUuid())).iterator()) { + if (!cursor.hasNext()) { + c.insertOne(fromUser(user)); + } else { + c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + } } - } - return true; - }, false); + return true; + }, false); + } finally { + user.getIoLock().unlock(); + } } @Override @@ -214,85 +240,103 @@ public class MongoDBDatastore extends Datastore { @Override public boolean createAndLoadGroup(String name) { - Group group = plugin.getGroupManager().make(name); - boolean success = call(() -> { - MongoCollection c = database.getCollection("groups"); + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("groups"); - try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { - if (!cursor.hasNext()) { - c.insertOne(fromGroup(group)); - } else { - Document d = cursor.next(); - group.setNodes(revert((Map) d.get("perms"))); + try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { + if (cursor.hasNext()) { + // Group exists, let's load. + Document d = cursor.next(); + group.setNodes(revert((Map) d.get("perms"))); + } else { + c.insertOne(fromGroup(group)); + } } - } - return true; - }, false); - - if (success) plugin.getGroupManager().updateOrSet(group); - return success; + return true; + }, false); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean loadGroup(String name) { - Group group = plugin.getGroupManager().make(name); - boolean success = call(() -> { - MongoCollection c = database.getCollection("groups"); + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("groups"); - try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { - if (cursor.hasNext()) { - Document d = cursor.next(); - group.setNodes(revert((Map) d.get("perms"))); - return true; + try (MongoCursor cursor = c.find(new Document("_id", group.getName())).iterator()) { + if (cursor.hasNext()) { + Document d = cursor.next(); + group.setNodes(revert((Map) d.get("perms"))); + return true; + } + return false; } - return false; - } - }, false); - - if (success) plugin.getGroupManager().updateOrSet(group); - return success; + }, false); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean loadAllGroups() { - List groups = new ArrayList<>(); + List groups = new ArrayList<>(); boolean success = call(() -> { MongoCollection c = database.getCollection("groups"); + boolean b = true; try (MongoCursor cursor = c.find().iterator()) { while (cursor.hasNext()) { - Document d = cursor.next(); - Group group = plugin.getGroupManager().make(d.getString("_id")); - group.setNodes(revert((Map) d.get("perms"))); - groups.add(group); + String name = cursor.next().getString("_id"); + if (!loadGroup(name)) { + b = false; + } + groups.add(name); } } - - return true; + return b; }, false); if (success) { GroupManager gm = plugin.getGroupManager(); - gm.unloadAll(); - groups.forEach(gm::set); + gm.getAll().values().stream() + .filter(g -> !groups.contains(g.getName())) + .forEach(gm::unload); } return success; } @Override public boolean saveGroup(Group group) { - return call(() -> { - MongoCollection c = database.getCollection("groups"); - return c.replaceOne(new Document("_id", group.getName()), fromGroup(group)).wasAcknowledged(); - }, false); + group.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("groups"); + return c.replaceOne(new Document("_id", group.getName()), fromGroup(group)).wasAcknowledged(); + }, false); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean deleteGroup(Group group) { - boolean success = call(() -> { - MongoCollection c = database.getCollection("groups"); - return c.deleteOne(new Document("_id", group.getName())).wasAcknowledged(); - }, false); + group.getIoLock().lock(); + boolean success; + try { + success = call(() -> { + MongoCollection c = database.getCollection("groups"); + return c.deleteOne(new Document("_id", group.getName())).wasAcknowledged(); + }, false); + } finally { + group.getIoLock().unlock(); + } if (success) plugin.getGroupManager().unload(group); return success; @@ -300,85 +344,102 @@ public class MongoDBDatastore extends Datastore { @Override public boolean createAndLoadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - boolean success = call(() -> { - MongoCollection c = database.getCollection("tracks"); + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("tracks"); - try (MongoCursor cursor = c.find(new Document("_id", track.getName())).iterator()) { - if (!cursor.hasNext()) { - c.insertOne(fromTrack(track)); - } else { - Document d = cursor.next(); - track.setGroups((List) d.get("groups")); + try (MongoCursor cursor = c.find(new Document("_id", track.getName())).iterator()) { + if (!cursor.hasNext()) { + c.insertOne(fromTrack(track)); + } else { + Document d = cursor.next(); + track.setGroups((List) d.get("groups")); + } } - } - return true; - }, false); - - if (success) plugin.getTrackManager().updateOrSet(track); - return success; + return true; + }, false); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean loadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - boolean success = call(() -> { - MongoCollection c = database.getCollection("tracks"); + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("tracks"); - try (MongoCursor cursor = c.find(new Document("_id", track.getName())).iterator()) { - if (cursor.hasNext()) { - Document d = cursor.next(); - track.setGroups((List) d.get("groups")); - return true; + try (MongoCursor cursor = c.find(new Document("_id", track.getName())).iterator()) { + if (cursor.hasNext()) { + Document d = cursor.next(); + track.setGroups((List) d.get("groups")); + return true; + } + return false; } - return false; - } - }, false); - - if (success) plugin.getTrackManager().updateOrSet(track); - return success; + }, false); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean loadAllTracks() { - List tracks = new ArrayList<>(); + List tracks = new ArrayList<>(); boolean success = call(() -> { MongoCollection c = database.getCollection("tracks"); + boolean b = true; try (MongoCursor cursor = c.find().iterator()) { while (cursor.hasNext()) { - Document d = cursor.next(); - Track track = plugin.getTrackManager().make(d.getString("_id")); - track.setGroups((List) d.get("groups")); - tracks.add(track); + String name = cursor.next().getString("_id"); + if (!loadTrack(name)) { + b = false; + } + tracks.add(name); } } - - return true; + return b; }, false); if (success) { TrackManager tm = plugin.getTrackManager(); - tm.unloadAll(); - tracks.forEach(tm::set); + tm.getAll().values().stream() + .filter(t -> !tracks.contains(t.getName())) + .forEach(tm::unload); } return success; } @Override public boolean saveTrack(Track track) { - return call(() -> { - MongoCollection c = database.getCollection("tracks"); - return c.replaceOne(new Document("_id", track.getName()), fromTrack(track)).wasAcknowledged(); - }, false); + track.getIoLock().lock(); + try { + return call(() -> { + MongoCollection c = database.getCollection("tracks"); + return c.replaceOne(new Document("_id", track.getName()), fromTrack(track)).wasAcknowledged(); + }, false); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean deleteTrack(Track track) { - boolean success = call(() -> { - MongoCollection c = database.getCollection("tracks"); - return c.deleteOne(new Document("_id", track.getName())).wasAcknowledged(); - }, false); + track.getIoLock().lock(); + boolean success; + try { + success = call(() -> { + MongoCollection c = database.getCollection("tracks"); + return c.deleteOne(new Document("_id", track.getName())).wasAcknowledged(); + }, false); + } finally { + track.getIoLock().unlock(); + } if (success) plugin.getTrackManager().unload(track); return success; diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java index 9954f70a..4e9c8154 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/SQLDatastore.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.storage.Datastore; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.User; +import me.lucko.luckperms.users.UserIdentifier; import java.lang.reflect.Type; import java.sql.Connection; @@ -142,20 +143,29 @@ abstract class SQLDatastore extends Datastore { @Override public boolean loadUser(UUID uuid, String username) { - User user = plugin.getUserManager().make(uuid, username); - boolean success = runQuery(USER_SELECT, - preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), - resultSet -> { - if (resultSet.next()) { - // User exists, let's load. - Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); - user.setNodes(nodes); - user.setPrimaryGroup(resultSet.getString("primary_group")); + User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); + user.getIoLock().lock(); + try { + return runQuery(USER_SELECT, + preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), + resultSet -> { + if (resultSet.next()) { + // User exists, let's load. + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + user.setNodes(nodes); + user.setPrimaryGroup(resultSet.getString("primary_group")); - if (user.getName().equalsIgnoreCase("null")) { - user.setName(resultSet.getString("name")); - } else { - if (!resultSet.getString("name").equals(user.getName())) { + boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); + + if (user.getName().equalsIgnoreCase("null")) { + user.setName(resultSet.getString("name")); + } else { + if (!resultSet.getString("name").equals(user.getName())) { + save = true; + } + } + + if (save) { runQuery(USER_UPDATE, preparedStatement -> { preparedStatement.setString(1, user.getName()); preparedStatement.setString(2, user.getPrimaryGroup()); @@ -163,47 +173,62 @@ abstract class SQLDatastore extends Datastore { preparedStatement.setString(4, user.getUuid().toString()); }); } + } else { + if (plugin.getUserManager().shouldSave(user)) { + user.clearNodes(); + user.setPrimaryGroup(null); + plugin.getUserManager().giveDefaultIfNeeded(user, false); + } } + return true; } - return true; - } - ); - - if (success) plugin.getUserManager().updateOrSet(user); - return success; + ); + } finally { + user.getIoLock().unlock(); + } } @Override public boolean saveUser(User user) { if (!plugin.getUserManager().shouldSave(user)) { - return runQuery(USER_DELETE, preparedStatement -> { - preparedStatement.setString(1, user.getUuid().toString()); - }); + user.getIoLock().lock(); + try { + return runQuery(USER_DELETE, preparedStatement -> { + preparedStatement.setString(1, user.getUuid().toString()); + }); + } finally { + user.getIoLock().unlock(); + } } - return runQuery(USER_SELECT, - preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), - resultSet -> { - if (!resultSet.next()) { - // Doesn't already exist, let's insert. - return runQuery(USER_INSERT, preparedStatement -> { - preparedStatement.setString(1, user.getUuid().toString()); - preparedStatement.setString(2, user.getName()); - preparedStatement.setString(3, user.getPrimaryGroup()); - preparedStatement.setString(4, gson.toJson(exportToLegacy(user.getNodes()))); - }); + user.getIoLock().lock(); + try { + return runQuery(USER_SELECT, + preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), + resultSet -> { + if (!resultSet.next()) { + // Doesn't already exist, let's insert. + return runQuery(USER_INSERT, preparedStatement -> { + preparedStatement.setString(1, user.getUuid().toString()); + preparedStatement.setString(2, user.getName()); + preparedStatement.setString(3, user.getPrimaryGroup()); + preparedStatement.setString(4, gson.toJson(exportToLegacy(user.getNodes()))); + }); - } else { - // User exists, let's update. - return runQuery(USER_UPDATE, preparedStatement -> { - preparedStatement.setString(1, user.getName()); - preparedStatement.setString(2, user.getPrimaryGroup()); - preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); - preparedStatement.setString(4, user.getUuid().toString()); - }); + } else { + // User exists, let's update. + return runQuery(USER_UPDATE, preparedStatement -> { + preparedStatement.setString(1, user.getName()); + preparedStatement.setString(2, user.getPrimaryGroup()); + preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); + preparedStatement.setString(4, user.getUuid().toString()); + }); + } } - } - ); + ); + } finally { + user.getIoLock().unlock(); + } } @Override @@ -230,80 +255,101 @@ abstract class SQLDatastore extends Datastore { @Override public boolean createAndLoadGroup(String name) { - Group group = plugin.getGroupManager().make(name); - boolean success = runQuery(GROUP_SELECT, - preparedStatement -> preparedStatement.setString(1, group.getName()), - resultSet -> { - if (!resultSet.next()) { - return runQuery(GROUP_INSERT, preparedStatement -> { - preparedStatement.setString(1, group.getName()); - preparedStatement.setString(2, gson.toJson(exportToLegacy(group.getNodes()))); - }); - } else { - Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); - group.setNodes(nodes); - return true; + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return runQuery(GROUP_SELECT, + preparedStatement -> preparedStatement.setString(1, group.getName()), + resultSet -> { + if (resultSet.next()) { + // Group exists, let's load. + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + group.setNodes(nodes); + return true; + } else { + return runQuery(GROUP_INSERT, preparedStatement -> { + preparedStatement.setString(1, group.getName()); + preparedStatement.setString(2, gson.toJson(exportToLegacy(group.getNodes()))); + }); + } } - } - ); - - if (success) plugin.getGroupManager().updateOrSet(group); - return success; + ); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean loadGroup(String name) { - Group group = plugin.getGroupManager().make(name); - boolean success = runQuery(GROUP_SELECT, - preparedStatement -> preparedStatement.setString(1, name), - resultSet -> { - if (resultSet.next()) { - Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); - group.setNodes(nodes); - return true; + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return runQuery(GROUP_SELECT, + preparedStatement -> preparedStatement.setString(1, name), + resultSet -> { + if (resultSet.next()) { + // Group exists, let's load. + Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); + group.setNodes(nodes); + return true; + } + return false; } - return false; - } - ); + ); + } finally { + group.getIoLock().unlock(); + } - if (success) plugin.getGroupManager().updateOrSet(group); - return success; } @Override public boolean loadAllGroups() { - List groups = new ArrayList<>(); + List groups = new ArrayList<>(); boolean success = runQuery(GROUP_SELECT_ALL, resultSet -> { + boolean b = true; while (resultSet.next()) { - Group group = plugin.getGroupManager().make(resultSet.getString("name")); - Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); - group.setNodes(nodes); - groups.add(group); + String name = resultSet.getString("name"); + if (!loadGroup(name)) { + b = false; + } + groups.add(name); } - return true; + return b; }); if (success) { GroupManager gm = plugin.getGroupManager(); - gm.unloadAll(); - groups.forEach(gm::set); + gm.getAll().values().stream() + .filter(g -> !groups.contains(g.getName())) + .forEach(gm::unload); } return success; } @Override public boolean saveGroup(Group group) { - return runQuery(GROUP_UPDATE, preparedStatement -> { - preparedStatement.setString(1, gson.toJson(exportToLegacy(group.getNodes()))); - preparedStatement.setString(2, group.getName()); - }); + group.getIoLock().lock(); + try { + return runQuery(GROUP_UPDATE, preparedStatement -> { + preparedStatement.setString(1, gson.toJson(exportToLegacy(group.getNodes()))); + preparedStatement.setString(2, group.getName()); + }); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean deleteGroup(Group group) { - boolean success = runQuery(GROUP_DELETE, preparedStatement -> { - preparedStatement.setString(1, group.getName()); - }); + group.getIoLock().lock(); + boolean success; + try { + success = runQuery(GROUP_DELETE, preparedStatement -> { + preparedStatement.setString(1, group.getName()); + }); + } finally { + group.getIoLock().unlock(); + } if (success) plugin.getGroupManager().unload(group); return success; @@ -311,77 +357,97 @@ abstract class SQLDatastore extends Datastore { @Override public boolean createAndLoadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - boolean success = runQuery(TRACK_SELECT, - preparedStatement -> preparedStatement.setString(1, track.getName()), - resultSet -> { - if (!resultSet.next()) { - return runQuery(TRACK_INSERT, preparedStatement -> { - preparedStatement.setString(1, track.getName()); - preparedStatement.setString(2, gson.toJson(track.getGroups())); - }); - } else { - track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); - return true; + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return runQuery(TRACK_SELECT, + preparedStatement -> preparedStatement.setString(1, track.getName()), + resultSet -> { + if (resultSet.next()) { + // Track exists, let's load. + track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); + return true; + } else { + return runQuery(TRACK_INSERT, preparedStatement -> { + preparedStatement.setString(1, track.getName()); + preparedStatement.setString(2, gson.toJson(track.getGroups())); + }); + } } - } - ); - - if (success) plugin.getTrackManager().updateOrSet(track); - return success; + ); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean loadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - boolean success = runQuery(TRACK_SELECT, - preparedStatement -> preparedStatement.setString(1, name), - resultSet -> { - if (resultSet.next()) { - track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); - return true; + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return runQuery(TRACK_SELECT, + preparedStatement -> preparedStatement.setString(1, name), + resultSet -> { + if (resultSet.next()) { + track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); + return true; + } + return false; } - return false; - } - ); - - if (success) plugin.getTrackManager().updateOrSet(track); - return success; + ); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean loadAllTracks() { - List tracks = new ArrayList<>(); + List tracks = new ArrayList<>(); boolean success = runQuery(TRACK_SELECT_ALL, resultSet -> { + boolean b = true; while (resultSet.next()) { - Track track = plugin.getTrackManager().make(resultSet.getString("name")); - track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); - tracks.add(track); + String name = resultSet.getString("name"); + if (!loadTrack(name)) { + b = false; + } + tracks.add(name); } - return true; + return b; }); if (success) { TrackManager tm = plugin.getTrackManager(); - tm.unloadAll(); - tracks.forEach(tm::set); + tm.getAll().values().stream() + .filter(t -> !tracks.contains(t.getName())) + .forEach(tm::unload); } return success; } @Override public boolean saveTrack(Track track) { - return runQuery(TRACK_UPDATE, preparedStatement -> { - preparedStatement.setString(1, gson.toJson(track.getGroups())); - preparedStatement.setString(2, track.getName()); - }); + track.getIoLock().lock(); + try { + return runQuery(TRACK_UPDATE, preparedStatement -> { + preparedStatement.setString(1, gson.toJson(track.getGroups())); + preparedStatement.setString(2, track.getName()); + }); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean deleteTrack(Track track) { - boolean success = runQuery(TRACK_DELETE, preparedStatement -> { - preparedStatement.setString(1, track.getName()); - }); + track.getIoLock().lock(); + boolean success; + try { + success = runQuery(TRACK_DELETE, preparedStatement -> { + preparedStatement.setString(1, track.getName()); + }); + } finally { + track.getIoLock().unlock(); + } if (success) plugin.getTrackManager().unload(track); return success; 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 index ae4d1e89..b52c4d66 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/YAMLDatastore.java @@ -26,13 +26,17 @@ import lombok.Cleanup; import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.core.Node; import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.groups.GroupManager; import me.lucko.luckperms.tracks.Track; +import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.users.User; +import me.lucko.luckperms.users.UserIdentifier; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import java.io.*; import java.util.*; +import java.util.concurrent.Callable; import java.util.stream.Collectors; import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; @@ -76,96 +80,117 @@ public class YAMLDatastore extends FlatfileDatastore { @Override public boolean loadUser(UUID uuid, String username) { - User user = plugin.getUserManager().make(uuid, username); - boolean success = false; + User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username)); + user.getIoLock().lock(); + try { + return call(() -> { + File userFile = new File(usersDir, uuid.toString() + ".yml"); + if (userFile.exists()) { + return doRead(userFile, values -> { + // User exists, let's load. + String name = (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())); + } - 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())); + boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false); + + if (user.getName().equalsIgnoreCase("null")) { + user.setName(name); + } else { + if (!name.equals(user.getName())) { + save = true; + } + } + + if (save) { + Map data = new HashMap<>(); + data.put("uuid", user.getUuid().toString()); + data.put("name", user.getName()); + data.put("primary-group", user.getPrimaryGroup()); + data.put("perms", exportToLegacy(user.getNodes())); + doWrite(userFile, data); + } + return true; + }); + } else { + if (plugin.getUserManager().shouldSave(user)) { + user.clearNodes(); + user.setPrimaryGroup(null); + plugin.getUserManager().giveDefaultIfNeeded(user, false); + } + return true; } - 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; + }, false); + } finally { + user.getIoLock().unlock(); } - - 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; - } + user.getIoLock().lock(); + try { + return call(() -> { + 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; - } - } + 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); + 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); + }, false); + } finally { + user.getIoLock().unlock(); + } } @Override public boolean cleanupUsers() { - File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".yml")); - if (files == null) return false; + return call(() -> { + File[] files = usersDir.listFiles((dir, name1) -> name1.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; - }); + 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(); + 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(); } } - - if (shouldDelete) { - file.delete(); - } - } - return true; + return true; + }, false); } @Override @@ -180,57 +205,57 @@ public class YAMLDatastore extends FlatfileDatastore { @Override public boolean createAndLoadGroup(String name) { - Group group = plugin.getGroupManager().make(name); + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, name + ".yml"); + if (groupFile.exists()) { + return 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; + }); + } else { + try { + groupFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } - 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; - } + Map values = new HashMap<>(); + values.put("name", group.getName()); + values.put("perms", exportToLegacy(group.getNodes())); + return doWrite(groupFile, values); + } + }, false); + } finally { + group.getIoLock().unlock(); } - - 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); + Group group = plugin.getGroupManager().getOrMake(name); + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, name + ".yml"); + return groupFile.exists() && 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; + }); - File groupFile = new File(groupsDir, name + ".yml"); - if (!groupFile.exists()) { - return false; + }, false); + } finally { + group.getIoLock().unlock(); } - - 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 @@ -241,89 +266,103 @@ public class YAMLDatastore extends FlatfileDatastore { .map(s -> s.substring(0, s.length() - 4)) .collect(Collectors.toList()); - plugin.getGroupManager().unloadAll(); groups.forEach(this::loadGroup); + + GroupManager gm = plugin.getGroupManager(); + gm.getAll().values().stream() + .filter(g -> !groups.contains(g.getName())) + .forEach(gm::unload); 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; - } - } + group.getIoLock().lock(); + try { + return call(() -> { + 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); + Map values = new HashMap<>(); + values.put("name", group.getName()); + values.put("perms", exportToLegacy(group.getNodes())); + return doWrite(groupFile, values); + }, false); + } finally { + group.getIoLock().unlock(); + } } @Override public boolean deleteGroup(Group group) { - File groupFile = new File(groupsDir, group.getName() + ".yml"); - if (groupFile.exists()) { - groupFile.delete(); + group.getIoLock().lock(); + try { + return call(() -> { + File groupFile = new File(groupsDir, group.getName() + ".yml"); + if (groupFile.exists()) { + groupFile.delete(); + } + return true; + }, false); + } finally { + group.getIoLock().unlock(); } - return true; } @Override public boolean createAndLoadTrack(String name) { - Track track = plugin.getTrackManager().make(name); - List groups = new ArrayList<>(); + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, name + ".yml"); + if (trackFile.exists()) { + return doRead(trackFile, values -> { + track.setGroups((List) values.get("groups")); + return true; + }); + } else { + try { + trackFile.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } - 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()); - Map values = new HashMap<>(); - values.put("name", track.getName()); - values.put("groups", track.getGroups()); - - if (!doWrite(trackFile, values)) { - return false; - } + return doWrite(trackFile, values); + } + }, false); + } finally { + track.getIoLock().unlock(); } - - 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; + Track track = plugin.getTrackManager().getOrMake(name); + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, name + ".yml"); + return trackFile.exists() && doRead(trackFile, values -> { + track.setGroups((List) values.get("groups")); + return true; + }); + }, false); + } finally { + track.getIoLock().unlock(); } - - 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 @@ -334,36 +373,63 @@ public class YAMLDatastore extends FlatfileDatastore { .map(s -> s.substring(0, s.length() - 4)) .collect(Collectors.toList()); - plugin.getTrackManager().unloadAll(); tracks.forEach(this::loadTrack); + + TrackManager tm = plugin.getTrackManager(); + tm.getAll().values().stream() + .filter(t -> !tracks.contains(t.getName())) + .forEach(tm::unload); 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; - } - } + track.getIoLock().lock(); + try { + return call(() -> { + 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); + Map values = new HashMap<>(); + values.put("name", track.getName()); + values.put("groups", track.getGroups()); + return doWrite(trackFile, values); + }, false); + } finally { + track.getIoLock().unlock(); + } } @Override public boolean deleteTrack(Track track) { - File trackFile = new File(tracksDir, track.getName() + ".yml"); - if (trackFile.exists()) { - trackFile.delete(); + track.getIoLock().lock(); + try { + return call(() -> { + File trackFile = new File(tracksDir, track.getName() + ".yml"); + if (trackFile.exists()) { + trackFile.delete(); + } + return true; + }, false); + } finally { + track.getIoLock().unlock(); + } + } + + private static T call(Callable c, T def) { + try { + return c.call(); + } catch (Exception e) { + e.printStackTrace(); + return def; } - return true; } interface ReadOperation { diff --git a/common/src/main/java/me/lucko/luckperms/tracks/Track.java b/common/src/main/java/me/lucko/luckperms/tracks/Track.java index 92517c13..dab0fdf8 100644 --- a/common/src/main/java/me/lucko/luckperms/tracks/Track.java +++ b/common/src/main/java/me/lucko/luckperms/tracks/Track.java @@ -34,6 +34,8 @@ import me.lucko.luckperms.utils.Identifiable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; @ToString @EqualsAndHashCode(of = {"name"}) @@ -51,6 +53,9 @@ public class Track implements Identifiable { */ private List groups = Collections.synchronizedList(new ArrayList<>()); + @Getter + private final Lock ioLock = new ReentrantLock(); + @Override public String getId() { return name; diff --git a/common/src/main/java/me/lucko/luckperms/tracks/TrackManager.java b/common/src/main/java/me/lucko/luckperms/tracks/TrackManager.java index 05ab3116..7ebbe83e 100644 --- a/common/src/main/java/me/lucko/luckperms/tracks/TrackManager.java +++ b/common/src/main/java/me/lucko/luckperms/tracks/TrackManager.java @@ -40,18 +40,13 @@ public class TrackManager extends AbstractManager { .collect(Collectors.toSet()); } - @Override - public void copy(Track from, Track to) { - to.setGroups(from.getGroups()); - } - /** * Makes a new track object * @param name The name of the track * @return a new {@link Track} object */ @Override - public Track make(String name) { + public Track apply(String name) { return new Track(name); } } diff --git a/common/src/main/java/me/lucko/luckperms/users/User.java b/common/src/main/java/me/lucko/luckperms/users/User.java index 689af3e6..8940ffe3 100644 --- a/common/src/main/java/me/lucko/luckperms/users/User.java +++ b/common/src/main/java/me/lucko/luckperms/users/User.java @@ -40,7 +40,7 @@ import java.util.UUID; @ToString(of = {"uuid"}) @EqualsAndHashCode(of = {"uuid"}, callSuper = false) -public abstract class User extends PermissionHolder implements Identifiable { +public abstract class User extends PermissionHolder implements Identifiable { /** * The users Mojang UUID @@ -75,8 +75,8 @@ public abstract class User extends PermissionHolder implements Identifiable + * + * 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.users; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; +import me.lucko.luckperms.utils.Identifiable; + +import java.util.UUID; + +@Getter +@ToString +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class UserIdentifier implements Identifiable { + public static UserIdentifier of(UUID uuid, String username) { + return new UserIdentifier(uuid, username); + } + + private final UUID uuid; + private final String username; + + @Override + public UUID getId() { + return getUuid(); + } + + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof UserIdentifier)) return false; + final UserIdentifier other = (UserIdentifier) o; + final Object thisUuid = this.getUuid(); + final Object otherUuid = other.getUuid(); + return thisUuid == null ? otherUuid == null : thisUuid.equals(otherUuid); + } + + public int hashCode() { + return 59 + (this.getUuid() == null ? 43 : this.getUuid().hashCode()); + } +} diff --git a/common/src/main/java/me/lucko/luckperms/users/UserManager.java b/common/src/main/java/me/lucko/luckperms/users/UserManager.java index e4da6d42..f276b8bc 100644 --- a/common/src/main/java/me/lucko/luckperms/users/UserManager.java +++ b/common/src/main/java/me/lucko/luckperms/users/UserManager.java @@ -34,7 +34,7 @@ import java.util.NoSuchElementException; import java.util.UUID; @RequiredArgsConstructor -public abstract class UserManager extends AbstractManager { +public abstract class UserManager extends AbstractManager { private final LuckPermsPlugin plugin; /** @@ -53,26 +53,15 @@ public abstract class UserManager extends AbstractManager { } } - @Override - public void preSet(User u) { - giveDefaultIfNeeded(u, true); - } - - @Override - public void copy(User from, User to) { - if (from.getPrimaryGroup() != null) { - // This isn't just a black user. we shouldn't override in that case. - to.setNodes(from.getNodes()); - to.setPrimaryGroup(from.getPrimaryGroup()); - } - to.refreshPermissions(); + public User get(UUID uuid) { + return get(UserIdentifier.of(uuid, null)); } /** * Set a user to the default group * @param user the user to give to */ - public void giveDefaultIfNeeded(User user, boolean save) { + public boolean giveDefaultIfNeeded(User user, boolean save) { boolean hasGroup = false; if (user.getPrimaryGroup() != null && !user.getPrimaryGroup().isEmpty()) { @@ -84,18 +73,22 @@ public abstract class UserManager extends AbstractManager { } } - if (!hasGroup) { - user.setPrimaryGroup("default"); - try { - user.setPermission("group.default", true); - } catch (ObjectAlreadyHasException ignored) { - ignored.printStackTrace(); - } - - if (save) { - plugin.getDatastore().saveUser(user, Callback.empty()); - } + if (hasGroup) { + return false; } + + user.setPrimaryGroup("default"); + try { + user.setPermission("group.default", true); + } catch (ObjectAlreadyHasException ignored) { + ignored.printStackTrace(); + } + + if (save) { + plugin.getDatastore().saveUser(user, Callback.empty()); + } + + return true; } public boolean shouldSave(User user) { @@ -132,14 +125,6 @@ public abstract class UserManager extends AbstractManager { */ public abstract void cleanup(User user); - /** - * Makes a new {@link User} object - * @param uuid The UUID of the user - * @param username The username of the user - * @return a new {@link User} object - */ - public abstract User make(UUID uuid, String username); - /** * Reloads the data of all online users */ diff --git a/common/src/main/java/me/lucko/luckperms/utils/AbstractManager.java b/common/src/main/java/me/lucko/luckperms/utils/AbstractManager.java index 420def3e..60000521 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/AbstractManager.java +++ b/common/src/main/java/me/lucko/luckperms/utils/AbstractManager.java @@ -26,18 +26,32 @@ import com.google.common.collect.ImmutableMap; import java.util.HashMap; import java.util.Map; +import java.util.function.Function; /** * An abstract manager class * @param the class used to identify each object held in this manager * @param the class this manager is "managing" */ -public abstract class AbstractManager> { +public abstract class AbstractManager> implements Function { private final Map objects = new HashMap<>(); public final Map getAll() { + Map map; synchronized (objects) { - return ImmutableMap.copyOf(objects); + map = ImmutableMap.copyOf(objects); + } + return map; + } + + /** + * Get an object by id + * @param id The id to search by + * @return a {@link T} object if the object is loaded or makes and returns a new object + */ + public final T getOrMake(I id) { + synchronized (objects) { + return objects.computeIfAbsent(id, this); } } @@ -52,38 +66,6 @@ public abstract class AbstractManager> { } } - /** - * Add a object to the loaded objects map - * @param t The object to add - */ - public final void set(T t) { - preSet(t); - synchronized (objects) { - objects.put(t.getId(), t); - } - } - - protected void preSet(T t) { - - } - - /** - * Updates (or sets if the object wasn't already loaded) an object in the objects map - * @param t The object to update or set - */ - public final void updateOrSet(T t) { - synchronized (objects) { - if (!isLoaded(t.getId())) { - // The object isn't already loaded - set(t); - } else { - copy(t, objects.get(t.getId())); - } - } - } - - public abstract void copy(T from, T to); - /** * Check to see if a object is loaded or not * @param id The id of the object @@ -101,9 +83,11 @@ public abstract class AbstractManager> { */ public final void unload(T t) { if (t != null) { - preUnload(t); synchronized (objects) { - objects.remove(t.getId()); + objects.computeIfPresent(t.getId(), (i, t1) -> { + preUnload(t1); + return null; + }); } } } @@ -117,15 +101,9 @@ public abstract class AbstractManager> { */ public final void unloadAll() { synchronized (objects) { + objects.values().forEach(this::preUnload); objects.clear(); } } - /** - * Makes a new object - * @param id the id of the object - * @return a new {@link T} object - */ - public abstract T make(I id); - } diff --git a/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java b/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java index e4216abf..8211b5c1 100644 --- a/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java +++ b/sponge/src/main/java/me/lucko/luckperms/api/sponge/collections/UserCollection.java @@ -28,6 +28,7 @@ import me.lucko.luckperms.api.sponge.LuckPermsService; import me.lucko.luckperms.api.sponge.LuckPermsUserSubject; import me.lucko.luckperms.api.sponge.simple.SimpleCollection; import me.lucko.luckperms.users.User; +import me.lucko.luckperms.users.UserIdentifier; import me.lucko.luckperms.users.UserManager; import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.permission.PermissionService; @@ -62,7 +63,7 @@ public class UserCollection implements SubjectCollection { private void load(UUID uuid) { UUID internal = service.getPlugin().getUuidCache().getUUID(uuid); - if (!manager.isLoaded(internal)) { + if (!manager.isLoaded(UserIdentifier.of(uuid, null))) { return; } @@ -82,7 +83,7 @@ public class UserCollection implements SubjectCollection { return users.get(u); } - if (manager.isLoaded(u)) { + if (manager.isLoaded(UserIdentifier.of(u, null))) { load(u); return users.get(u); } @@ -108,7 +109,7 @@ public class UserCollection implements SubjectCollection { public boolean hasRegistered(@NonNull String id) { try { UUID u = UUID.fromString(id); - return manager.isLoaded(service.getPlugin().getUuidCache().getUUID(u)); + return manager.isLoaded(UserIdentifier.of(service.getPlugin().getUuidCache().getUUID(u), null)); } catch (IllegalArgumentException e) { User user = manager.get(id); return user != null; diff --git a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java index a580964c..7c3fab72 100644 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java @@ -46,13 +46,12 @@ public class SpongeUserManager extends UserManager implements ContextListener