Implement locks to hopefully resolve race conditions with I/O - experimental

This commit is contained in:
Luck 2016-10-01 19:03:05 +01:00
parent b5ece8b5bd
commit 4787361e66
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
22 changed files with 1159 additions and 878 deletions

View File

@ -47,7 +47,7 @@ public class BukkitUserManager extends UserManager implements ContextListener<Pl
Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid())); Player player = plugin.getServer().getPlayer(plugin.getUuidCache().getExternalUUID(u.getUuid()));
if (player != null) { if (player != null) {
if (u.getLpPermissible() != null) { if (u.getLpPermissible() != null) {
Injector.unInject(player); Injector.unInject(player); // TODO is this needed?
u.setLpPermissible(null); u.setLpPermissible(null);
} }
@ -66,18 +66,17 @@ public class BukkitUserManager extends UserManager implements ContextListener<Pl
} }
@Override @Override
public User make(UUID uuid) { public User apply(UserIdentifier id) {
return new BukkitUser(uuid, plugin); BukkitUser user = id.getUsername() == null ?
} new BukkitUser(id.getUuid(), plugin) :
new BukkitUser(id.getUuid(), id.getUsername(), plugin);
@Override giveDefaultIfNeeded(user, false);
public User make(UUID uuid, String username) { return user;
return new BukkitUser(uuid, username, plugin);
} }
@Override @Override
public void updateAllUsers() { public void updateAllUsers() {
// Sometimes called async, so we need to get the players on the Bukkit thread. // Sometimes called async, as we need to get the players on the Bukkit thread.
plugin.doSync(() -> { plugin.doSync(() -> {
Set<UUID> players = plugin.getServer().getOnlinePlayers().stream() Set<UUID> players = plugin.getServer().getOnlinePlayers().stream()
.map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId()))

View File

@ -45,13 +45,12 @@ public class BungeeUserManager extends UserManager implements ContextListener<Pr
} }
@Override @Override
public User make(UUID uuid) { public User apply(UserIdentifier id) {
return new BungeeUser(uuid, plugin); BungeeUser user = id.getUsername() == null ?
} new BungeeUser(id.getUuid(), plugin) :
new BungeeUser(id.getUuid(), id.getUsername(), plugin);
@Override giveDefaultIfNeeded(user, false);
public User make(UUID uuid, String username) { return user;
return new BungeeUser(uuid, username, plugin);
} }
@Override @Override

View File

@ -32,6 +32,7 @@ import me.lucko.luckperms.api.context.IContextCalculator;
import me.lucko.luckperms.api.event.LPEvent; import me.lucko.luckperms.api.event.LPEvent;
import me.lucko.luckperms.api.event.LPListener; import me.lucko.luckperms.api.event.LPListener;
import me.lucko.luckperms.api.implementation.internal.*; import me.lucko.luckperms.api.implementation.internal.*;
import me.lucko.luckperms.users.UserIdentifier;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
@ -149,7 +150,7 @@ public class ApiProvider implements LuckPermsApi {
@Override @Override
public boolean isUserLoaded(@NonNull UUID uuid) { public boolean isUserLoaded(@NonNull UUID uuid) {
return plugin.getUserManager().isLoaded(uuid); return plugin.getUserManager().isLoaded(UserIdentifier.of(uuid, null));
} }
@Override @Override

View File

@ -63,7 +63,7 @@ public class GroupClone extends SubCommand<Group> {
return CommandResult.LOADING_ERROR; return CommandResult.LOADING_ERROR;
} }
plugin.getGroupManager().copy(group, newGroup); newGroup.setNodes(group.getNodes());
Message.CLONE_SUCCESS.send(sender, group.getName(), newGroup.getName()); Message.CLONE_SUCCESS.send(sender, group.getName(), newGroup.getName());
LogEntry.build().actor(sender).acted(group).action("clone " + newGroup.getName()).build().submit(plugin, sender); LogEntry.build().actor(sender).acted(group).action("clone " + newGroup.getName()).build().submit(plugin, sender);

View File

@ -68,7 +68,7 @@ public class GroupRename extends SubCommand<Group> {
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
plugin.getGroupManager().copy(group, newGroup); newGroup.setNodes(group.getNodes());
Message.RENAME_SUCCESS.send(sender, group.getName(), newGroup.getName()); Message.RENAME_SUCCESS.send(sender, group.getName(), newGroup.getName());
LogEntry.build().actor(sender).acted(group).action("rename " + newGroup.getName()).build().submit(plugin, sender); LogEntry.build().actor(sender).acted(group).action("rename " + newGroup.getName()).build().submit(plugin, sender);

View File

@ -63,7 +63,7 @@ public class TrackClone extends SubCommand<Track> {
return CommandResult.LOADING_ERROR; return CommandResult.LOADING_ERROR;
} }
plugin.getTrackManager().copy(track, newTrack); newTrack.setGroups(track.getGroups());
Message.CLONE_SUCCESS.send(sender, track.getName(), newTrack.getName()); Message.CLONE_SUCCESS.send(sender, track.getName(), newTrack.getName());
LogEntry.build().actor(sender).acted(track).action("clone " + newTrack.getName()).build().submit(plugin, sender); LogEntry.build().actor(sender).acted(track).action("clone " + newTrack.getName()).build().submit(plugin, sender);

View File

@ -68,7 +68,7 @@ public class TrackRename extends SubCommand<Track> {
return CommandResult.FAILURE; return CommandResult.FAILURE;
} }
plugin.getTrackManager().copy(track, newTrack); newTrack.setGroups(track.getGroups());
Message.RENAME_SUCCESS.send(sender, track.getName(), newTrack.getName()); Message.RENAME_SUCCESS.send(sender, track.getName(), newTrack.getName());
LogEntry.build().actor(sender).acted(track).action("rename " + newTrack.getName()).build().submit(plugin, sender); LogEntry.build().actor(sender).acted(track).action("rename " + newTrack.getName()).build().submit(plugin, sender);

View File

@ -40,6 +40,8 @@ import me.lucko.luckperms.groups.Group;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -74,6 +76,9 @@ public abstract class PermissionHolder {
@Getter @Getter
private Set<Node> transientNodes = ConcurrentHashMap.newKeySet(); private Set<Node> transientNodes = ConcurrentHashMap.newKeySet();
@Getter
private final Lock ioLock = new ReentrantLock();
/** /**
* Returns a Set of nodes in priority order * Returns a Set of nodes in priority order
* @return the holders transient and permanent nodes * @return the holders transient and permanent nodes

View File

@ -30,18 +30,13 @@ import me.lucko.luckperms.utils.AbstractManager;
public class GroupManager extends AbstractManager<String, Group> { public class GroupManager extends AbstractManager<String, Group> {
private final LuckPermsPlugin plugin; private final LuckPermsPlugin plugin;
@Override
public void copy(Group from, Group to) {
to.setNodes(from.getNodes());
}
/** /**
* Makes a new group object * Makes a new group object
* @param name The name of the group * @param name The name of the group
* @return a new {@link Group} object * @return a new {@link Group} object
*/ */
@Override @Override
public Group make(String name) { public Group apply(String name) {
return new Group(name, plugin); return new Group(name, plugin);
} }
} }

View File

