From 6aea3b53f38ac6b53a808a905af3c3435752f141 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 28 Aug 2016 21:36:16 +0100 Subject: [PATCH] Only save unique users --- .../me/lucko/luckperms/api/Datastore.java | 39 ++++ .../me/lucko/luckperms/api/LuckPermsApi.java | 6 + .../luckperms/users/BukkitUserManager.java | 2 +- .../me/lucko/luckperms/BungeeListener.java | 2 +- .../luckperms/users/BungeeUserManager.java | 2 +- .../api/implementation/ApiProvider.java | 5 + .../internal/DatastoreLink.java | 65 +++++- .../subcommands/MigrationBungeePerms.java | 2 +- .../subcommands/MigrationGroupManager.java | 2 +- .../subcommands/MigrationPermissionsEx.java | 2 +- .../subcommands/MigrationPowerfulPerms.java | 2 +- .../subcommands/MigrationZPermissions.java | 2 +- .../commands/user/UserMainCommand.java | 61 ++---- .../me/lucko/luckperms/constants/Message.java | 1 - .../me/lucko/luckperms/storage/Datastore.java | 31 ++- .../storage/methods/FlatfileDatastore.java | 189 +++++++++--------- .../storage/methods/MongoDBDatastore.java | 57 ++++-- .../storage/methods/SQLDatastore.java | 152 ++++++++------ .../me/lucko/luckperms/users/UserManager.java | 28 +++ .../luckperms/utils/AbstractListener.java | 2 +- .../luckperms/users/SpongeUserManager.java | 2 +- 21 files changed, 412 insertions(+), 242 deletions(-) diff --git a/api/src/main/java/me/lucko/luckperms/api/Datastore.java b/api/src/main/java/me/lucko/luckperms/api/Datastore.java index c45b3d79..d43d111d 100644 --- a/api/src/main/java/me/lucko/luckperms/api/Datastore.java +++ b/api/src/main/java/me/lucko/luckperms/api/Datastore.java @@ -24,6 +24,7 @@ package me.lucko.luckperms.api; import me.lucko.luckperms.api.data.Callback; +import java.util.Set; import java.util.UUID; /** @@ -90,7 +91,9 @@ public interface Datastore { * @return true if the operation completed successfully. * @throws NullPointerException if uuid or username is null * @throws IllegalArgumentException if either of the parameters are invalid + * @deprecated functionality of this method is taken on by {@link #loadUser(UUID, String)} */ + @Deprecated boolean loadOrCreateUser(UUID uuid, String username); /** @@ -98,9 +101,21 @@ public interface Datastore { * @param uuid the uuid of the user to load * @return true if the user exists, and was loaded correctly. * @throws NullPointerException if uuid is null + * @deprecated replaced by {@link #loadUser(UUID, String)} */ + @Deprecated boolean loadUser(UUID uuid); + /** + * Loads a user's data into the plugins internal storage. + * @param uuid the uuid of the user to load + * @param username the users username. (if you want to specify null here, just input "null" as a string.) + * @return if the operation was performed successfully + * @throws NullPointerException if uuid or username is null + * @since 1.6 + */ + boolean loadUser(UUID uuid, String username); + /** * Saves a user object into the datastore. You should call this after you make any changes to a user. * @param user the user to save @@ -110,6 +125,20 @@ public interface Datastore { */ boolean saveUser(User user); + /** + * Removes users from the datastore who are "default". This is called every time the plugin loads. + * @return true if the operation completed successfully + * @since 1.6 + */ + boolean cleanupUsers(); + + /** + * Gets a set user's UUIDs who are "unique", aren't just a member of the "default" group. + * @return a set of uuids, or null if the operation failed. + * @since 1.6 + */ + Set getUniqueUsers(); + /** * Creates and loads a group into the plugins internal storage * @param name the name of the group @@ -223,9 +252,14 @@ public interface Datastore { interface Async { void logAction(LogEntry entry, Callback callback); void getLog(Callback callback); + @Deprecated void loadOrCreateUser(UUID uuid, String username, Callback callback); + @Deprecated void loadUser(UUID uuid, Callback callback); + void loadUser(UUID uuid, String username, Callback callback); void saveUser(User user, Callback callback); + void cleanupUsers(Callback callback); + void getUniqueUsers(Callback> callback); void createAndLoadGroup(String name, Callback callback); void loadGroup(String name, Callback callback); void loadAllGroups(Callback callback); @@ -250,9 +284,14 @@ public interface Datastore { interface Future { java.util.concurrent.Future logAction(LogEntry entry); java.util.concurrent.Future getLog(); + @Deprecated java.util.concurrent.Future loadOrCreateUser(UUID uuid, String username); + @Deprecated java.util.concurrent.Future loadUser(UUID uuid); + java.util.concurrent.Future loadUser(UUID uuid, String username); java.util.concurrent.Future saveUser(User user); + java.util.concurrent.Future cleanupUsers(); + java.util.concurrent.Future> getUniqueUsers(); java.util.concurrent.Future createAndLoadGroup(String name); java.util.concurrent.Future loadGroup(String name); java.util.concurrent.Future loadAllGroups(); diff --git a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java index ee8533d4..33dc40b2 100644 --- a/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java +++ b/api/src/main/java/me/lucko/luckperms/api/LuckPermsApi.java @@ -39,6 +39,12 @@ public interface LuckPermsApi { */ void runUpdateTask(); + /** + * @return the version of the API running on the platform + * @since 1.6 + */ + double getApiVersion(); + /** * @return the version of the plugin running on the platform */ 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 e2d19f92..95546bba 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java +++ b/bukkit/src/main/java/me/lucko/luckperms/users/BukkitUserManager.java @@ -88,7 +88,7 @@ public class BukkitUserManager extends UserManager { Set players = plugin.getServer().getOnlinePlayers().stream() .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) .collect(Collectors.toSet()); - plugin.doAsync(() -> players.forEach(u -> plugin.getDatastore().loadUser(u))); + plugin.doAsync(() -> players.forEach(u -> plugin.getDatastore().loadUser(u, "null"))); }); } } diff --git a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java index f0ea870e..c796d123 100644 --- a/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java +++ b/bungee/src/main/java/me/lucko/luckperms/BungeeListener.java @@ -97,7 +97,7 @@ public class BungeeListener extends AbstractListener implements Listener { // We have to make a new user on this thread whilst the connection is being held, or we get concurrency issues as the Bukkit server // and the BungeeCord server try to make a new user at the same time. - plugin.getDatastore().loadOrCreateUser(cache.getUUID(c.getUniqueId()), c.getName()); + plugin.getDatastore().loadUser(cache.getUUID(c.getUniqueId()), c.getName()); final long time = System.currentTimeMillis() - startTime; if (time >= 1000) { plugin.getLog().warn("Processing login for " + c.getName() + " took " + time + "ms."); 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 8ff79af7..bb6f4ce7 100644 --- a/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java +++ b/bungee/src/main/java/me/lucko/luckperms/users/BungeeUserManager.java @@ -64,6 +64,6 @@ public class BungeeUserManager extends UserManager { public void updateAllUsers() { plugin.getProxy().getPlayers().stream() .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) - .forEach(u -> plugin.getDatastore().loadUser(u)); + .forEach(u -> plugin.getDatastore().loadUser(u, "null")); } } diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java index 8a46df6d..93e4d908 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/ApiProvider.java @@ -66,6 +66,11 @@ public class ApiProvider implements LuckPermsApi { plugin.runUpdateTask(); } + @Override + public double getApiVersion() { + return 1.6; + } + @Override public String getVersion() { return plugin.getVersion(); diff --git a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java index 84265862..3687c488 100644 --- a/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java +++ b/common/src/main/java/me/lucko/luckperms/api/implementation/internal/DatastoreLink.java @@ -30,8 +30,10 @@ import me.lucko.luckperms.LuckPermsPlugin; import me.lucko.luckperms.api.*; import me.lucko.luckperms.api.data.Callback; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -106,12 +108,17 @@ public class DatastoreLink implements Datastore { @Override public void loadOrCreateUser(@NonNull UUID uuid, @NonNull String username, Callback callback) { - master.loadOrCreateUser(uuid, checkUsername(username), checkCallback(callback)); + master.loadUser(uuid, checkUsername(username), checkCallback(callback)); } @Override public void loadUser(@NonNull UUID uuid, Callback callback) { - master.loadUser(uuid, checkCallback(callback)); + master.loadUser(uuid, "null", checkCallback(callback)); + } + + @Override + public void loadUser(@NonNull UUID uuid, @NonNull String username, Callback callback) { + master.loadUser(uuid, checkUsername(username), checkCallback(callback)); } @Override @@ -120,6 +127,16 @@ public class DatastoreLink implements Datastore { master.saveUser(((UserLink) user).getMaster(), checkCallback(callback)); } + @Override + public void cleanupUsers(Callback callback) { + master.cleanupUsers(checkCallback(callback)); + } + + @Override + public void getUniqueUsers(Callback> callback) { + master.getUniqueUsers(checkCallback(callback)); + } + @Override public void createAndLoadGroup(@NonNull String name, Callback callback) { master.createAndLoadGroup(checkName(name), checkCallback(callback)); @@ -204,12 +221,17 @@ public class DatastoreLink implements Datastore { @Override public boolean loadOrCreateUser(@NonNull UUID uuid, @NonNull String username) { - return master.loadOrCreateUser(uuid, checkUsername(username)); + return master.loadUser(uuid, checkUsername(username)); } @Override public boolean loadUser(@NonNull UUID uuid) { - return master.loadUser(uuid); + return master.loadUser(uuid, "null"); + } + + @Override + public boolean loadUser(@NonNull UUID uuid, @NonNull String username) { + return master.loadUser(uuid, checkUsername(username)); } @Override @@ -218,6 +240,16 @@ public class DatastoreLink implements Datastore { return master.saveUser(((UserLink) user).getMaster()); } + @Override + public boolean cleanupUsers() { + return master.cleanupUsers(); + } + + @Override + public Set getUniqueUsers() { + return master.getUniqueUsers(); + } + @Override public boolean createAndLoadGroup(@NonNull String name) { return master.createAndLoadGroup(checkName(name)); @@ -307,14 +339,21 @@ public class DatastoreLink implements Datastore { @Override public java.util.concurrent.Future loadOrCreateUser(@NonNull UUID uuid, @NonNull String username) { LPFuture lpf = new LPFuture<>(); - master.loadOrCreateUser(uuid, checkUsername(username), lpf); + master.loadUser(uuid, checkUsername(username), lpf); return lpf; } @Override public java.util.concurrent.Future loadUser(@NonNull UUID uuid) { LPFuture lpf = new LPFuture<>(); - master.loadUser(uuid, lpf); + master.loadUser(uuid, "null", lpf); + return lpf; + } + + @Override + public java.util.concurrent.Future loadUser(@NonNull UUID uuid, @NonNull String username) { + LPFuture lpf = new LPFuture<>(); + master.loadUser(uuid, checkUsername(username), lpf); return lpf; } @@ -326,6 +365,20 @@ public class DatastoreLink implements Datastore { return lpf; } + @Override + public java.util.concurrent.Future cleanupUsers() { + LPFuture lpf = new LPFuture<>(); + master.cleanupUsers(lpf); + return lpf; + } + + @Override + public java.util.concurrent.Future> getUniqueUsers() { + LPFuture> lpf = new LPFuture<>(); + master.getUniqueUsers(lpf); + return lpf; + } + @Override public java.util.concurrent.Future createAndLoadGroup(@NonNull String name) { LPFuture lpf = new LPFuture<>(); diff --git a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationBungeePerms.java b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationBungeePerms.java index 9cc38bf6..e996179a 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationBungeePerms.java +++ b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationBungeePerms.java @@ -188,7 +188,7 @@ public class MigrationBungeePerms extends SubCommand { userCount++; // Make a LuckPerms user for the one being migrated. - plugin.getDatastore().loadOrCreateUser(u.getUUID(), "null"); + plugin.getDatastore().loadUser(u.getUUID(), "null"); me.lucko.luckperms.users.User user = plugin.getUserManager().get(u.getUUID()); // Migrate global perms diff --git a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationGroupManager.java b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationGroupManager.java index b69d3b06..7164a1b4 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationGroupManager.java +++ b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationGroupManager.java @@ -246,7 +246,7 @@ public class MigrationGroupManager extends SubCommand { } for (Map.Entry, Boolean>> e : users.entrySet()) { - plugin.getDatastore().loadOrCreateUser(e.getKey(), "null"); + plugin.getDatastore().loadUser(e.getKey(), "null"); me.lucko.luckperms.users.User user = plugin.getUserManager().get(e.getKey()); for (Map.Entry, Boolean> n : e.getValue().entrySet()) { diff --git a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPermissionsEx.java b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPermissionsEx.java index 544f4aed..39cef37b 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPermissionsEx.java +++ b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPermissionsEx.java @@ -286,7 +286,7 @@ public class MigrationPermissionsEx extends SubCommand { } userCount++; - plugin.getDatastore().loadOrCreateUser(u, "null"); + plugin.getDatastore().loadUser(u, "null"); User lpUser = plugin.getUserManager().get(u); try { diff --git a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPowerfulPerms.java b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPowerfulPerms.java index 35d2059d..70aaebe5 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPowerfulPerms.java +++ b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationPowerfulPerms.java @@ -212,7 +212,7 @@ public class MigrationPowerfulPerms extends SubCommand { progress.put(uuid, new CountDownLatch(2)); // Create a LuckPerms user for the UUID - plugin.getDatastore().loadOrCreateUser(uuid, "null"); + plugin.getDatastore().loadUser(uuid, "null"); User user = plugin.getUserManager().get(uuid); // Get a list of Permissions held by the user from the PP API. diff --git a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationZPermissions.java b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationZPermissions.java index fcd9ddb8..72eeb060 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationZPermissions.java +++ b/common/src/main/java/me/lucko/luckperms/commands/migration/subcommands/MigrationZPermissions.java @@ -152,7 +152,7 @@ public class MigrationZPermissions extends SubCommand { // Migrate all users. log.info("zPermissions Migration: Starting user migration."); for (UUID u : service.getAllPlayersUUID()) { - plugin.getDatastore().loadOrCreateUser(u, "null"); + plugin.getDatastore().loadUser(u, "null"); User user = plugin.getUserManager().get(u); for (Map.Entry e : service.getPlayerPermissions(null, null, u).entrySet()) { diff --git a/common/src/main/java/me/lucko/luckperms/commands/user/UserMainCommand.java b/common/src/main/java/me/lucko/luckperms/commands/user/UserMainCommand.java index 406c2182..5c279086 100644 --- a/common/src/main/java/me/lucko/luckperms/commands/user/UserMainCommand.java +++ b/common/src/main/java/me/lucko/luckperms/commands/user/UserMainCommand.java @@ -65,64 +65,43 @@ public class UserMainCommand extends MainCommand { @Override protected User getTarget(String target, LuckPermsPlugin plugin, Sender sender) { UUID u = Util.parseUuid(target); - if (u != null) { - User user = getUser(plugin, u); - if (user == null) { - - Message.USER_NEVER_JOINED.send(sender); - if (!plugin.getDatastore().loadOrCreateUser(u, "null")) { - Message.USER_CREATE_FAIL.send(sender); + if (u == null) { + if (target.length() <= 16) { + if (Patterns.NON_USERNAME.matcher(target).find()) { + Message.USER_INVALID_ENTRY.send(sender, target); return null; } - user = getUser(plugin, u); - } - return user; - } + Message.USER_ATTEMPTING_LOOKUP.send(sender); - if (target.length() <= 16) { - if (Patterns.NON_USERNAME.matcher(target).find()) { + u = plugin.getDatastore().getUUID(target); + if (u == null) { + Message.USER_NOT_FOUND.send(sender); + return null; + } + } else { Message.USER_INVALID_ENTRY.send(sender, target); - return null; } - - Message.USER_ATTEMPTING_LOOKUP.send(sender); - - UUID uuid = plugin.getDatastore().getUUID(target); - if (uuid == null) { - Message.USER_NOT_FOUND.send(sender); - return null; - } - - User user = getUser(plugin, uuid); - if (user == null) { - Message.USER_NOT_FOUND.send(sender); - } - return user; } - Message.USER_INVALID_ENTRY.send(sender, target); - return null; - } - - @Override - protected void cleanup(User user, LuckPermsPlugin plugin) { - plugin.getUserManager().cleanup(user); - } - - private User getUser(LuckPermsPlugin plugin, UUID uuid) { - if (!plugin.getDatastore().loadUser(uuid)) { - return null; + if (!plugin.getDatastore().loadUser(u, "null")) { + Message.LOADING_ERROR.send(sender); } - User user = plugin.getUserManager().get(uuid); + User user = plugin.getUserManager().get(u); if (user == null) { + Message.LOADING_ERROR.send(sender); return null; } return user; } + @Override + protected void cleanup(User user, LuckPermsPlugin plugin) { + plugin.getUserManager().cleanup(user); + } + @Override protected List getObjects(LuckPermsPlugin plugin) { return plugin.getPlayerList(); diff --git a/common/src/main/java/me/lucko/luckperms/constants/Message.java b/common/src/main/java/me/lucko/luckperms/constants/Message.java index 8a6fe3b8..f867abdf 100644 --- a/common/src/main/java/me/lucko/luckperms/constants/Message.java +++ b/common/src/main/java/me/lucko/luckperms/constants/Message.java @@ -60,7 +60,6 @@ public enum Message { USER_SAVE_SUCCESS("&7(User data was saved to the datastore)", true), USER_SAVE_ERROR("There was an error whilst saving the user.", true), USER_ATTEMPTING_LOOKUP("&7(Attempting UUID lookup, since you specified a username)", true), - USER_NEVER_JOINED("&6(&e&lWARNING: &cA user with that UUID has not joined the server before.&6)", true), USER_CREATE_FAIL("There was an error whilst creating a new user.", true), GROUP_NOT_FOUND("&eGroup could not be found.", true), diff --git a/common/src/main/java/me/lucko/luckperms/storage/Datastore.java b/common/src/main/java/me/lucko/luckperms/storage/Datastore.java index f3db32b9..03564ae2 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/Datastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/Datastore.java @@ -34,6 +34,7 @@ import me.lucko.luckperms.groups.Group; import me.lucko.luckperms.tracks.Track; import me.lucko.luckperms.users.User; +import java.util.Set; import java.util.UUID; @RequiredArgsConstructor(access = AccessLevel.PROTECTED) @@ -70,9 +71,10 @@ public abstract class Datastore { public abstract void shutdown(); public abstract boolean logAction(LogEntry entry); public abstract Log getLog(); - public abstract boolean loadOrCreateUser(UUID uuid, String username); - public abstract boolean loadUser(UUID uuid); + public abstract boolean loadUser(UUID uuid, String username); public abstract boolean saveUser(User user); + public abstract boolean cleanupUsers(); + public abstract Set getUniqueUsers(); public abstract boolean createAndLoadGroup(String name); public abstract boolean loadGroup(String name); public abstract boolean loadAllGroups(); @@ -106,16 +108,9 @@ public abstract class Datastore { }); } - public void loadOrCreateUser(UUID uuid, String username, Callback callback) { + public void loadUser(UUID uuid, String username, Callback callback) { doAsync(() -> { - boolean result = loadOrCreateUser(uuid, username); - doSync(() -> callback.onComplete(result)); - }); - } - - public void loadUser(UUID uuid, Callback callback) { - doAsync(() -> { - boolean result = loadUser(uuid); + boolean result = loadUser(uuid, username); doSync(() -> callback.onComplete(result)); }); } @@ -127,6 +122,20 @@ public abstract class Datastore { }); } + public void cleanupUsers(Callback callback) { + doAsync(() -> { + boolean result = cleanupUsers(); + doSync(() -> callback.onComplete(result)); + }); + } + + public void getUniqueUsers(Callback> callback) { + doAsync(() -> { + Set result = getUniqueUsers(); + doSync(() -> callback.onComplete(result)); + }); + } + public void createAndLoadGroup(String name, Callback callback) { doAsync(() -> { boolean result = createAndLoadGroup(name); diff --git a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java index 7fc647f4..821c786a 100644 --- a/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java +++ b/common/src/main/java/me/lucko/luckperms/storage/methods/FlatfileDatastore.java @@ -118,6 +118,8 @@ public class FlatfileDatastore extends Datastore { e.printStackTrace(); } + cleanupUsers(); + setAcceptingLogins(true); } @@ -166,108 +168,58 @@ public class FlatfileDatastore extends Datastore { } @Override - public boolean loadOrCreateUser(UUID uuid, String username) { + 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()) { - try { - userFile.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - - boolean success = doWrite(userFile, writer -> { - writer.beginObject(); - writer.name("uuid").value(user.getUuid().toString()); - writer.name("name").value(user.getName()); - writer.name("primaryGroup").value(user.getPrimaryGroup()); - writer.name("perms"); - writer.beginObject(); - for (Map.Entry e : exportToLegacy(user.getNodes()).entrySet()) { - writer.name(e.getKey()).value(e.getValue().booleanValue()); + 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)); } - writer.endObject(); - writer.endObject(); + + reader.endObject(); + reader.endObject(); return true; }); - if (!success) return false; - } - - final String[] name = new String[1]; - boolean 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 (!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()); + if (user.getName().equalsIgnoreCase("null")) { + user.setName(name[0]); + } else { + if (!name[0].equals(user.getName())) { + doWrite(userFile, writer -> { + writer.beginObject(); + writer.name("uuid").value(user.getUuid().toString()); + writer.name("name").value(user.getName()); + writer.name("primaryGroup").value(user.getPrimaryGroup()); + writer.name("perms"); + writer.beginObject(); + for (Map.Entry e : exportToLegacy(user.getNodes()).entrySet()) { + writer.name(e.getKey()).value(e.getValue().booleanValue()); + } + writer.endObject(); + writer.endObject(); + return true; + }); } - writer.endObject(); - writer.endObject(); - return true; - }); - } - - if (success) plugin.getUserManager().updateOrSet(user); - return success; - } - - @Override - public boolean loadUser(UUID uuid) { - User user = plugin.getUserManager().make(uuid); - - File userFile = new File(usersDir, uuid.toString() + ".json"); - if (!userFile.exists()) { - return false; - } - - boolean success = doRead(userFile, reader -> { - reader.beginObject(); - reader.nextName(); // uuid record - reader.nextString(); // uuid - reader.nextName(); // name record - user.setName(reader.nextString()); // name - reader.nextName(); // primaryGroup record - user.setPrimaryGroup(reader.nextString()); // primaryGroup - reader.nextName(); // perms record - 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; - }); + } else { + success = true; + } if (success) plugin.getUserManager().updateOrSet(user); return success; @@ -302,6 +254,59 @@ public class FlatfileDatastore extends Datastore { return success; } + @Override + public boolean cleanupUsers() { + File[] files = usersDir.listFiles((dir, name) -> name.endsWith(".json")); + if (files == null) return false; + + for (File file : files) { + Map nodes = new HashMap<>(); + doRead(file, reader -> { + reader.beginObject(); + reader.nextName(); // uuid record + reader.nextString(); // uuid + reader.nextName(); // name record + reader.nextString(); // name + reader.nextName(); // primaryGroup record + reader.nextString(); // primaryGroup + reader.nextName(); //perms + reader.beginObject(); + while (reader.hasNext()) { + String node = reader.nextName(); + boolean b = reader.nextBoolean(); + nodes.put(node, b); + } + + reader.endObject(); + reader.endObject(); + return true; + }); + + boolean shouldDelete = false; + if (nodes.size() == 1) { + for (Map.Entry e : nodes.entrySet()) { + // There's only one + shouldDelete = e.getKey().equalsIgnoreCase("group.default") && e.getValue(); + } + } + + if (shouldDelete) { + file.delete(); + } + } + return true; + } + + @Override + public Set getUniqueUsers() { + String[] fileNames = usersDir.list((dir, name) -> name.endsWith(".json")); + if (fileNames == null) return null; + return Arrays.stream(fileNames) + .map(s -> s.substring(0, s.length() - 5)) + .map(UUID::fromString) + .collect(Collectors.toSet()); + } + @Override public boolean createAndLoadGroup(String name) { Group group = plugin.getGroupManager().make(name); 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 3f6f6023..285b6536 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 @@ -139,21 +139,23 @@ public class MongoDBDatastore extends Datastore { } @Override - public boolean loadOrCreateUser(UUID uuid, String username) { + public boolean loadUser(UUID uuid, String username) { User user = plugin.getUserManager().make(uuid, username); boolean success = 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 { + if (cursor.hasNext()) { Document d = cursor.next(); user.setPrimaryGroup(d.getString("primaryGroup")); user.setNodes(revert((Map) d.get("perms"))); - if (!d.getString("name").equals(user.getName())) { - c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + if (user.getName().equalsIgnoreCase("null")) { + user.setName(d.getString("name")); + } else { + if (!d.getString("name").equals(user.getName())) { + c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + } } } } @@ -165,34 +167,47 @@ public class MongoDBDatastore extends Datastore { } @Override - public boolean loadUser(UUID uuid) { - User user = plugin.getUserManager().make(uuid); + public boolean saveUser(User user) { + if (!plugin.getUserManager().shouldSave(user)) { + return true; + } + boolean success = 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.setName(d.getString("name")); - user.setPrimaryGroup(d.getString("primaryGroup")); - user.setNodes(revert((Map) d.get("perms"))); - return true; + if (!cursor.hasNext()) { + c.insertOne(fromUser(user)); + } else { + c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); } - return false; } + return true; }, false); - - if (success) plugin.getUserManager().updateOrSet(user); return success; } @Override - public boolean saveUser(User user) { - return call(() -> { + public boolean cleanupUsers() { + return true; // TODO + } + + @Override + public Set getUniqueUsers() { + Set uuids = new HashSet<>(); + boolean success = call(() -> { MongoCollection c = database.getCollection("users"); - c.replaceOne(new Document("_id", user.getUuid()), fromUser(user)); + + try (MongoCursor cursor = c.find().iterator()) { + while (cursor.hasNext()) { + Document d = cursor.next(); + uuids.add(UUID.fromString(d.getString("_id"))); + } + } + return true; }, false); + + return success ? uuids : null; } @Override 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 a84714b6..1f7aa5fd 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 @@ -41,10 +41,7 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import static me.lucko.luckperms.core.PermissionHolder.exportToLegacy; @@ -56,7 +53,9 @@ abstract class SQLDatastore extends Datastore { private static final String USER_INSERT = "INSERT INTO lp_users VALUES(?, ?, ?, ?)"; private static final String USER_SELECT = "SELECT * FROM lp_users WHERE uuid=?"; + private static final String USER_SELECT_ALL = "SELECT uuid FROM lp_users"; private static final String USER_UPDATE = "UPDATE lp_users SET name=?, primary_group = ?, perms=? WHERE uuid=?"; + private static final String USER_DELETE = "DELETE FROM lp_users WHERE perms=?"; private static final String GROUP_INSERT = "INSERT INTO lp_groups VALUES(?, ?)"; private static final String GROUP_SELECT = "SELECT perms FROM lp_groups WHERE name=?"; @@ -95,33 +94,7 @@ abstract class SQLDatastore extends Datastore { if (!runQuery(new Query(q))) success = false; } - return success; - } - - @Override - public boolean loadUser(UUID uuid) { - User user = plugin.getUserManager().make(uuid); - boolean success = runQuery(new QueryRS(USER_SELECT) { - @Override - void onRun(PreparedStatement preparedStatement) throws SQLException { - preparedStatement.setString(1, uuid.toString()); - } - - @Override - boolean onResult(ResultSet resultSet) throws SQLException { - if (resultSet.next()) { - user.setName(resultSet.getString("name")); - Map nodes = gson.fromJson(resultSet.getString("perms"), NM_TYPE); - user.setNodes(nodes); - user.setPrimaryGroup(resultSet.getString("primary_group")); - return true; - } - return false; - } - }); - - if (success) plugin.getUserManager().updateOrSet(user); - return success; + return success && cleanupUsers(); } @Override @@ -172,7 +145,7 @@ abstract class SQLDatastore extends Datastore { } @Override - public boolean loadOrCreateUser(UUID uuid, String username) { + public boolean loadUser(UUID uuid, String username) { User user = plugin.getUserManager().make(uuid, username); boolean success = runQuery(new QueryRS(USER_SELECT) { @Override @@ -182,35 +155,29 @@ abstract class SQLDatastore extends Datastore { @Override boolean onResult(ResultSet resultSet) throws SQLException { - boolean success = true; - if (!resultSet.next()) { - success = runQuery(new QueryPS(USER_INSERT) { - @Override - void onRun(PreparedStatement preparedStatement) throws SQLException { - 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 { + 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 (!resultSet.getString("name").equals(user.getName())) { - runQuery(new QueryPS(USER_UPDATE) { - @Override - void onRun(PreparedStatement preparedStatement) throws SQLException { - preparedStatement.setString(1, user.getName()); - preparedStatement.setString(2, user.getPrimaryGroup()); - preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); - preparedStatement.setString(4, user.getUuid().toString()); - } - }); + if (user.getName().equalsIgnoreCase("null")) { + user.setName(resultSet.getString("name")); + } else { + if (!resultSet.getString("name").equals(user.getName())) { + runQuery(new QueryPS(USER_UPDATE) { + @Override + void onRun(PreparedStatement preparedStatement) throws SQLException { + preparedStatement.setString(1, user.getName()); + preparedStatement.setString(2, user.getPrimaryGroup()); + preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); + preparedStatement.setString(4, user.getUuid().toString()); + } + }); + } } } - return success; + return true; } }); @@ -220,18 +187,83 @@ abstract class SQLDatastore extends Datastore { @Override public boolean saveUser(User user) { - boolean success = runQuery(new QueryPS(USER_UPDATE) { + if (!plugin.getUserManager().shouldSave(user)) { + return true; + } + + boolean success = runQuery(new QueryRS(USER_SELECT) { @Override void onRun(PreparedStatement preparedStatement) throws SQLException { - preparedStatement.setString(1, user.getName()); - preparedStatement.setString(2, user.getPrimaryGroup()); - preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); - preparedStatement.setString(4, user.getUuid().toString()); + preparedStatement.setString(1, user.getUuid().toString()); + } + + @Override + boolean onResult(ResultSet resultSet) throws SQLException { + boolean b; + if (!resultSet.next()) { + // Doesn't already exist, let's insert. + b = runQuery(new QueryPS(USER_INSERT) { + @Override + void onRun(PreparedStatement preparedStatement) throws SQLException { + 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. + b = runQuery(new QueryPS(USER_UPDATE) { + @Override + void onRun(PreparedStatement preparedStatement) throws SQLException { + preparedStatement.setString(1, user.getName()); + preparedStatement.setString(2, user.getPrimaryGroup()); + preparedStatement.setString(3, gson.toJson(exportToLegacy(user.getNodes()))); + preparedStatement.setString(4, user.getUuid().toString()); + } + }); + } + return b; + } + }); + + return success; + } + + @Override + public boolean cleanupUsers() { + boolean success = runQuery(new QueryPS(USER_DELETE) { + @Override + void onRun(PreparedStatement preparedStatement) throws SQLException { + preparedStatement.setString(1, "{\"group.default\":true}"); } }); return success; } + @Override + public Set getUniqueUsers() { + Set uuids = new HashSet<>(); + + boolean success = runQuery(new QueryRS(USER_SELECT_ALL) { + @Override + void onRun(PreparedStatement preparedStatement) throws SQLException { + } + + @Override + boolean onResult(ResultSet resultSet) throws SQLException { + while (resultSet.next()) { + String uuid = resultSet.getString("uuid"); + uuids.add(UUID.fromString(uuid)); + } + return false; + } + }); + + return success ? uuids : null; + } + @Override public boolean createAndLoadGroup(String name) { Group group = plugin.getGroupManager().make(name); 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 56bd5834..ee257893 100644 --- a/common/src/main/java/me/lucko/luckperms/users/UserManager.java +++ b/common/src/main/java/me/lucko/luckperms/users/UserManager.java @@ -93,6 +93,34 @@ public abstract class UserManager extends AbstractManager { } } + public boolean shouldSave(User user) { + if (user.getNodes().size() != 1) { + return true; + } + + for (Node node : user.getNodes()) { + // There's only one. + if (!node.isGroupNode()) { + return true; + } + + if (node.isTemporary() || node.isServerSpecific() || node.isWorldSpecific()) { + return true; + } + + if (!node.getGroupName().equalsIgnoreCase("default")) { + // The user's only node is not the default group one. + return true; + } + } + + if (!user.getPrimaryGroup().equalsIgnoreCase("default")) { + return true; // Not in the default primary group + } + + return false; + } + /** * Checks to see if the user is online, and if they are not, runs {@link #unload(Identifiable)} * @param user The user to be cleaned up diff --git a/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java b/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java index 188ddb02..397be9c3 100644 --- a/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java +++ b/common/src/main/java/me/lucko/luckperms/utils/AbstractListener.java @@ -59,7 +59,7 @@ public class AbstractListener { plugin.getDatastore().saveUUIDData(username, u, Callback.empty()); } - plugin.getDatastore().loadOrCreateUser(cache.getUUID(u), username); + plugin.getDatastore().loadUser(cache.getUUID(u), username); final long time = System.currentTimeMillis() - startTime; if (time >= 1000) { plugin.getLog().warn("Processing login for " + username + " took " + time + "ms."); 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 db93b983..227be20f 100644 --- a/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/users/SpongeUserManager.java @@ -62,6 +62,6 @@ public class SpongeUserManager extends UserManager { public void updateAllUsers() { plugin.getGame().getServer().getOnlinePlayers().stream() .map(p -> plugin.getUuidCache().getUUID(p.getUniqueId())) - .forEach(u -> plugin.getDatastore().loadUser(u)); + .forEach(u -> plugin.getDatastore().loadUser(u, "null")); } }