@ -28,11 +28,15 @@ import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.core.Node; import me.lucko.luckperms.core.Node;
import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import me.lucko.luckperms.users.UserIdentifier;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
@ -73,138 +77,157 @@ public class JSONDatastore extends FlatfileDatastore {
@Override @Override
public boolean loadUser(UUID uuid, String username) { public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username); User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
boolean success = false; user.getIoLock().lock();
try {
File userFile = new File(usersDir, uuid.toString() + ".json"); return call(() -> {
if (userFile.exists()) { File userFile = new File(usersDir, uuid.toString() + ".json");
final String[] name = new String[1]; if (userFile.exists()) {
success = doRead(userFile, reader -> { return doRead(userFile, reader -> {
reader.beginObject(); reader.beginObject();
reader.nextName(); // uuid record reader.nextName(); // uuid record
reader.nextString(); // uuid reader.nextString(); // uuid
reader.nextName(); // name record reader.nextName(); // name record
name[0] = reader.nextString(); // name String name1 = reader.nextString(); // name
reader.nextName(); // primaryGroup record reader.nextName(); // primaryGroup record
user.setPrimaryGroup(reader.nextString()); // primaryGroup user.setPrimaryGroup(reader.nextString()); // primaryGroup
reader.nextName(); //perms reader.nextName(); // perms
reader.beginObject(); reader.beginObject();
while (reader.hasNext()) { while (reader.hasNext()) {
String node = reader.nextName(); String node = reader.nextName();
boolean b = reader.nextBoolean(); boolean b = reader.nextBoolean();
user.getNodes().add(Node.fromSerialisedNode(node, b)); user.getNodes().add(Node.fromSerialisedNode(node, b));
} }
reader.endObject();
reader.endObject(); reader.endObject();
reader.endObject();
return true; boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
});
if (user.getName().equalsIgnoreCase("null")) {
if (user.getName().equalsIgnoreCase("null")) { user.setName(name1);
user.setName(name[0]); } else {
} else { if (!name1.equals(user.getName())) {
if (!name[0].equals(user.getName())) { save = true;
doWrite(userFile, writer -> { }
writer.beginObject(); }
writer.name("uuid").value(user.getUuid().toString());
writer.name("name").value(user.getName()); if (save) {
writer.name("primaryGroup").value(user.getPrimaryGroup()); doWrite(userFile, writer -> {
writer.name("perms"); writer.beginObject();
writer.beginObject(); writer.name("uuid").value(user.getUuid().toString());
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) { writer.name("name").value(user.getName());
writer.name(e.getKey()).value(e.getValue().booleanValue()); 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;
});
} }
writer.endObject();
writer.endObject();
return true; return true;
}); });
} else {
if (plugin.getUserManager().shouldSave(user)) {
user.clearNodes();
user.setPrimaryGroup(null);
plugin.getUserManager().giveDefaultIfNeeded(user, false);
}
return true;
} }
} }, false);
} finally {
} else { user.getIoLock().unlock();
success = true;
} }
if (success) plugin.getUserManager().updateOrSet(user);
return success;
} }
@Override @Override
public boolean saveUser(User user) { public boolean saveUser(User user) {
File userFile = new File(usersDir, user.getUuid().toString() + ".json"); user.getIoLock().lock();
if (!plugin.getUserManager().shouldSave(user)) { try {
if (userFile.exists()) { return call(() -> {
userFile.delete(); File userFile = new File(usersDir, user.getUuid().toString() + ".json");
} if (!plugin.getUserManager().shouldSave(user)) {
return true; if (userFile.exists()) {
} userFile.delete();
}
return true;
}
if (!userFile.exists()) { if (!userFile.exists()) {
try { try {
userFile.createNewFile(); userFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return false; return false;
} }
} }
return doWrite(userFile, writer -> { return doWrite(userFile, writer -> {
writer.beginObject(); writer.beginObject();
writer.name("uuid").value(user.getUuid().toString()); writer.name("uuid").value(user.getUuid().toString());
writer.name("name").value(user.getName()); writer.name("name").value(user.getName());
writer.name("primaryGroup").value(user.getPrimaryGroup()); writer.name("primaryGroup").value(user.getPrimaryGroup());
writer.name("perms"); writer.name("perms");
writer.beginObject(); writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) { for (Map.Entry<String, Boolean> e : exportToLegacy(user.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue()); writer.name(e.getKey()).value(e.getValue().booleanValue());
} }
writer.endObject(); writer.endObject();
writer.endObject(); writer.endObject();
return true; return true;
}); });
}, false);
} finally {
user.getIoLock().unlock();
}
} }
@Override @Override
public boolean cleanupUsers() { public boolean cleanupUsers() {
File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json")); return call(() -> {
if (files == null) return false; File[] files = usersDir.listFiles((dir, name1) -> name1.endsWith(".json"));
if (files == null) return false;
for (File file : files) { for (File file : files) {
Map<String, Boolean> nodes = new HashMap<>(); Map<String, Boolean> nodes = new HashMap<>();
doRead(file, reader -> { doRead(file, reader -> {
reader.beginObject(); reader.beginObject();
reader.nextName(); // uuid record reader.nextName(); // uuid record
reader.nextString(); // uuid reader.nextString(); // uuid
reader.nextName(); // name record reader.nextName(); // name record
reader.nextString(); // name reader.nextString(); // name
reader.nextName(); // primaryGroup record reader.nextName(); // primaryGroup record
reader.nextString(); // primaryGroup reader.nextString(); // primaryGroup
reader.nextName(); //perms reader.nextName(); //perms
reader.beginObject(); reader.beginObject();
while (reader.hasNext()) { while (reader.hasNext()) {
String node = reader.nextName(); String node = reader.nextName();
boolean b = reader.nextBoolean(); boolean b = reader.nextBoolean();
nodes.put(node, b); 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();
}
} }
reader.endObject(); if (shouldDelete) {
reader.endObject(); file.delete();
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();
} }
} }
return true;
if (shouldDelete) { }, false);
file.delete();
}
}
return true;
} }
@Override @Override
@ -219,82 +242,81 @@ public class JSONDatastore extends FlatfileDatastore {
@Override @Override
public boolean createAndLoadGroup(String name) { 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"); reader.endObject();
if (!groupFile.exists()) { reader.endObject();
try { return true;
groupFile.createNewFile(); });
} catch (IOException e) { } else {
e.printStackTrace(); try {
return false; groupFile.createNewFile();
} } catch (IOException e) {
e.printStackTrace();
return false;
}
boolean success = doWrite(groupFile, writer -> { return doWrite(groupFile, writer -> {
writer.beginObject(); writer.beginObject();
writer.name("name").value(group.getName()); writer.name("name").value(group.getName());
writer.name("perms"); writer.name("perms");
writer.beginObject(); writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) { for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue()); writer.name(e.getKey()).value(e.getValue().booleanValue());
}
writer.endObject();
writer.endObject();
return true;
});
} }
writer.endObject(); }, false);
writer.endObject(); } finally {
return true; group.getIoLock().unlock();
});
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 @Override
public boolean loadGroup(String name) { public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().make(name); Group group = plugin.getGroupManager().getOrMake(name);
group.getIoLock().lock();
File groupFile = new File(groupsDir, name + ".json"); try {
if (!groupFile.exists()) { return call(() -> {
return false; 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 @Override
@ -305,122 +327,141 @@ public class JSONDatastore extends FlatfileDatastore {
.map(s -> s.substring(0, s.length() - 5)) .map(s -> s.substring(0, s.length() - 5))
.collect(Collectors.toList()); .collect(Collectors.toList());
plugin.getGroupManager().unloadAll();
groups.forEach(this::loadGroup); groups.forEach(this::loadGroup);
GroupManager gm = plugin.getGroupManager();
gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
return true; return true;
} }
@Override @Override
public boolean saveGroup(Group group) { public boolean saveGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json"); group.getIoLock().lock();
if (!groupFile.exists()) { try {
try { return call(() -> {
groupFile.createNewFile(); File groupFile = new File(groupsDir, group.getName() + ".json");
} catch (IOException e) { if (!groupFile.exists()) {
e.printStackTrace(); try {
return false; groupFile.createNewFile();
} } catch (IOException e) {
} e.printStackTrace();
return false;
}
}
return doWrite(groupFile, writer -> { return doWrite(groupFile, writer -> {
writer.beginObject(); writer.beginObject();
writer.name("name").value(group.getName()); writer.name("name").value(group.getName());
writer.name("perms"); writer.name("perms");
writer.beginObject(); writer.beginObject();
for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) { for (Map.Entry<String, Boolean> e : exportToLegacy(group.getNodes()).entrySet()) {
writer.name(e.getKey()).value(e.getValue().booleanValue()); writer.name(e.getKey()).value(e.getValue().booleanValue());
} }
writer.endObject(); writer.endObject();
writer.endObject(); writer.endObject();
return true; return true;
}); });
}, false);
} finally {
group.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteGroup(Group group) { public boolean deleteGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".json"); group.getIoLock().lock();
if (groupFile.exists()) { try {
groupFile.delete(); 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 @Override
public boolean createAndLoadTrack(String name) { public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
List<String> groups = new ArrayList<>(); 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<String> 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"); return doWrite(trackFile, writer -> {
if (!trackFile.exists()) { writer.beginObject();
try { writer.name("name").value(track.getName());
trackFile.createNewFile(); writer.name("groups");
} catch (IOException e) { writer.beginArray();
e.printStackTrace(); for (String s : track.getGroups()) {
return false; writer.value(s);
} }
writer.endArray();
boolean success = doWrite(trackFile, writer -> { writer.endObject();
writer.beginObject(); return true;
writer.name("name").value(track.getName()); });
writer.name("groups");
writer.beginArray();
for (String s : track.getGroups()) {
writer.value(s);
} }
writer.endArray(); }, false);
writer.endObject(); } finally {
return true; track.getIoLock().unlock();
});
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 @Override
public boolean loadTrack(String name) { public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
List<String> groups = new ArrayList<>(); 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<String> 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"); }, false);
if (!trackFile.exists()) { } finally {
return false; 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 @Override
@ -431,44 +472,71 @@ public class JSONDatastore extends FlatfileDatastore {
.map(s -> s.substring(0, s.length() - 5)) .map(s -> s.substring(0, s.length() - 5))
.collect(Collectors.toList()); .collect(Collectors.toList());
plugin.getTrackManager().unloadAll();
tracks.forEach(this::loadTrack); tracks.forEach(this::loadTrack);
TrackManager tm = plugin.getTrackManager();
tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);
return true; return true;
} }
@Override @Override
public boolean saveTrack(Track track) { public boolean saveTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json"); track.getIoLock().lock();
if (!trackFile.exists()) { try {
try { return call(() -> {
trackFile.createNewFile(); File trackFile = new File(tracksDir, track.getName() + ".json");
} catch (IOException e) { if (!trackFile.exists()) {
e.printStackTrace(); try {
return false; trackFile.createNewFile();
} } catch (IOException e) {
} e.printStackTrace();
return false;
}
}
return doWrite(trackFile, writer -> { return doWrite(trackFile, writer -> {
writer.beginObject(); writer.beginObject();
writer.name("name").value(track.getName()); writer.name("name").value(track.getName());
writer.name("groups"); writer.name("groups");
writer.beginArray(); writer.beginArray();
for (String s : track.getGroups()) { for (String s : track.getGroups()) {
writer.value(s); writer.value(s);
} }
writer.endArray(); writer.endArray();
writer.endObject(); writer.endObject();
return true; return true;
}); });
}, false);
} finally {
track.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteTrack(Track track) { public boolean deleteTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".json"); track.getIoLock().lock();
if (trackFile.exists()) { try {
trackFile.delete(); 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> T call(Callable<T> c, T def) {
try {
return c.call();
} catch (Exception e) {
e.printStackTrace();
return def;
} }
return true;
} }
interface WriteOperation { interface WriteOperation {

View File

@ -39,6 +39,7 @@ import me.lucko.luckperms.storage.DatastoreConfiguration;
import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import me.lucko.luckperms.users.UserIdentifier;
import org.bson.Document; import org.bson.Document;
import java.util.*; import java.util.*;
@ -140,52 +141,77 @@ public class MongoDBDatastore extends Datastore {
@Override @Override
public boolean loadUser(UUID uuid, String username) { public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username); User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
boolean success = call(() -> { user.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("users"); try {
return call(() -> {
MongoCollection<Document> c = database.getCollection("users");
try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
Document d = cursor.next(); // User exists, let's load.
user.setPrimaryGroup(d.getString("primaryGroup")); Document d = cursor.next();
user.setNodes(revert((Map<String, Boolean>) d.get("perms"))); user.setNodes(revert((Map<String, Boolean>) d.get("perms")));
user.setPrimaryGroup(d.getString("primaryGroup"));
if (user.getName().equalsIgnoreCase("null")) { boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
user.setName(d.getString("name"));
} else { if (user.getName().equalsIgnoreCase("null")) {
if (!d.getString("name").equals(user.getName())) { 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)); 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;
return true; }, false);
}, false); } finally {
user.getIoLock().unlock();
if (success) plugin.getUserManager().updateOrSet(user); }
return success;
} }
@Override @Override
public boolean saveUser(User user) { public boolean saveUser(User user) {
if (!plugin.getUserManager().shouldSave(user)) { if (!plugin.getUserManager().shouldSave(user)) {
return call(() -> { user.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("users"); try {
return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged(); return call(() -> {
}, false); MongoCollection<Document> c = database.getCollection("users");
return c.deleteOne(new Document("_id", user.getUuid())).wasAcknowledged();
}, false);
} finally {
user.getIoLock().unlock();
}
} }
return call(() -> { user.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("users"); try {
try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) { return call(() -> {
if (!cursor.hasNext()) { MongoCollection<Document> c = database.getCollection("users");
c.insertOne(fromUser(user)); try (MongoCursor<Document> cursor = c.find(new Document("_id", user.getUuid())).iterator()) {
} else { if (!cursor.hasNext()) {
c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); c.insertOne(fromUser(user));
} else {
c.replaceOne(new Document("_id", user.getUuid()), fromUser(user));
}
} }
} return true;
return true; }, false);
}, false); } finally {
user.getIoLock().unlock();
}
} }
@Override @Override
@ -214,85 +240,103 @@ public class MongoDBDatastore extends Datastore {
@Override @Override
public boolean createAndLoadGroup(String name) { public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().make(name); Group group = plugin.getGroupManager().getOrMake(name);
boolean success = call(() -> { group.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("groups"); try {
return call(() -> {
MongoCollection<Document> c = database.getCollection("groups");
try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) {
if (!cursor.hasNext()) { if (cursor.hasNext()) {
c.insertOne(fromGroup(group)); // Group exists, let's load.
} else { Document d = cursor.next();
Document d = cursor.next(); group.setNodes(revert((Map<String, Boolean>) d.get("perms")));
group.setNodes(revert((Map<String, Boolean>) d.get("perms"))); } else {
c.insertOne(fromGroup(group));
}
} }
} return true;
return true; }, false);
}, false); } finally {
group.getIoLock().unlock();
if (success) plugin.getGroupManager().updateOrSet(group); }
return success;
} }
@Override @Override
public boolean loadGroup(String name) { public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().make(name); Group group = plugin.getGroupManager().getOrMake(name);
boolean success = call(() -> { group.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("groups"); try {
return call(() -> {
MongoCollection<Document> c = database.getCollection("groups");
try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", group.getName())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
group.setNodes(revert((Map<String, Boolean>) d.get("perms"))); group.setNodes(revert((Map<String, Boolean>) d.get("perms")));
return true; return true;
}
return false;
} }
return false; }, false);
} } finally {
}, false); group.getIoLock().unlock();
}
if (success) plugin.getGroupManager().updateOrSet(group);
return success;
} }
@Override @Override
public boolean loadAllGroups() { public boolean loadAllGroups() {
List<Group> groups = new ArrayList<>(); List<String> groups = new ArrayList<>();
boolean success = call(() -> { boolean success = call(() -> {
MongoCollection<Document> c = database.getCollection("groups"); MongoCollection<Document> c = database.getCollection("groups");
boolean b = true;
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); String name = cursor.next().getString("_id");
Group group = plugin.getGroupManager().make(d.getString("_id")); if (!loadGroup(name)) {
group.setNodes(revert((Map<String, Boolean>) d.get("perms"))); b = false;
groups.add(group); }
groups.add(name);
} }
} }
return b;
return true;
}, false); }, false);
if (success) { if (success) {
GroupManager gm = plugin.getGroupManager(); GroupManager gm = plugin.getGroupManager();
gm.unloadAll(); gm.getAll().values().stream()
groups.forEach(gm::set); .filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
} }
return success; return success;
} }
@Override @Override
public boolean saveGroup(Group group) { public boolean saveGroup(Group group) {
return call(() -> { group.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("groups"); try {
return c.replaceOne(new Document("_id", group.getName()), fromGroup(group)).wasAcknowledged(); return call(() -> {
}, false); MongoCollection<Document> c = database.getCollection("groups");
return c.replaceOne(new Document("_id", group.getName()), fromGroup(group)).wasAcknowledged();
}, false);
} finally {
group.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteGroup(Group group) { public boolean deleteGroup(Group group) {
boolean success = call(() -> { group.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("groups"); boolean success;
return c.deleteOne(new Document("_id", group.getName())).wasAcknowledged(); try {
}, false); success = call(() -> {
MongoCollection<Document> c = database.getCollection("groups");
return c.deleteOne(new Document("_id", group.getName())).wasAcknowledged();
}, false);
} finally {
group.getIoLock().unlock();
}
if (success) plugin.getGroupManager().unload(group); if (success) plugin.getGroupManager().unload(group);
return success; return success;
@ -300,85 +344,102 @@ public class MongoDBDatastore extends Datastore {
@Override @Override
public boolean createAndLoadTrack(String name) { public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
boolean success = call(() -> { track.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("tracks"); try {
return call(() -> {
MongoCollection<Document> c = database.getCollection("tracks");
try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) {
if (!cursor.hasNext()) { if (!cursor.hasNext()) {
c.insertOne(fromTrack(track)); c.insertOne(fromTrack(track));
} else { } else {
Document d = cursor.next(); Document d = cursor.next();
track.setGroups((List<String>) d.get("groups")); track.setGroups((List<String>) d.get("groups"));
}
} }
} return true;
return true; }, false);
}, false); } finally {
track.getIoLock().unlock();
if (success) plugin.getTrackManager().updateOrSet(track); }
return success;
} }
@Override @Override
public boolean loadTrack(String name) { public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
boolean success = call(() -> { track.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("tracks"); try {
return call(() -> {
MongoCollection<Document> c = database.getCollection("tracks");
try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) { try (MongoCursor<Document> cursor = c.find(new Document("_id", track.getName())).iterator()) {
if (cursor.hasNext()) { if (cursor.hasNext()) {
Document d = cursor.next(); Document d = cursor.next();
track.setGroups((List<String>) d.get("groups")); track.setGroups((List<String>) d.get("groups"));
return true; return true;
}
return false;
} }
return false; }, false);
} } finally {
}, false); track.getIoLock().unlock();
}
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
} }
@Override @Override
public boolean loadAllTracks() { public boolean loadAllTracks() {
List<Track> tracks = new ArrayList<>(); List<String> tracks = new ArrayList<>();
boolean success = call(() -> { boolean success = call(() -> {
MongoCollection<Document> c = database.getCollection("tracks"); MongoCollection<Document> c = database.getCollection("tracks");
boolean b = true;
try (MongoCursor<Document> cursor = c.find().iterator()) { try (MongoCursor<Document> cursor = c.find().iterator()) {
while (cursor.hasNext()) { while (cursor.hasNext()) {
Document d = cursor.next(); String name = cursor.next().getString("_id");
Track track = plugin.getTrackManager().make(d.getString("_id")); if (!loadTrack(name)) {
track.setGroups((List<String>) d.get("groups")); b = false;
tracks.add(track); }
tracks.add(name);
} }
} }
return b;
return true;
}, false); }, false);
if (success) { if (success) {
TrackManager tm = plugin.getTrackManager(); TrackManager tm = plugin.getTrackManager();
tm.unloadAll(); tm.getAll().values().stream()
tracks.forEach(tm::set); .filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);
} }
return success; return success;
} }
@Override @Override
public boolean saveTrack(Track track) { public boolean saveTrack(Track track) {
return call(() -> { track.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("tracks"); try {
return c.replaceOne(new Document("_id", track.getName()), fromTrack(track)).wasAcknowledged(); return call(() -> {
}, false); MongoCollection<Document> c = database.getCollection("tracks");
return c.replaceOne(new Document("_id", track.getName()), fromTrack(track)).wasAcknowledged();
}, false);
} finally {
track.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteTrack(Track track) { public boolean deleteTrack(Track track) {
boolean success = call(() -> { track.getIoLock().lock();
MongoCollection<Document> c = database.getCollection("tracks"); boolean success;
return c.deleteOne(new Document("_id", track.getName())).wasAcknowledged(); try {
}, false); success = call(() -> {
MongoCollection<Document> c = database.getCollection("tracks");
return c.deleteOne(new Document("_id", track.getName())).wasAcknowledged();
}, false);
} finally {
track.getIoLock().unlock();
}
if (success) plugin.getTrackManager().unload(track); if (success) plugin.getTrackManager().unload(track);
return success; return success;

View File

@ -33,6 +33,7 @@ import me.lucko.luckperms.storage.Datastore;
import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.tracks.TrackManager; import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import me.lucko.luckperms.users.UserIdentifier;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.sql.Connection; import java.sql.Connection;
@ -142,20 +143,29 @@ abstract class SQLDatastore extends Datastore {
@Override @Override
public boolean loadUser(UUID uuid, String username) { public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username); User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
boolean success = runQuery(USER_SELECT, user.getIoLock().lock();
preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), try {
resultSet -> { return runQuery(USER_SELECT,
if (resultSet.next()) { preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()),
// User exists, let's load. resultSet -> {
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); if (resultSet.next()) {
user.setNodes(nodes); // User exists, let's load.
user.setPrimaryGroup(resultSet.getString("primary_group")); Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
user.setNodes(nodes);
user.setPrimaryGroup(resultSet.getString("primary_group"));
if (user.getName().equalsIgnoreCase("null")) { boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
user.setName(resultSet.getString("name"));
} else { if (user.getName().equalsIgnoreCase("null")) {
if (!resultSet.getString("name").equals(user.getName())) { user.setName(resultSet.getString("name"));
} else {
if (!resultSet.getString("name").equals(user.getName())) {
save = true;
}
}
if (save) {
runQuery(USER_UPDATE, preparedStatement -> { runQuery(USER_UPDATE, preparedStatement -> {
preparedStatement.setString(1, user.getName()); preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getPrimaryGroup()); preparedStatement.setString(2, user.getPrimaryGroup());
@ -163,47 +173,62 @@ abstract class SQLDatastore extends Datastore {
preparedStatement.setString(4, user.getUuid().toString()); 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; );
} } finally {
); user.getIoLock().unlock();
}
if (success) plugin.getUserManager().updateOrSet(user);
return success;
} }
@Override @Override
public boolean saveUser(User user) { public boolean saveUser(User user) {
if (!plugin.getUserManager().shouldSave(user)) { if (!plugin.getUserManager().shouldSave(user)) {
return runQuery(USER_DELETE, preparedStatement -> { user.getIoLock().lock();
preparedStatement.setString(1, user.getUuid().toString()); try {
}); return runQuery(USER_DELETE, preparedStatement -> {
preparedStatement.setString(1, user.getUuid().toString());
});
} finally {
user.getIoLock().unlock();
}
} }
return runQuery(USER_SELECT, user.getIoLock().lock();
preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()), try {
resultSet -> { return runQuery(USER_SELECT,
if (!resultSet.next()) { preparedStatement -> preparedStatement.setString(1, user.getUuid().toString()),
// Doesn't already exist, let's insert. resultSet -> {
return runQuery(USER_INSERT, preparedStatement -> { if (!resultSet.next()) {
preparedStatement.setString(1, user.getUuid().toString()); // Doesn't already exist, let's insert.
preparedStatement.setString(2, user.getName()); return runQuery(USER_INSERT, preparedStatement -> {
preparedStatement.setString(3, user.getPrimaryGroup()); preparedStatement.setString(1, user.getUuid().toString());
preparedStatement.setString(4, gson.toJson(exportToLegacy(user.getNodes()))); preparedStatement.setString(2, user.getName());
}); preparedStatement.setString(3, user.getPrimaryGroup());
preparedStatement.setString(4, gson.toJson(exportToLegacy(user.getNodes())));
});
} else { } else {
// User exists, let's update. // User exists, let's update.
return runQuery(USER_UPDATE, preparedStatement -> { return runQuery(USER_UPDATE, preparedStatement -> {
preparedStatement.setString(1, user.getName()); preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getPrimaryGroup()); preparedStatement.setString(2, user.getPrimaryGroup());
preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes())));
preparedStatement.setString(4, user.getUuid().toString()); preparedStatement.setString(4, user.getUuid().toString());
}); });
}
} }
} );
); } finally {
user.getIoLock().unlock();
}
} }
@Override @Override
@ -230,80 +255,101 @@ abstract class SQLDatastore extends Datastore {
@Override @Override
public boolean createAndLoadGroup(String name) { public boolean createAndLoadGroup(String name) {
Group group = plugin.getGroupManager().make(name); Group group = plugin.getGroupManager().getOrMake(name);
boolean success = runQuery(GROUP_SELECT, group.getIoLock().lock();
preparedStatement -> preparedStatement.setString(1, group.getName()), try {
resultSet -> { return runQuery(GROUP_SELECT,
if (!resultSet.next()) { preparedStatement -> preparedStatement.setString(1, group.getName()),
return runQuery(GROUP_INSERT, preparedStatement -> { resultSet -> {
preparedStatement.setString(1, group.getName()); if (resultSet.next()) {
preparedStatement.setString(2, gson.toJson(exportToLegacy(group.getNodes()))); // Group exists, let's load.
}); Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE);
} else { group.setNodes(nodes);
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); return true;
group.setNodes(nodes); } else {
return true; return runQuery(GROUP_INSERT, preparedStatement -> {
preparedStatement.setString(1, group.getName());
preparedStatement.setString(2, gson.toJson(exportToLegacy(group.getNodes())));
});
}
} }
} );
); } finally {
group.getIoLock().unlock();
if (success) plugin.getGroupManager().updateOrSet(group); }
return success;
} }
@Override @Override
public boolean loadGroup(String name) { public boolean loadGroup(String name) {
Group group = plugin.getGroupManager().make(name); Group group = plugin.getGroupManager().getOrMake(name);
boolean success = runQuery(GROUP_SELECT, group.getIoLock().lock();
preparedStatement -> preparedStatement.setString(1, name), try {
resultSet -> { return runQuery(GROUP_SELECT,
if (resultSet.next()) { preparedStatement -> preparedStatement.setString(1, name),
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); resultSet -> {
group.setNodes(nodes); if (resultSet.next()) {
return true; // Group exists, let's load.
Map<String, Boolean> 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 @Override
public boolean loadAllGroups() { public boolean loadAllGroups() {
List<Group> groups = new ArrayList<>(); List<String> groups = new ArrayList<>();
boolean success = runQuery(GROUP_SELECT_ALL, resultSet -> { boolean success = runQuery(GROUP_SELECT_ALL, resultSet -> {
boolean b = true;
while (resultSet.next()) { while (resultSet.next()) {
Group group = plugin.getGroupManager().make(resultSet.getString("name")); String name = resultSet.getString("name");
Map<String, Boolean> nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); if (!loadGroup(name)) {
group.setNodes(nodes); b = false;
groups.add(group); }
groups.add(name);
} }
return true; return b;
}); });
if (success) { if (success) {
GroupManager gm = plugin.getGroupManager(); GroupManager gm = plugin.getGroupManager();
gm.unloadAll(); gm.getAll().values().stream()
groups.forEach(gm::set); .filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
} }
return success; return success;
} }
@Override @Override
public boolean saveGroup(Group group) { public boolean saveGroup(Group group) {
return runQuery(GROUP_UPDATE, preparedStatement -> { group.getIoLock().lock();
preparedStatement.setString(1, gson.toJson(exportToLegacy(group.getNodes()))); try {
preparedStatement.setString(2, group.getName()); return runQuery(GROUP_UPDATE, preparedStatement -> {
}); preparedStatement.setString(1, gson.toJson(exportToLegacy(group.getNodes())));
preparedStatement.setString(2, group.getName());
});
} finally {
group.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteGroup(Group group) { public boolean deleteGroup(Group group) {
boolean success = runQuery(GROUP_DELETE, preparedStatement -> { group.getIoLock().lock();
preparedStatement.setString(1, group.getName()); boolean success;
}); try {
success = runQuery(GROUP_DELETE, preparedStatement -> {
preparedStatement.setString(1, group.getName());
});
} finally {
group.getIoLock().unlock();
}
if (success) plugin.getGroupManager().unload(group); if (success) plugin.getGroupManager().unload(group);
return success; return success;
@ -311,77 +357,97 @@ abstract class SQLDatastore extends Datastore {
@Override @Override
public boolean createAndLoadTrack(String name) { public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
boolean success = runQuery(TRACK_SELECT, track.getIoLock().lock();
preparedStatement -> preparedStatement.setString(1, track.getName()), try {
resultSet -> { return runQuery(TRACK_SELECT,
if (!resultSet.next()) { preparedStatement -> preparedStatement.setString(1, track.getName()),
return runQuery(TRACK_INSERT, preparedStatement -> { resultSet -> {
preparedStatement.setString(1, track.getName()); if (resultSet.next()) {
preparedStatement.setString(2, gson.toJson(track.getGroups())); // Track exists, let's load.
}); track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE));
} else { return true;
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); } else {
return true; return runQuery(TRACK_INSERT, preparedStatement -> {
preparedStatement.setString(1, track.getName());
preparedStatement.setString(2, gson.toJson(track.getGroups()));
});
}
} }
} );
); } finally {
track.getIoLock().unlock();
if (success) plugin.getTrackManager().updateOrSet(track); }
return success;
} }
@Override @Override
public boolean loadTrack(String name) { public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
boolean success = runQuery(TRACK_SELECT, track.getIoLock().lock();
preparedStatement -> preparedStatement.setString(1, name), try {
resultSet -> { return runQuery(TRACK_SELECT,
if (resultSet.next()) { preparedStatement -> preparedStatement.setString(1, name),
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); resultSet -> {
return true; if (resultSet.next()) {
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE));
return true;
}
return false;
} }
return false; );
} } finally {
); track.getIoLock().unlock();
}
if (success) plugin.getTrackManager().updateOrSet(track);
return success;
} }
@Override @Override
public boolean loadAllTracks() { public boolean loadAllTracks() {
List<Track> tracks = new ArrayList<>(); List<String> tracks = new ArrayList<>();
boolean success = runQuery(TRACK_SELECT_ALL, resultSet -> { boolean success = runQuery(TRACK_SELECT_ALL, resultSet -> {
boolean b = true;
while (resultSet.next()) { while (resultSet.next()) {
Track track = plugin.getTrackManager().make(resultSet.getString("name")); String name = resultSet.getString("name");
track.setGroups(gson.fromJson(resultSet.getString("groups"), T_TYPE)); if (!loadTrack(name)) {
tracks.add(track); b = false;
}
tracks.add(name);
} }
return true; return b;
}); });
if (success) { if (success) {
TrackManager tm = plugin.getTrackManager(); TrackManager tm = plugin.getTrackManager();
tm.unloadAll(); tm.getAll().values().stream()
tracks.forEach(tm::set); .filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);
} }
return success; return success;
} }
@Override @Override
public boolean saveTrack(Track track) { public boolean saveTrack(Track track) {
return runQuery(TRACK_UPDATE, preparedStatement -> { track.getIoLock().lock();
preparedStatement.setString(1, gson.toJson(track.getGroups())); try {
preparedStatement.setString(2, track.getName()); return runQuery(TRACK_UPDATE, preparedStatement -> {
}); preparedStatement.setString(1, gson.toJson(track.getGroups()));
preparedStatement.setString(2, track.getName());
});
} finally {
track.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteTrack(Track track) { public boolean deleteTrack(Track track) {
boolean success = runQuery(TRACK_DELETE, preparedStatement -> { track.getIoLock().lock();
preparedStatement.setString(1, track.getName()); boolean success;
}); try {
success = runQuery(TRACK_DELETE, preparedStatement -> {
preparedStatement.setString(1, track.getName());
});
} finally {
track.getIoLock().unlock();
}
if (success) plugin.getTrackManager().unload(track); if (success) plugin.getTrackManager().unload(track);
return success; return success;

View File

@ -26,13 +26,17 @@ import lombok.Cleanup;
import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.LuckPermsPlugin;
import me.lucko.luckperms.core.Node; import me.lucko.luckperms.core.Node;
import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.groups.Group;
import me.lucko.luckperms.groups.GroupManager;
import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.tracks.Track;
import me.lucko.luckperms.tracks.TrackManager;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import me.lucko.luckperms.users.UserIdentifier;
import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.Yaml;
import java.io.*; import java.io.*;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy;
@ -76,96 +80,117 @@ public class YAMLDatastore extends FlatfileDatastore {
@Override @Override
public boolean loadUser(UUID uuid, String username) { public boolean loadUser(UUID uuid, String username) {
User user = plugin.getUserManager().make(uuid, username); User user = plugin.getUserManager().getOrMake(UserIdentifier.of(uuid, username));
boolean success = false; 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<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()));
}
File userFile = new File(usersDir, uuid.toString() + ".yml"); boolean save = plugin.getUserManager().giveDefaultIfNeeded(user, false);
if (userFile.exists()) {
final String[] name = {null}; if (user.getName().equalsIgnoreCase("null")) {
success = doRead(userFile, values -> { user.setName(name);
name[0] = (String) values.get("name"); } else {
user.setPrimaryGroup((String) values.get("primary-group")); if (!name.equals(user.getName())) {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms"); save = true;
for (Map.Entry<String, Boolean> e : perms.entrySet()) { }
user.getNodes().add(Node.fromSerialisedNode(e.getKey(), e.getValue())); }
if (save) {
Map<String, Object> 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; }, false);
}); } finally {
user.getIoLock().unlock();
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 @Override
public boolean saveUser(User user) { public boolean saveUser(User user) {
File userFile = new File(usersDir, user.getUuid().toString() + ".yml"); user.getIoLock().lock();
if (!plugin.getUserManager().shouldSave(user)) { try {
if (userFile.exists()) { return call(() -> {
userFile.delete(); File userFile = new File(usersDir, user.getUuid().toString() + ".yml");
} if (!plugin.getUserManager().shouldSave(user)) {
return true; if (userFile.exists()) {
} userFile.delete();
}
return true;
}
if (!userFile.exists()) { if (!userFile.exists()) {
try { try {
userFile.createNewFile(); userFile.createNewFile();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return false; return false;
} }
} }
Map<String, Object> values = new HashMap<>(); Map<String, Object> values = new HashMap<>();
values.put("uuid", user.getUuid().toString()); values.put("uuid", user.getUuid().toString());
values.put("name", user.getName()); values.put("name", user.getName());
values.put("primary-group", user.getPrimaryGroup()); values.put("primary-group", user.getPrimaryGroup());
values.put("perms", exportToLegacy(user.getNodes())); values.put("perms", exportToLegacy(user.getNodes()));
return doWrite(userFile, values); return doWrite(userFile, values);
}, false);
} finally {
user.getIoLock().unlock();
}
} }
@Override @Override
public boolean cleanupUsers() { public boolean cleanupUsers() {
File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".yml")); return call(() -> {
if (files == null) return false; File[] files = usersDir.listFiles((dir, name1) -> name1.endsWith(".yml"));
if (files == null) return false;
for (File file : files) { for (File file : files) {
Map<String, Boolean> nodes = new HashMap<>(); Map<String, Boolean> nodes = new HashMap<>();
doRead(file, values -> { doRead(file, values -> {
Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms"); Map<String, Boolean> perms = (Map<String, Boolean>) values.get("perms");
nodes.putAll(perms); nodes.putAll(perms);
return true; return true;
}); });
boolean shouldDelete = false; boolean shouldDelete = false;
if (nodes.size() == 1) { if (nodes.size() == 1) {
for (Map.Entry<String, Boolean> e : nodes.entrySet()) { for (Map.Entry<String, Boolean> e : nodes.entrySet()) {
// There's only one // There's only one
shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue(); shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue();
}
}
if (shouldDelete) {
file.delete();
} }
} }
return true;
if (shouldDelete) { }, false);
file.delete();
}
}
return true;
} }
@Override @Override
@ -180,57 +205,57 @@ public class YAMLDatastore extends FlatfileDatastore {
@Override @Override
public boolean createAndLoadGroup(String name) { 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<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;
});
} else {
try {
groupFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
File groupFile = new File(groupsDir, name + ".yml"); Map<String, Object> values = new HashMap<>();
if (!groupFile.exists()) { values.put("name", group.getName());
try { values.put("perms", exportToLegacy(group.getNodes()));
groupFile.createNewFile(); return doWrite(groupFile, values);
} catch (IOException e) { }
e.printStackTrace(); }, false);
return false; } finally {
} group.getIoLock().unlock();
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 @Override
public boolean loadGroup(String name) { 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<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;
});
File groupFile = new File(groupsDir, name + ".yml"); }, false);
if (!groupFile.exists()) { } finally {
return false; group.getIoLock().unlock();
} }
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 @Override
@ -241,89 +266,103 @@ public class YAMLDatastore extends FlatfileDatastore {
.map(s -> s.substring(0, s.length() - 4)) .map(s -> s.substring(0, s.length() - 4))
.collect(Collectors.toList()); .collect(Collectors.toList());
plugin.getGroupManager().unloadAll();
groups.forEach(this::loadGroup); groups.forEach(this::loadGroup);
GroupManager gm = plugin.getGroupManager();
gm.getAll().values().stream()
.filter(g -> !groups.contains(g.getName()))
.forEach(gm::unload);
return true; return true;
} }
@Override @Override
public boolean saveGroup(Group group) { public boolean saveGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".yml"); group.getIoLock().lock();
if (!groupFile.exists()) { try {
try { return call(() -> {
groupFile.createNewFile(); File groupFile = new File(groupsDir, group.getName() + ".yml");
} catch (IOException e) { if (!groupFile.exists()) {
e.printStackTrace(); try {
return false; groupFile.createNewFile();
} } catch (IOException e) {
} e.printStackTrace();
return false;
}
}
Map<String, Object> values = new HashMap<>(); Map<String, Object> values = new HashMap<>();
values.put("name", group.getName()); values.put("name", group.getName());
values.put("perms", exportToLegacy(group.getNodes())); values.put("perms", exportToLegacy(group.getNodes()));
return doWrite(groupFile, values); return doWrite(groupFile, values);
}, false);
} finally {
group.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteGroup(Group group) { public boolean deleteGroup(Group group) {
File groupFile = new File(groupsDir, group.getName() + ".yml"); group.getIoLock().lock();
if (groupFile.exists()) { try {
groupFile.delete(); 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 @Override
public boolean createAndLoadTrack(String name) { public boolean createAndLoadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
List<String> groups = new ArrayList<>(); track.getIoLock().lock();
try {
return call(() -> {
File trackFile = new File(tracksDir, name + ".yml");
if (trackFile.exists()) {
return doRead(trackFile, values -> {
track.setGroups((List<String>) values.get("groups"));
return true;
});
} else {
try {
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
File trackFile = new File(tracksDir, name + ".yml"); Map<String, Object> values = new HashMap<>();
if (!trackFile.exists()) { values.put("name", track.getName());
try { values.put("groups", track.getGroups());
trackFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return false;
}
Map<String, Object> values = new HashMap<>(); return doWrite(trackFile, values);
values.put("name", track.getName()); }
values.put("groups", track.getGroups()); }, false);
} finally {
if (!doWrite(trackFile, values)) { track.getIoLock().unlock();
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 @Override
public boolean loadTrack(String name) { public boolean loadTrack(String name) {
Track track = plugin.getTrackManager().make(name); Track track = plugin.getTrackManager().getOrMake(name);
List<String> groups = new ArrayList<>(); track.getIoLock().lock();
try {
File trackFile = new File(tracksDir, name + ".yml"); return call(() -> {
if (!trackFile.exists()) { File trackFile = new File(tracksDir, name + ".yml");
return false; return trackFile.exists() && doRead(trackFile, values -> {
track.setGroups((List<String>) values.get("groups"));
return true;
});
}, false);
} finally {
track.getIoLock().unlock();
} }
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 @Override
@ -334,36 +373,63 @@ public class YAMLDatastore extends FlatfileDatastore {
.map(s -> s.substring(0, s.length() - 4)) .map(s -> s.substring(0, s.length() - 4))
.collect(Collectors.toList()); .collect(Collectors.toList());
plugin.getTrackManager().unloadAll();
tracks.forEach(this::loadTrack); tracks.forEach(this::loadTrack);
TrackManager tm = plugin.getTrackManager();
tm.getAll().values().stream()
.filter(t -> !tracks.contains(t.getName()))
.forEach(tm::unload);
return true; return true;
} }
@Override @Override
public boolean saveTrack(Track track) { public boolean saveTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".yml"); track.getIoLock().lock();
if (!trackFile.exists()) { try {
try { return call(() -> {
trackFile.createNewFile(); File trackFile = new File(tracksDir, track.getName() + ".yml");
} catch (IOException e) { if (!trackFile.exists()) {
e.printStackTrace(); try {
return false; trackFile.createNewFile();
} } catch (IOException e) {
} e.printStackTrace();
return false;
}
}
Map<String, Object> values = new HashMap<>(); Map<String, Object> values = new HashMap<>();
values.put("name", track.getName()); values.put("name", track.getName());
values.put("groups", track.getGroups()); values.put("groups", track.getGroups());
return doWrite(trackFile, values); return doWrite(trackFile, values);
}, false);
} finally {
track.getIoLock().unlock();
}
} }
@Override @Override
public boolean deleteTrack(Track track) { public boolean deleteTrack(Track track) {
File trackFile = new File(tracksDir, track.getName() + ".yml"); track.getIoLock().lock();
if (trackFile.exists()) { try {
trackFile.delete(); 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> T call(Callable<T> c, T def) {
try {
return c.call();
} catch (Exception e) {
e.printStackTrace();
return def;
} }
return true;
} }
interface ReadOperation { interface ReadOperation {

View File

@ -34,6 +34,8 @@ import me.lucko.luckperms.utils.Identifiable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ToString @ToString
@EqualsAndHashCode(of = {"name"}) @EqualsAndHashCode(of = {"name"})
@ -51,6 +53,9 @@ public class Track implements Identifiable<String> {
*/ */
private List<String> groups = Collections.synchronizedList(new ArrayList<>()); private List<String> groups = Collections.synchronizedList(new ArrayList<>());
@Getter
private final Lock ioLock = new ReentrantLock();
@Override @Override
public String getId() { public String getId() {
return name; return name;

View File

@ -40,18 +40,13 @@ public class TrackManager extends AbstractManager<String, Track> {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
@Override
public void copy(Track from, Track to) {
to.setGroups(from.getGroups());
}
/** /**
* Makes a new track object * Makes a new track object
* @param name The name of the track * @param name The name of the track
* @return a new {@link Track} object * @return a new {@link Track} object
*/ */
@Override @Override
public Track make(String name) { public Track apply(String name) {
return new Track(name); return new Track(name);
} }
} }

View File

@ -40,7 +40,7 @@ import java.util.UUID;
@ToString(of = {"uuid"}) @ToString(of = {"uuid"})
@EqualsAndHashCode(of = {"uuid"}, callSuper = false) @EqualsAndHashCode(of = {"uuid"}, callSuper = false)
public abstract class User extends PermissionHolder implements Identifiable<UUID> { public abstract class User extends PermissionHolder implements Identifiable<UserIdentifier> {
/** /**
* The users Mojang UUID * The users Mojang UUID
@ -75,8 +75,8 @@ public abstract class User extends PermissionHolder implements Identifiable<UUID
} }
@Override @Override
public UUID getId() { public UserIdentifier getId() {
return uuid; return UserIdentifier.of(uuid, name);
} }
/** /**

View File

@ -0,0 +1,61 @@
/*
* 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.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<UUID> {
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());
}
}

View File

@ -34,7 +34,7 @@ import java.util.NoSuchElementException;
import java.util.UUID; import java.util.UUID;
@RequiredArgsConstructor @RequiredArgsConstructor
public abstract class UserManager extends AbstractManager<UUID, User> { public abstract class UserManager extends AbstractManager<UserIdentifier, User> {
private final LuckPermsPlugin plugin; private final LuckPermsPlugin plugin;
/** /**
@ -53,26 +53,15 @@ public abstract class UserManager extends AbstractManager<UUID, User> {
} }
} }
@Override public User get(UUID uuid) {
public void preSet(User u) { return get(UserIdentifier.of(uuid, null));
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();
} }
/** /**
* Set a user to the default group * Set a user to the default group
* @param user the user to give to * @param user the user to give to
*/ */
public void giveDefaultIfNeeded(User user, boolean save) { public boolean giveDefaultIfNeeded(User user, boolean save) {
boolean hasGroup = false; boolean hasGroup = false;
if (user.getPrimaryGroup() != null && !user.getPrimaryGroup().isEmpty()) { if (user.getPrimaryGroup() != null && !user.getPrimaryGroup().isEmpty()) {
@ -84,18 +73,22 @@ public abstract class UserManager extends AbstractManager<UUID, User> {
} }
} }
if (!hasGroup) { if (hasGroup) {
user.setPrimaryGroup("default"); return false;
try {
user.setPermission("group.default", true);
} catch (ObjectAlreadyHasException ignored) {
ignored.printStackTrace();
}
if (save) {
plugin.getDatastore().saveUser(user, Callback.empty());
}
} }
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) { public boolean shouldSave(User user) {
@ -132,14 +125,6 @@ public abstract class UserManager extends AbstractManager<UUID, User> {
*/ */
public abstract void cleanup(User user); 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 * Reloads the data of all online users
*/ */

View File

@ -26,18 +26,32 @@ import com.google.common.collect.ImmutableMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
/** /**
* An abstract manager class * An abstract manager class
* @param <I> the class used to identify each object held in this manager * @param <I> the class used to identify each object held in this manager
* @param <T> the class this manager is "managing" * @param <T> the class this manager is "managing"
*/ */
public abstract class AbstractManager<I, T extends Identifiable<I>> { public abstract class AbstractManager<I, T extends Identifiable<I>> implements Function<I, T> {
private final Map<I, T> objects = new HashMap<>(); private final Map<I, T> objects = new HashMap<>();
public final Map<I, T> getAll() { public final Map<I, T> getAll() {
Map<I, T> map;
synchronized (objects) { 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<I, T extends Identifiable<I>> {
} }
} }
/**
* 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 * Check to see if a object is loaded or not
* @param id The id of the object * @param id The id of the object
@ -101,9 +83,11 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> {
*/ */
public final void unload(T t) { public final void unload(T t) {
if (t != null) { if (t != null) {
preUnload(t);
synchronized (objects) { synchronized (objects) {
objects.remove(t.getId()); objects.computeIfPresent(t.getId(), (i, t1) -> {
preUnload(t1);
return null;
});
} }
} }
} }
@ -117,15 +101,9 @@ public abstract class AbstractManager<I, T extends Identifiable<I>> {
*/ */
public final void unloadAll() { public final void unloadAll() {
synchronized (objects) { synchronized (objects) {
objects.values().forEach(this::preUnload);
objects.clear(); 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);
} }

View File

@ -28,6 +28,7 @@ import me.lucko.luckperms.api.sponge.LuckPermsService;
import me.lucko.luckperms.api.sponge.LuckPermsUserSubject; import me.lucko.luckperms.api.sponge.LuckPermsUserSubject;
import me.lucko.luckperms.api.sponge.simple.SimpleCollection; import me.lucko.luckperms.api.sponge.simple.SimpleCollection;
import me.lucko.luckperms.users.User; import me.lucko.luckperms.users.User;
import me.lucko.luckperms.users.UserIdentifier;
import me.lucko.luckperms.users.UserManager; import me.lucko.luckperms.users.UserManager;
import org.spongepowered.api.service.context.Context; import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.permission.PermissionService; import org.spongepowered.api.service.permission.PermissionService;
@ -62,7 +63,7 @@ public class UserCollection implements SubjectCollection {
private void load(UUID uuid) { private void load(UUID uuid) {
UUID internal = service.getPlugin().getUuidCache().getUUID(uuid); UUID internal = service.getPlugin().getUuidCache().getUUID(uuid);
if (!manager.isLoaded(internal)) { if (!manager.isLoaded(UserIdentifier.of(uuid, null))) {
return; return;
} }
@ -82,7 +83,7 @@ public class UserCollection implements SubjectCollection {
return users.get(u); return users.get(u);
} }
if (manager.isLoaded(u)) { if (manager.isLoaded(UserIdentifier.of(u, null))) {
load(u); load(u);
return users.get(u); return users.get(u);
} }
@ -108,7 +109,7 @@ public class UserCollection implements SubjectCollection {
public boolean hasRegistered(@NonNull String id) { public boolean hasRegistered(@NonNull String id) {
try { try {
UUID u = UUID.fromString(id); 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) { } catch (IllegalArgumentException e) {
User user = manager.get(id); User user = manager.get(id);
return user != null; return user != null;

View File

@ -46,13 +46,12 @@ public class SpongeUserManager extends UserManager implements ContextListener<Pl
} }
@Override @Override
public User make(UUID uuid) { public User apply(UserIdentifier id) {
return new SpongeUser(uuid, plugin); SpongeUser user = id.getUsername() == null ?
} new SpongeUser(id.getUuid(), plugin) :
new SpongeUser(id.getUuid(), id.getUsername(), plugin);
@Override giveDefaultIfNeeded(user, false);
public User make(UUID uuid, String username) { return user;
return new SpongeUser(uuid, username, plugin);
} }
@Override @Override

View File

@ -24,8 +24,6 @@ package me.lucko.luckperms.users;
import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.LuckPermsPlugin;
import java.util.UUID;
public class StandaloneUserManager extends UserManager { public class StandaloneUserManager extends UserManager {
private final LuckPermsPlugin plugin; private final LuckPermsPlugin plugin;
@ -41,13 +39,12 @@ public class StandaloneUserManager extends UserManager {
} }
@Override @Override
public User make(UUID id) { public User apply(UserIdentifier id) {
return new StandaloneUser(id, plugin); StandaloneUser user = id.getUsername() == null ?
} new StandaloneUser(id.getUuid(), plugin) :
new StandaloneUser(id.getUuid(), id.getUsername(), plugin);
@Override giveDefaultIfNeeded(user, false);
public User make(UUID uuid, String username) { return user;
return new StandaloneUser(uuid, username, plugin);
} }
@Override @Override