Refactoring storage - WIP. Towards #32

This commit is contained in:
Luck 2016-10-20 21:55:34 +01:00
parent 0d515abadb
commit 37fc00cce8
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
55 changed files with 1470 additions and 340 deletions

View File

@ -49,6 +49,7 @@ import me.lucko.luckperms.common.storage.Datastore;
import me.lucko.luckperms.common.storage.StorageFactory;
import me.lucko.luckperms.common.tracks.TrackManager;
import me.lucko.luckperms.common.users.UserManager;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.LocaleManager;
import me.lucko.luckperms.common.utils.LogFactory;
import org.bukkit.World;
@ -84,6 +85,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
private ContextManager<Player> contextManager;
private WorldCalculator worldCalculator;
private CalculatorFactory calculatorFactory;
private BufferedRequest<Void> updateTaskBuffer;
@Override
public void onEnable() {
@ -139,10 +141,19 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
contextManager.registerListener(new AutoOPListener());
}
final LPBukkitPlugin i = this;
updateTaskBuffer = new BufferedRequest<Void>(5000L, this::doAsync) {
@Override
protected Void perform() {
getServer().getScheduler().runTaskAsynchronously(i, new UpdateTask(i));
return null;
}
};
int mins = getConfiguration().getSyncTime();
if (mins > 0) {
long ticks = mins * 60 * 20;
getServer().getScheduler().runTaskTimerAsynchronously(this, new UpdateTask(this), ticks, ticks);
getServer().getScheduler().runTaskTimerAsynchronously(this, () -> updateTaskBuffer.request(), ticks, ticks);
}
getServer().getScheduler().runTaskTimer(this, BukkitSenderFactory.get(this), 1L, 1L);
@ -188,7 +199,7 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
@Override
public void onDisable() {
getLog().info("Closing datastore...");
datastore.shutdown();
datastore.shutdown().getOrDefault(null);
getLog().info("Unregistering API...");
ApiHandler.unregisterProvider();
@ -209,6 +220,11 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
getServer().getScheduler().runTask(this, r);
}
@Override
public void doAsyncRepeating(Runnable r, long interval) {
getServer().getScheduler().runTaskTimerAsynchronously(this, r, interval, interval);
}
@Override
public String getVersion() {
return getDescription().getVersion();
@ -347,11 +363,6 @@ public class LPBukkitPlugin extends JavaPlugin implements LuckPermsPlugin {
return getServer().getPluginManager().isPluginEnabled(name);
}
@Override
public void runUpdateTask() {
getServer().getScheduler().runTaskAsynchronously(this, new UpdateTask(this));
}
private void registerPermissions(PermissionDefault def) {
PluginManager pm = getServer().getPluginManager();

View File

@ -95,7 +95,7 @@ public class BungeeListener extends AbstractListener implements Listener {
final PendingConnection c = e.getConnection();
if (!cache.isOnlineMode()) {
UUID uuid = plugin.getDatastore().getUUID(c.getName());
UUID uuid = plugin.getDatastore().getUUID(c.getName()).getOrDefault(null);
if (uuid != null) {
cache.addToCache(c.getUniqueId(), uuid);
} else {
@ -105,7 +105,7 @@ public class BungeeListener extends AbstractListener implements Listener {
plugin.getDatastore().saveUUIDData(c.getName(), c.getUniqueId());
}
} else {
UUID uuid = plugin.getDatastore().getUUID(c.getName());
UUID uuid = plugin.getDatastore().getUUID(c.getName()).getOrDefault(null);
if (uuid == null) {
plugin.getApiProvider().fireEventAsync(new UserFirstLoginEvent(c.getUniqueId(), c.getName()));
}

View File

@ -268,4 +268,10 @@ public class LPBungeePlugin extends Plugin implements LuckPermsPlugin {
public void doSync(Runnable r) {
r.run();
}
@Override
public void doAsyncRepeating(Runnable r, long interval) {
long millis = interval * 50L; // convert from ticks to milliseconds
getProxy().getScheduler().schedule(this, r, millis, millis, TimeUnit.MILLISECONDS);
}
}

View File

@ -38,6 +38,7 @@ import me.lucko.luckperms.common.groups.GroupManager;
import me.lucko.luckperms.common.storage.Datastore;
import me.lucko.luckperms.common.tracks.TrackManager;
import me.lucko.luckperms.common.users.UserManager;
import me.lucko.luckperms.common.utils.BufferedRequest;
import me.lucko.luckperms.common.utils.LocaleManager;
import java.io.File;
@ -177,7 +178,7 @@ public interface LuckPermsPlugin {
/**
* Runs an update task
*/
void runUpdateTask();
BufferedRequest<Void> getUpdateTaskBuffer();
/**
* Execute a runnable asynchronously
@ -191,4 +192,11 @@ public interface LuckPermsPlugin {
*/
void doSync(Runnable r);
/**
* Execute a runnable asynchronously on a loop
* @param r the task to run
* @param interval the time between runs in ticks
*/
void doAsyncRepeating(Runnable r, long interval);
}

View File

@ -22,19 +22,15 @@
package me.lucko.luckperms.common.api.internal;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import me.lucko.luckperms.api.*;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.AbstractFuture;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static me.lucko.luckperms.common.api.internal.Utils.*;
@ -210,64 +206,68 @@ public class DatastoreLink implements Datastore {
@Override
public boolean logAction(@NonNull LogEntry entry) {
return master.logAction(entry);
return master.logAction(entry).getOrDefault(false);
}
@Override
public Log getLog() {
return new LogLink(master.getLog());
me.lucko.luckperms.common.data.Log log = master.getLog().getOrDefault(null);
if (log == null) {
return null;
}
return new LogLink(log);
}
@Override
public boolean loadOrCreateUser(@NonNull UUID uuid, @NonNull String username) {
return master.loadUser(uuid, checkUsername(username));
return master.loadUser(uuid, checkUsername(username)).getOrDefault(false);
}
@Override
public boolean loadUser(@NonNull UUID uuid) {
return master.loadUser(uuid, "null");
return master.loadUser(uuid, "null").getOrDefault(false);
}
@Override
public boolean loadUser(@NonNull UUID uuid, @NonNull String username) {
return master.loadUser(uuid, checkUsername(username));
return master.loadUser(uuid, checkUsername(username)).getOrDefault(false);
}
@Override
public boolean saveUser(@NonNull User user) {
checkUser(user);
return master.saveUser(((UserLink) user).getMaster());
return master.saveUser(((UserLink) user).getMaster()).getOrDefault(false);
}
@Override
public boolean cleanupUsers() {
return master.cleanupUsers();
return master.cleanupUsers().getOrDefault(false);
}
@Override
public Set<UUID> getUniqueUsers() {
return master.getUniqueUsers();
return master.getUniqueUsers().getOrDefault(null);
}
@Override
public boolean createAndLoadGroup(@NonNull String name) {
return master.createAndLoadGroup(checkName(name));
return master.createAndLoadGroup(checkName(name)).getOrDefault(false);
}
@Override
public boolean loadGroup(@NonNull String name) {
return master.loadGroup(checkName(name));
return master.loadGroup(checkName(name)).getOrDefault(false);
}
@Override
public boolean loadAllGroups() {
return master.loadAllGroups();
return master.loadAllGroups().getOrDefault(false);
}
@Override
public boolean saveGroup(@NonNull Group group) {
checkGroup(group);
return master.saveGroup(((GroupLink) group).getMaster());
return master.saveGroup(((GroupLink) group).getMaster()).getOrDefault(false);
}
@Override
@ -276,44 +276,44 @@ public class DatastoreLink implements Datastore {
if (group.getName().equalsIgnoreCase(plugin.getConfiguration().getDefaultGroupName())) {
throw new IllegalArgumentException("Cannot delete the default group.");
}
return master.deleteGroup(((GroupLink) group).getMaster());
return master.deleteGroup(((GroupLink) group).getMaster()).getOrDefault(false);
}
@Override
public boolean createAndLoadTrack(@NonNull String name) {
return master.createAndLoadTrack(checkName(name));
return master.createAndLoadTrack(checkName(name)).getOrDefault(false);
}
@Override
public boolean loadTrack(@NonNull String name) {
return master.loadTrack(checkName(name));
return master.loadTrack(checkName(name)).getOrDefault(false);
}
@Override
public boolean loadAllTracks() {
return master.loadAllTracks();
return master.loadAllTracks().getOrDefault(false);
}
@Override
public boolean saveTrack(@NonNull Track track) {
checkTrack(track);
return master.saveTrack(((TrackLink) track).getMaster());
return master.saveTrack(((TrackLink) track).getMaster()).getOrDefault(false);
}
@Override
public boolean deleteTrack(@NonNull Track track) {
checkTrack(track);
return master.deleteTrack(((TrackLink) track).getMaster());
return master.deleteTrack(((TrackLink) track).getMaster()).getOrDefault(false);
}
@Override
public boolean saveUUIDData(@NonNull String username, @NonNull UUID uuid) {
return master.saveUUIDData(checkUsername(username), uuid);
return master.saveUUIDData(checkUsername(username), uuid).getOrDefault(false);
}
@Override
public UUID getUUID(@NonNull String username) {
return master.getUUID(checkUsername(username));
return master.getUUID(checkUsername(username)).getOrDefault(null);
}
}
@ -323,193 +323,112 @@ public class DatastoreLink implements Datastore {
@Override
public java.util.concurrent.Future<Boolean> logAction(@NonNull LogEntry entry) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.logAction(entry, lpf);
return lpf;
return master.logAction(entry);
}
@Override
public java.util.concurrent.Future<Log> getLog() {
LPFuture<Log> lpf = new LPFuture<>();
master.getLog(log -> lpf.onComplete(new LogLink(log)));
return lpf;
AbstractFuture<Log> fut = new AbstractFuture<>();
master.getLog(log -> fut.complete(new LogLink(log)));
return fut;
}
@Override
public java.util.concurrent.Future<Boolean> loadOrCreateUser(@NonNull UUID uuid, @NonNull String username) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadUser(uuid, checkUsername(username), lpf);
return lpf;
return master.loadUser(uuid, checkUsername(username));
}
@Override
public java.util.concurrent.Future<Boolean> loadUser(@NonNull UUID uuid) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadUser(uuid, "null", lpf);
return lpf;
return master.loadUser(uuid, "null");
}
@Override
public java.util.concurrent.Future<Boolean> loadUser(@NonNull UUID uuid, @NonNull String username) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadUser(uuid, checkUsername(username), lpf);
return lpf;
return master.loadUser(uuid, checkUsername(username));
}
@Override
public java.util.concurrent.Future<Boolean> saveUser(@NonNull User user) {
LPFuture<Boolean> lpf = new LPFuture<>();
checkUser(user);
master.saveUser(((UserLink) user).getMaster(), lpf);
return lpf;
return master.saveUser(((UserLink) user).getMaster());
}
@Override
public java.util.concurrent.Future<Boolean> cleanupUsers() {
LPFuture<Boolean> lpf = new LPFuture<>();
master.cleanupUsers(lpf);
return lpf;
return master.cleanupUsers();
}
@Override
public java.util.concurrent.Future<Set<UUID>> getUniqueUsers() {
LPFuture<Set<UUID>> lpf = new LPFuture<>();
master.getUniqueUsers(lpf);
return lpf;
return master.getUniqueUsers();
}
@Override
public java.util.concurrent.Future<Boolean> createAndLoadGroup(@NonNull String name) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.createAndLoadGroup(checkName(name), lpf);
return lpf;
return master.createAndLoadGroup(checkName(name));
}
@Override
public java.util.concurrent.Future<Boolean> loadGroup(@NonNull String name) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadGroup(checkName(name), lpf);
return lpf;
return master.loadGroup(checkName(name));
}
@Override
public java.util.concurrent.Future<Boolean> loadAllGroups() {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadAllGroups(lpf);
return lpf;
return master.loadAllGroups();
}
@Override
public java.util.concurrent.Future<Boolean> saveGroup(@NonNull Group group) {
LPFuture<Boolean> lpf = new LPFuture<>();
checkGroup(group);
master.saveGroup(((GroupLink) group).getMaster(), lpf);
return lpf;
return master.saveGroup(((GroupLink) group).getMaster());
}
@Override
public java.util.concurrent.Future<Boolean> deleteGroup(@NonNull Group group) {
LPFuture<Boolean> lpf = new LPFuture<>();
checkGroup(group);
if (group.getName().equalsIgnoreCase(plugin.getConfiguration().getDefaultGroupName())) {
throw new IllegalArgumentException("Cannot delete the default group.");
}
master.deleteGroup(((GroupLink) group).getMaster(), lpf);
return lpf;
return master.deleteGroup(((GroupLink) group).getMaster());
}
@Override
public java.util.concurrent.Future<Boolean> createAndLoadTrack(@NonNull String name) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.createAndLoadTrack(checkName(name), lpf);
return lpf;
return master.createAndLoadTrack(checkName(name));
}
@Override
public java.util.concurrent.Future<Boolean> loadTrack(@NonNull String name) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadTrack(checkName(name), lpf);
return lpf;
return master.loadTrack(checkName(name));
}
@Override
public java.util.concurrent.Future<Boolean> loadAllTracks() {
LPFuture<Boolean> lpf = new LPFuture<>();
master.loadAllTracks(lpf);
return lpf;
return master.loadAllTracks();
}
@Override
public java.util.concurrent.Future<Boolean> saveTrack(@NonNull Track track) {
LPFuture<Boolean> lpf = new LPFuture<>();
checkTrack(track);
master.saveTrack(((TrackLink) track).getMaster(), lpf);
return lpf;
return master.saveTrack(((TrackLink) track).getMaster());
}
@Override
public java.util.concurrent.Future<Boolean> deleteTrack(@NonNull Track track) {
LPFuture<Boolean> lpf = new LPFuture<>();
checkTrack(track);
master.deleteTrack(((TrackLink) track).getMaster(), lpf);
return lpf;
return master.deleteTrack(((TrackLink) track).getMaster());
}
@Override
public java.util.concurrent.Future<Boolean> saveUUIDData(@NonNull String username, @NonNull UUID uuid) {
LPFuture<Boolean> lpf = new LPFuture<>();
master.saveUUIDData(checkUsername(username), uuid, lpf);
return lpf;
return master.saveUUIDData(checkUsername(username), uuid);
}
@Override
public java.util.concurrent.Future<UUID> getUUID(@NonNull String username) {
LPFuture<UUID> lpf = new LPFuture<>();
master.getUUID(checkUsername(username), lpf);
return lpf;
}
}
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class LPFuture<T> implements Callback<T>, java.util.concurrent.Future<T> {
private final CountDownLatch latch = new CountDownLatch(1);
private T value;
@Override
public void onComplete(T t) {
value = t;
latch.countDown();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
// Not supported
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return latch.getCount() == 0;
}
@Override
public T get() throws InterruptedException {
latch.await();
return value;
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
if (latch.await(timeout, unit)) {
return value;
} else {
throw new TimeoutException();
}
return master.getUUID(checkUsername(username));
}
}

View File

@ -162,7 +162,7 @@ public abstract class SubCommand<T> {
}
public static void save(User user, Sender sender, LuckPermsPlugin plugin) {
if (plugin.getDatastore().saveUser(user)) {
if (plugin.getDatastore().saveUser(user).getOrDefault(false)) {
Message.USER_SAVE_SUCCESS.send(sender);
} else {
Message.USER_SAVE_ERROR.send(sender);
@ -172,7 +172,7 @@ public abstract class SubCommand<T> {
}
public static void save(Group group, Sender sender, LuckPermsPlugin plugin) {
if (plugin.getDatastore().saveGroup(group)) {
if (plugin.getDatastore().saveGroup(group).getOrDefault(false)) {
Message.GROUP_SAVE_SUCCESS.send(sender);
} else {
Message.GROUP_SAVE_ERROR.send(sender);
@ -182,7 +182,7 @@ public abstract class SubCommand<T> {
}
public static void save(Track track, Sender sender, LuckPermsPlugin plugin) {
if (plugin.getDatastore().saveTrack(track)) {
if (plugin.getDatastore().saveTrack(track).getOrDefault(false)) {
Message.TRACK_SAVE_SUCCESS.send(sender);
} else {
Message.TRACK_SAVE_ERROR.send(sender);

View File

@ -61,7 +61,7 @@ public class ParentAdd extends SecondarySubCommand {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadGroup(groupName)) {
if (!plugin.getDatastore().loadGroup(groupName).getOrDefault(false)) {
Message.GROUP_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}

View File

@ -80,7 +80,7 @@ public class ParentAddTemp extends SecondarySubCommand {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadGroup(groupName)) {
if (!plugin.getDatastore().loadGroup(groupName).getOrDefault(false)) {
Message.GROUP_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}

View File

@ -51,12 +51,12 @@ public class CreateGroup extends SingleMainCommand {
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadGroup(groupName)) {
if (plugin.getDatastore().loadGroup(groupName).getOrDefault(false)) {
Message.GROUP_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadGroup(groupName)) {
if (!plugin.getDatastore().createAndLoadGroup(groupName).getOrDefault(false)) {
Message.CREATE_GROUP_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -55,7 +55,7 @@ public class DeleteGroup extends SingleMainCommand {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadGroup(groupName)) {
if (!plugin.getDatastore().loadGroup(groupName).getOrDefault(false)) {
Message.GROUP_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}
@ -66,7 +66,7 @@ public class DeleteGroup extends SingleMainCommand {
return CommandResult.LOADING_ERROR;
}
if (!plugin.getDatastore().deleteGroup(group)) {
if (!plugin.getDatastore().deleteGroup(group).getOrDefault(false)) {
Message.DELETE_GROUP_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -57,7 +57,7 @@ public class GroupMainCommand extends MainCommand<Group> {
@Override
protected Group getTarget(String target, LuckPermsPlugin plugin, Sender sender) {
if (!plugin.getDatastore().loadGroup(target)) {
if (!plugin.getDatastore().loadGroup(target).getOrDefault(false)) {
Message.GROUP_NOT_FOUND.send(sender);
return null;
}

View File

@ -42,7 +42,7 @@ public class ListGroups extends SingleMainCommand {
@Override
protected CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
if (!plugin.getDatastore().loadAllGroups()) {
if (!plugin.getDatastore().loadAllGroups().getOrDefault(false)) {
Message.GROUPS_LOAD_ERROR.send(sender);
return CommandResult.LOADING_ERROR;
}

View File

@ -47,12 +47,12 @@ public class GroupClone extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadGroup(newGroupName)) {
if (plugin.getDatastore().loadGroup(newGroupName).getOrDefault(false)) {
Message.GROUP_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadGroup(newGroupName)) {
if (!plugin.getDatastore().createAndLoadGroup(newGroupName).getOrDefault(false)) {
Message.CREATE_GROUP_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -47,12 +47,12 @@ public class GroupRename extends SubCommand<Group> {
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadGroup(newGroupName)) {
if (plugin.getDatastore().loadGroup(newGroupName).getOrDefault(false)) {
Message.GROUP_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadGroup(newGroupName)) {
if (!plugin.getDatastore().createAndLoadGroup(newGroupName).getOrDefault(false)) {
Message.CREATE_GROUP_ERROR.send(sender);
return CommandResult.FAILURE;
}
@ -63,7 +63,7 @@ public class GroupRename extends SubCommand<Group> {
return CommandResult.LOADING_ERROR;
}
if (!plugin.getDatastore().deleteGroup(group)) {
if (!plugin.getDatastore().deleteGroup(group).getOrDefault(false)) {
Message.DELETE_GROUP_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -39,7 +39,7 @@ public class GroupShowTracks extends SubCommand<Group> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, Group group, List<String> args, String label) {
if (!plugin.getDatastore().loadAllTracks()) {
if (!plugin.getDatastore().loadAllTracks().getOrDefault(false)) {
Message.TRACKS_LOAD_ERROR.send(sender);
return CommandResult.LOADING_ERROR;
}

View File

@ -52,7 +52,7 @@ public class LogMainCommand extends MainCommand<Log> {
@Override
protected Log getTarget(String target, LuckPermsPlugin plugin, Sender sender) {
Log log = plugin.getDatastore().getLog();
Log log = plugin.getDatastore().getLog().getOrDefault(null);
if (log == null) {
Message.LOG_LOAD_ERROR.send(sender);

View File

@ -74,7 +74,7 @@ public class LogRecent extends SubCommand<Log> {
return CommandResult.INVALID_ARGS;
}
UUID uuid = plugin.getDatastore().getUUID(s);
UUID uuid = plugin.getDatastore().getUUID(s).getOrDefault(null);
if (uuid == null) {
Message.USER_NOT_FOUND.send(sender);

View File

@ -76,7 +76,7 @@ public class LogUserHistory extends SubCommand<Log> {
return CommandResult.INVALID_ARGS;
}
UUID uuid1 = plugin.getDatastore().getUUID(user);
UUID uuid1 = plugin.getDatastore().getUUID(user).getOrDefault(null);
if (uuid1 == null) {
Message.USER_NOT_FOUND.send(sender);

View File

@ -118,7 +118,7 @@ public class ExportCommand extends SingleMainCommand {
// Export users
log.info("Export: Exporting all users. Finding a list of unique users to export.");
Datastore ds = plugin.getDatastore();
Set<UUID> users = ds.getUniqueUsers();
Set<UUID> users = ds.getUniqueUsers().getOrDefault(null);
log.info("Export: Found " + users.size() + " unique users to export.");
int userCount = 0;

View File

@ -51,12 +51,12 @@ public class CreateTrack extends SingleMainCommand {
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadTrack(trackName)) {
if (plugin.getDatastore().loadTrack(trackName).getOrDefault(false)) {
Message.TRACK_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadTrack(trackName)) {
if (!plugin.getDatastore().createAndLoadTrack(trackName).getOrDefault(false)) {
Message.CREATE_TRACK_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -49,7 +49,7 @@ public class DeleteTrack extends SingleMainCommand {
}
String trackName = args.get(0).toLowerCase();
if (!plugin.getDatastore().loadTrack(trackName)) {
if (!plugin.getDatastore().loadTrack(trackName).getOrDefault(false)) {
Message.TRACK_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}
@ -60,7 +60,7 @@ public class DeleteTrack extends SingleMainCommand {
return CommandResult.LOADING_ERROR;
}
if (!plugin.getDatastore().deleteTrack(track)) {
if (!plugin.getDatastore().deleteTrack(track).getOrDefault(false)) {
Message.DELETE_TRACK_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -40,7 +40,7 @@ public class ListTracks extends SingleMainCommand {
@Override
protected CommandResult execute(LuckPermsPlugin plugin, Sender sender, List<String> args, String label) {
if (!plugin.getDatastore().loadAllTracks()) {
if (!plugin.getDatastore().loadAllTracks().getOrDefault(false)) {
Message.TRACKS_LOAD_ERROR.send(sender);
return CommandResult.LOADING_ERROR;
}

View File

@ -50,7 +50,7 @@ public class TrackMainCommand extends MainCommand<Track> {
@Override
protected Track getTarget(String target, LuckPermsPlugin plugin, Sender sender) {
if (!plugin.getDatastore().loadTrack(target)) {
if (!plugin.getDatastore().loadTrack(target).getOrDefault(false)) {
Message.TRACK_NOT_FOUND.send(sender);
return null;
}

View File

@ -50,7 +50,7 @@ public class TrackAppend extends SubCommand<Track> {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadGroup(groupName)) {
if (!plugin.getDatastore().loadGroup(groupName).getOrDefault(false)) {
Message.GROUP_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}

View File

@ -47,12 +47,12 @@ public class TrackClone extends SubCommand<Track> {
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadTrack(newTrackName)) {
if (plugin.getDatastore().loadTrack(newTrackName).getOrDefault(false)) {
Message.TRACK_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadTrack(newTrackName)) {
if (!plugin.getDatastore().createAndLoadTrack(newTrackName).getOrDefault(false)) {
Message.CREATE_TRACK_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -61,7 +61,7 @@ public class TrackInsert extends SubCommand<Track> {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadGroup(groupName)) {
if (!plugin.getDatastore().loadGroup(groupName).getOrDefault(false)) {
Message.GROUP_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}

View File

@ -47,12 +47,12 @@ public class TrackRename extends SubCommand<Track> {
return CommandResult.INVALID_ARGS;
}
if (plugin.getDatastore().loadTrack(newTrackName)) {
if (plugin.getDatastore().loadTrack(newTrackName).getOrDefault(false)) {
Message.TRACK_ALREADY_EXISTS.send(sender);
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().createAndLoadTrack(newTrackName)) {
if (!plugin.getDatastore().createAndLoadTrack(newTrackName).getOrDefault(false)) {
Message.CREATE_TRACK_ERROR.send(sender);
return CommandResult.FAILURE;
}
@ -63,7 +63,7 @@ public class TrackRename extends SubCommand<Track> {
return CommandResult.LOADING_ERROR;
}
if (!plugin.getDatastore().deleteTrack(track)) {
if (!plugin.getDatastore().deleteTrack(track).getOrDefault(false)) {
Message.DELETE_TRACK_ERROR.send(sender);
return CommandResult.FAILURE;
}

View File

@ -70,7 +70,7 @@ public class UserMainCommand extends MainCommand<User> {
return null;
}
u = plugin.getDatastore().getUUID(target);
u = plugin.getDatastore().getUUID(target).getOrDefault(null);
if (u == null) {
Message.USER_NOT_FOUND.send(sender);
return null;
@ -80,10 +80,10 @@ public class UserMainCommand extends MainCommand<User> {
}
}
String name = plugin.getDatastore().getName(u);
String name = plugin.getDatastore().getName(u).getOrDefault(null);
if (name == null) name = "null";
if (!plugin.getDatastore().loadUser(u, name)) {
if (!plugin.getDatastore().loadUser(u, name).getOrDefault(false)) {
Message.LOADING_ERROR.send(sender);
}

View File

@ -54,7 +54,7 @@ public class UserDemote extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadTrack(trackName)) {
if (!plugin.getDatastore().loadTrack(trackName).getOrDefault(false)) {
Message.TRACK_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}
@ -85,7 +85,7 @@ public class UserDemote extends SubCommand<User> {
return CommandResult.STATE_ERROR;
}
if (!plugin.getDatastore().loadGroup(previous)) {
if (!plugin.getDatastore().loadGroup(previous).getOrDefault(false)) {
Message.USER_DEMOTE_ERROR_MALFORMED.send(sender, previous);
return CommandResult.STATE_ERROR;
}

View File

@ -54,7 +54,7 @@ public class UserPromote extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadTrack(trackName)) {
if (!plugin.getDatastore().loadTrack(trackName).getOrDefault(false)) {
Message.TRACK_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}
@ -85,7 +85,7 @@ public class UserPromote extends SubCommand<User> {
return CommandResult.STATE_ERROR;
}
if (!plugin.getDatastore().loadGroup(next)) {
if (!plugin.getDatastore().loadGroup(next).getOrDefault(false)) {
Message.USER_PROMOTE_ERROR_MALFORMED.send(sender, next);
return CommandResult.STATE_ERROR;
}

View File

@ -47,7 +47,7 @@ public class UserShowPos extends SubCommand<User> {
return CommandResult.INVALID_ARGS;
}
if (!plugin.getDatastore().loadTrack(trackName)) {
if (!plugin.getDatastore().loadTrack(trackName).getOrDefault(false)) {
Message.TRACK_DOES_NOT_EXIST.send(sender);
return CommandResult.INVALID_ARGS;
}

View File

@ -40,7 +40,7 @@ public class UserShowTracks extends SubCommand<User> {
@Override
public CommandResult execute(LuckPermsPlugin plugin, Sender sender, User user, List<String> args, String label) {
if (!plugin.getDatastore().loadAllTracks()) {
if (!plugin.getDatastore().loadAllTracks().getOrDefault(false)) {
Message.TRACKS_LOAD_ERROR.send(sender);
return CommandResult.LOADING_ERROR;
}

View File

@ -61,7 +61,7 @@ public class BulkEditGroup extends SubCommand<Datastore> {
return CommandResult.FAILURE;
}
Set<UUID> uuids = datastore.getUniqueUsers();
Set<UUID> uuids = datastore.getUniqueUsers().getOrDefault(null);
for (UUID u : uuids) {
plugin.getDatastore().loadUser(u, "null");

View File

@ -61,7 +61,7 @@ public class BulkEditPermission extends SubCommand<Datastore> {
return CommandResult.FAILURE;
}
Set<UUID> uuids = datastore.getUniqueUsers();
Set<UUID> uuids = datastore.getUniqueUsers().getOrDefault(null);
for (UUID u : uuids) {
plugin.getDatastore().loadUser(u, "null");

View File

@ -0,0 +1,195 @@
/*
* 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.common.storage;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.storage.backing.AbstractBacking;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.utils.LPFuture;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
import java.util.function.Supplier;
/**
* Converts a {@link AbstractBacking} to use {@link Future}s
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class AbstractDatastore implements Datastore {
public static Datastore wrap(AbstractBacking backing) {
return TolerantDatastore.wrap(new AbstractDatastore(backing));
}
private final AbstractBacking backing;
private <T> LPFuture<T> makeFuture(Supplier<T> supplier) {
AbstractFuture<T> future = new AbstractFuture<>();
backing.doAsync(() -> {
T result = supplier.get();
future.complete(result);
});
return future;
}
@Override
public String getName() {
return backing.getName();
}
@Override
public boolean isAcceptingLogins() {
return backing.isAcceptingLogins();
}
@Override
public void setAcceptingLogins(boolean acceptingLogins) {
backing.setAcceptingLogins(acceptingLogins);
}
@Override
public void doAsync(Runnable r) {
backing.doAsync(r);
}
@Override
public void doSync(Runnable r) {
backing.doSync(r);
}
@Override
public LPFuture<Void> init() {
return makeFuture(() -> {
backing.init();
return null;
});
}
@Override
public LPFuture<Void> shutdown() {
return makeFuture(() -> {
backing.shutdown();
return null;
});
}
@Override
public LPFuture<Boolean> logAction(LogEntry entry) {
return makeFuture(() -> backing.logAction(entry));
}
@Override
public LPFuture<Log> getLog() {
return makeFuture(backing::getLog);
}
@Override
public LPFuture<Boolean> loadUser(UUID uuid, String username) {
return makeFuture(() -> backing.loadUser(uuid, username));
}
@Override
public LPFuture<Boolean> saveUser(User user) {
return makeFuture(() -> backing.saveUser(user));
}
@Override
public LPFuture<Boolean> cleanupUsers() {
return makeFuture(backing::cleanupUsers);
}
@Override
public LPFuture<Set<UUID>> getUniqueUsers() {
return makeFuture(backing::getUniqueUsers);
}
@Override
public LPFuture<Boolean> createAndLoadGroup(String name) {
return makeFuture(() -> backing.createAndLoadGroup(name));
}
@Override
public LPFuture<Boolean> loadGroup(String name) {
return makeFuture(() -> backing.loadGroup(name));
}
@Override
public LPFuture<Boolean> loadAllGroups() {
return makeFuture(backing::loadAllGroups);
}
@Override
public LPFuture<Boolean> saveGroup(Group group) {
return makeFuture(() -> backing.saveGroup(group));
}
@Override
public LPFuture<Boolean> deleteGroup(Group group) {
return makeFuture(() -> backing.deleteGroup(group));
}
@Override
public LPFuture<Boolean> createAndLoadTrack(String name) {
return makeFuture(() -> backing.createAndLoadTrack(name));
}
@Override
public LPFuture<Boolean> loadTrack(String name) {
return makeFuture(() -> backing.loadTrack(name));
}
@Override
public LPFuture<Boolean> loadAllTracks() {
return makeFuture(backing::loadAllTracks);
}
@Override
public LPFuture<Boolean> saveTrack(Track track) {
return makeFuture(() -> backing.saveTrack(track));
}
@Override
public LPFuture<Boolean> deleteTrack(Track track) {
return makeFuture(() -> backing.deleteTrack(track));
}
@Override
public LPFuture<Boolean> saveUUIDData(String username, UUID uuid) {
return makeFuture(() -> backing.saveUUIDData(username, uuid));
}
@Override
public LPFuture<UUID> getUUID(String username) {
return makeFuture(() -> backing.getUUID(username));
}
@Override
public LPFuture<String> getName(UUID uuid) {
return makeFuture(() -> backing.getName(uuid));
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.common.storage;
import me.lucko.luckperms.common.utils.LPFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class AbstractFuture<R> implements LPFuture<R> {
private final CountDownLatch latch = new CountDownLatch(1);
private R value;
public void complete(R r) {
value = r;
latch.countDown();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
// Not supported
return false;
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return latch.getCount() == 0;
}
@Override
public R get() throws InterruptedException {
latch.await();
return value;
}
@Override
public R get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
if (latch.await(timeout, unit)) {
return value;
} else {
throw new TimeoutException();
}
}
}

View File

@ -0,0 +1,237 @@
/*
* 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.common.storage;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.users.UserIdentifier;
import me.lucko.luckperms.common.utils.Buffer;
import me.lucko.luckperms.common.utils.LPFuture;
import java.util.Set;
import java.util.UUID;
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class BufferedOutputDatastore implements Datastore, Runnable {
public static BufferedOutputDatastore wrap(Datastore datastore, long flushTime) {
return new BufferedOutputDatastore(datastore, flushTime);
}
@Getter
private final Datastore backing;
private final long flushTime;
private final Buffer<LogEntry, Boolean> logOutputBuffer = new Buffer<LogEntry, Boolean>() {
@Override
public Boolean dequeue(LogEntry logEntry) {
return backing.logAction(logEntry).getOrDefault(false);
}
};
private final Buffer<User, Boolean> userOutputBuffer = new Buffer<User, Boolean>() {
@Override
public Boolean dequeue(User user) {
return saveUser(user).getOrDefault(false);
}
};
private final Buffer<Group, Boolean> groupOutputBuffer = new Buffer<Group, Boolean>() {
@Override
public Boolean dequeue(Group group) {
return saveGroup(group).getOrDefault(false);
}
};
private final Buffer<Track, Boolean> trackOutputBuffer = new Buffer<Track, Boolean>() {
@Override
public Boolean dequeue(Track track) {
return saveTrack(track).getOrDefault(false);
}
};
private final Buffer<UserIdentifier, Boolean> uuidDataOutputBuffer = new Buffer<UserIdentifier, Boolean>() {
@Override
protected Boolean dequeue(UserIdentifier userIdentifier) {
return saveUUIDData(userIdentifier.getUsername(), userIdentifier.getUuid()).getOrDefault(false);
}
};
@Override
public void run() {
flush(flushTime);
}
public void forceFlush() {
flush(-1);
}
public void flush(long flushTime) {
logOutputBuffer.flush(flushTime);
userOutputBuffer.flush(flushTime);
groupOutputBuffer.flush(flushTime);
trackOutputBuffer.flush(flushTime);
userOutputBuffer.flush(flushTime);
}
public Datastore force() {
return backing;
}
@Override
public LPFuture<Void> init() {
return backing.init();
}
@Override
public LPFuture<Void> shutdown() {
forceFlush();
return backing.shutdown();
}
@Override
public LPFuture<Boolean> logAction(LogEntry entry) {
return logOutputBuffer.enqueue(entry);
}
@Override
public LPFuture<Log> getLog() {
return backing.getLog();
}
@Override
public LPFuture<Boolean> loadUser(UUID uuid, String username) {
return backing.loadUser(uuid, username);
}
@Override
public LPFuture<Boolean> saveUser(User user) {
return userOutputBuffer.enqueue(user);
}
@Override
public LPFuture<Boolean> cleanupUsers() {
return backing.cleanupUsers();
}
@Override
public LPFuture<Set<UUID>> getUniqueUsers() {
return backing.getUniqueUsers();
}
@Override
public LPFuture<Boolean> createAndLoadGroup(String name) {
return backing.createAndLoadGroup(name);
}
@Override
public LPFuture<Boolean> loadGroup(String name) {
return backing.loadGroup(name);
}
@Override
public LPFuture<Boolean> loadAllGroups() {
return backing.loadAllGroups();
}
@Override
public LPFuture<Boolean> saveGroup(Group group) {
return groupOutputBuffer.enqueue(group);
}
@Override
public LPFuture<Boolean> deleteGroup(Group group) {
return backing.deleteGroup(group);
}
@Override
public LPFuture<Boolean> createAndLoadTrack(String name) {
return backing.createAndLoadTrack(name);
}
@Override
public LPFuture<Boolean> loadTrack(String name) {
return backing.loadTrack(name);
}
@Override
public LPFuture<Boolean> loadAllTracks() {
return backing.loadAllTracks();
}
@Override
public LPFuture<Boolean> saveTrack(Track track) {
return trackOutputBuffer.enqueue(track);
}
@Override
public LPFuture<Boolean> deleteTrack(Track track) {
return backing.deleteTrack(track);
}
@Override
public LPFuture<Boolean> saveUUIDData(String username, UUID uuid) {
return uuidDataOutputBuffer.enqueue(UserIdentifier.of(uuid, username));
}
@Override
public LPFuture<UUID> getUUID(String username) {
return backing.getUUID(username);
}
@Override
public LPFuture<String> getName(UUID uuid) {
return backing.getName(uuid);
}
@Override
public String getName() {
return backing.getName();
}
@Override
public boolean isAcceptingLogins() {
return backing.isAcceptingLogins();
}
@Override
public void setAcceptingLogins(boolean acceptingLogins) {
backing.setAcceptingLogins(acceptingLogins);
}
@Override
public void doAsync(Runnable r) {
backing.doAsync(r);
}
@Override
public void doSync(Runnable r) {
backing.doSync(r);
}
}

View File

@ -22,208 +22,191 @@
package me.lucko.luckperms.common.storage;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.api.data.Callback;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.utils.LPFuture;
import java.util.Set;
import java.util.UUID;
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class Datastore {
protected final LuckPermsPlugin plugin;
@Getter
public final String name;
@Getter
@Setter
private boolean acceptingLogins = false;
public interface Datastore {
String getName();
boolean isAcceptingLogins();
void setAcceptingLogins(boolean acceptingLogins);
/**
* Execute a runnable asynchronously
* @param r the task to run
*/
private void doAsync(Runnable r) {
plugin.doAsync(r);
}
void doAsync(Runnable r);
/**
* Execute a runnable synchronously
* @param r the task to run
*/
private void doSync(Runnable r) {
plugin.doSync(r);
void doSync(Runnable r);
default Datastore force() {
return this;
}
/*
These methods are called immediately and in the same thread as they are called in.
*/
public abstract void init();
public abstract void shutdown();
public abstract boolean logAction(LogEntry entry);
public abstract Log getLog();
public abstract boolean loadUser(UUID uuid, String username);
public abstract boolean saveUser(User user);
public abstract boolean cleanupUsers();
public abstract Set<UUID> getUniqueUsers();
public abstract boolean createAndLoadGroup(String name);
public abstract boolean loadGroup(String name);
public abstract boolean loadAllGroups();
public abstract boolean saveGroup(Group group);
public abstract boolean deleteGroup(Group group);
public abstract boolean createAndLoadTrack(String name);
public abstract boolean loadTrack(String name);
public abstract boolean loadAllTracks();
public abstract boolean saveTrack(Track track);
public abstract boolean deleteTrack(Track track);
public abstract boolean saveUUIDData(String username, UUID uuid);
public abstract UUID getUUID(String username);
public abstract String getName(UUID uuid);
LPFuture<Void> init();
LPFuture<Void> shutdown();
LPFuture<Boolean> logAction(LogEntry entry);
LPFuture<Log> getLog();
LPFuture<Boolean> loadUser(UUID uuid, String username);
LPFuture<Boolean> saveUser(User user);
LPFuture<Boolean> cleanupUsers();
LPFuture<Set<UUID>> getUniqueUsers();
LPFuture<Boolean> createAndLoadGroup(String name);
LPFuture<Boolean> loadGroup(String name);
LPFuture<Boolean> loadAllGroups();
LPFuture<Boolean> saveGroup(Group group);
LPFuture<Boolean> deleteGroup(Group group);
LPFuture<Boolean> createAndLoadTrack(String name);
LPFuture<Boolean> loadTrack(String name);
LPFuture<Boolean> loadAllTracks();
LPFuture<Boolean> saveTrack(Track track);
LPFuture<Boolean> deleteTrack(Track track);
LPFuture<Boolean> saveUUIDData(String username, UUID uuid);
LPFuture<UUID> getUUID(String username);
LPFuture<String> getName(UUID uuid);
/*
These methods will schedule the operation to run async. The callback will be ran when the task is complete.
Callbacks are ran on the main server thread (except on BungeeCord)
*/
public void logAction(LogEntry entry, Callback<Boolean> callback) {
default void logAction(LogEntry entry, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = logAction(entry);
boolean result = logAction(entry).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void getLog(Callback<Log> callback) {
default void getLog(Callback<Log> callback) {
doAsync(() -> {
Log result = getLog();
Log result = getLog().getOrDefault(null);
doSync(() -> callback.onComplete(result));
});
}
public void loadUser(UUID uuid, String username, Callback<Boolean> callback) {
default void loadUser(UUID uuid, String username, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = loadUser(uuid, username);
boolean result = loadUser(uuid, username).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void saveUser(User user, Callback<Boolean> callback) {
default void saveUser(User user, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = saveUser(user);
boolean result = saveUser(user).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void cleanupUsers(Callback<Boolean> callback) {
default void cleanupUsers(Callback<Boolean> callback) {
doAsync(() -> {
boolean result = cleanupUsers();
boolean result = cleanupUsers().getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void getUniqueUsers(Callback<Set<UUID>> callback) {
default void getUniqueUsers(Callback<Set<UUID>> callback) {
doAsync(() -> {
Set<UUID> result = getUniqueUsers();
Set<UUID> result = getUniqueUsers().getOrDefault(null);
doSync(() -> callback.onComplete(result));
});
}
public void createAndLoadGroup(String name, Callback<Boolean> callback) {
default void createAndLoadGroup(String name, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = createAndLoadGroup(name);
boolean result = createAndLoadGroup(name).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void loadGroup(String name, Callback<Boolean> callback) {
default void loadGroup(String name, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = loadGroup(name);
boolean result = loadGroup(name).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void loadAllGroups(Callback<Boolean> callback) {
default void loadAllGroups(Callback<Boolean> callback) {
doAsync(() -> {
boolean result = loadAllGroups();
boolean result = loadAllGroups().getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void saveGroup(Group group, Callback<Boolean> callback) {
default void saveGroup(Group group, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = saveGroup(group);
boolean result = saveGroup(group).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void deleteGroup(Group group, Callback<Boolean> callback) {
default void deleteGroup(Group group, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = deleteGroup(group);
boolean result = deleteGroup(group).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void createAndLoadTrack(String name, Callback<Boolean> callback) {
default void createAndLoadTrack(String name, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = createAndLoadTrack(name);
boolean result = createAndLoadTrack(name).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void loadTrack(String name, Callback<Boolean> callback) {
default void loadTrack(String name, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = loadTrack(name);
boolean result = loadTrack(name).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void loadAllTracks(Callback<Boolean> callback) {
default void loadAllTracks(Callback<Boolean> callback) {
doAsync(() -> {
boolean result = loadAllTracks();
boolean result = loadAllTracks().getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void saveTrack(Track track, Callback<Boolean> callback) {
default void saveTrack(Track track, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = saveTrack(track);
boolean result = saveTrack(track).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void deleteTrack(Track track, Callback<Boolean> callback) {
default void deleteTrack(Track track, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = deleteTrack(track);
boolean result = deleteTrack(track).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void saveUUIDData(String username, UUID uuid, Callback<Boolean> callback) {
default void saveUUIDData(String username, UUID uuid, Callback<Boolean> callback) {
doAsync(() -> {
boolean result = saveUUIDData(username, uuid);
boolean result = saveUUIDData(username, uuid).getOrDefault(false);
doSync(() -> callback.onComplete(result));
});
}
public void getUUID(String username, Callback<UUID> callback) {
default void getUUID(String username, Callback<UUID> callback) {
doAsync(() -> {
UUID result = getUUID(username);
UUID result = getUUID(username).getOrDefault(null);
doSync(() -> callback.onComplete(result));
});
}
public void getName(UUID uuid, Callback<String> callback) {
default void getName(UUID uuid, Callback<String> callback) {
doAsync(() -> {
String result = getName(uuid);
String result = getName(uuid).getOrDefault(null);
doSync(() -> callback.onComplete(result));
});
}

View File

@ -23,136 +23,172 @@
package me.lucko.luckperms.common.storage;
import com.google.common.collect.ImmutableMap;
import lombok.Getter;
import lombok.Setter;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.utils.LPFuture;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class SplitDatastore extends Datastore {
public class SplitBacking implements Datastore {
@Getter
@Setter
private boolean acceptingLogins = false;
private final LuckPermsPlugin plugin;
private final Map<String, Datastore> backing;
private final Map<String, String> types;
protected SplitDatastore(LuckPermsPlugin plugin, Map<String, Datastore> backing, Map<String, String> types) {
super(plugin, "Split Storage");
protected SplitBacking(LuckPermsPlugin plugin, Map<String, Datastore> backing, Map<String, String> types) {
this.plugin = plugin;
this.backing = ImmutableMap.copyOf(backing);
this.types = ImmutableMap.copyOf(types);
}
@Override
public void init() {
backing.values().forEach(Datastore::init);
for (Datastore ds : backing.values()) {
if (!ds.isAcceptingLogins()) {
return;
public String getName() {
return "Split Storage";
}
@Override
public void doAsync(Runnable r) {
plugin.doAsync(r);
}
@Override
public void doSync(Runnable r) {
plugin.doSync(r);
}
@Override
public LPFuture<Void> init() {
AbstractFuture<Void> future = new AbstractFuture<>();
doAsync(() -> {
boolean success = true;
backing.values().forEach(Datastore::init);
for (Datastore ds : backing.values()) {
if (!ds.isAcceptingLogins()) {
success = false;
}
}
}
setAcceptingLogins(true);
setAcceptingLogins(success);
future.complete(null);
});
return future;
}
@Override
public void shutdown() {
backing.values().forEach(Datastore::shutdown);
public LPFuture<Void> shutdown() {
AbstractFuture<Void> future = new AbstractFuture<>();
doAsync(() -> {
backing.values().forEach(Datastore::shutdown);
future.complete(null);
});
return future;
}
@Override
public boolean logAction(LogEntry entry) {
public LPFuture<Boolean> logAction(LogEntry entry) {
return backing.get(types.get("log")).logAction(entry);
}
@Override
public Log getLog() {
public LPFuture<Log> getLog() {
return backing.get(types.get("log")).getLog();
}
@Override
public boolean loadUser(UUID uuid, String username) {
public LPFuture<Boolean> loadUser(UUID uuid, String username) {
return backing.get(types.get("user")).loadUser(uuid, username);
}
@Override
public boolean saveUser(User user) {
public LPFuture<Boolean> saveUser(User user) {
return backing.get(types.get("user")).saveUser(user);
}
@Override
public boolean cleanupUsers() {
public LPFuture<Boolean> cleanupUsers() {
return backing.get(types.get("user")).cleanupUsers();
}
@Override
public Set<UUID> getUniqueUsers() {
public LPFuture<Set<UUID>> getUniqueUsers() {
return backing.get(types.get("user")).getUniqueUsers();
}
@Override
public boolean createAndLoadGroup(String name) {
public LPFuture<Boolean> createAndLoadGroup(String name) {
return backing.get(types.get("group")).createAndLoadGroup(name);
}
@Override
public boolean loadGroup(String name) {
public LPFuture<Boolean> loadGroup(String name) {
return backing.get(types.get("group")).loadGroup(name);
}
@Override
public boolean loadAllGroups() {
public LPFuture<Boolean> loadAllGroups() {
return backing.get(types.get("group")).loadAllGroups();
}
@Override
public boolean saveGroup(Group group) {
public LPFuture<Boolean> saveGroup(Group group) {
return backing.get(types.get("group")).saveGroup(group);
}
@Override
public boolean deleteGroup(Group group) {
public LPFuture<Boolean> deleteGroup(Group group) {
return backing.get(types.get("group")).deleteGroup(group);
}
@Override
public boolean createAndLoadTrack(String name) {
public LPFuture<Boolean> createAndLoadTrack(String name) {
return backing.get(types.get("track")).createAndLoadTrack(name);
}
@Override
public boolean loadTrack(String name) {
public LPFuture<Boolean> loadTrack(String name) {
return backing.get(types.get("track")).loadTrack(name);
}
@Override
public boolean loadAllTracks() {
public LPFuture<Boolean> loadAllTracks() {
return backing.get(types.get("track")).loadAllTracks();
}
@Override
public boolean saveTrack(Track track) {
public LPFuture<Boolean> saveTrack(Track track) {
return backing.get(types.get("track")).saveTrack(track);
}
@Override
public boolean deleteTrack(Track track) {
public LPFuture<Boolean> deleteTrack(Track track) {
return backing.get(types.get("track")).deleteTrack(track);
}
@Override
public boolean saveUUIDData(String username, UUID uuid) {
public LPFuture<Boolean> saveUUIDData(String username, UUID uuid) {
return backing.get(types.get("uuid")).saveUUIDData(username, uuid);
}
@Override
public UUID getUUID(String username) {
public LPFuture<UUID> getUUID(String username) {
return backing.get(types.get("uuid")).getUUID(username);
}
@Override
public String getName(UUID uuid) {
public LPFuture<String> getName(UUID uuid) {
return backing.get(types.get("uuid")).getName(uuid);
}
}

View File

@ -25,7 +25,7 @@ package me.lucko.luckperms.common.storage;
import com.google.common.collect.ImmutableSet;
import lombok.experimental.UtilityClass;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.storage.methods.*;
import me.lucko.luckperms.common.storage.backing.*;
import java.io.File;
import java.util.HashMap;
@ -63,7 +63,7 @@ public class StorageFactory {
backing.put(type, fromString(type, plugin));
}
datastore = new SplitDatastore(plugin, backing, types);
datastore = new SplitBacking(plugin, backing, types);
} else {
String storageMethod = plugin.getConfiguration().getStorageMethod().toLowerCase();
@ -77,24 +77,26 @@ public class StorageFactory {
}
plugin.getLog().info("Initialising datastore...");
datastore.init();
datastore.init().getOrDefault(null);
return datastore;
}
private static Datastore fromString(String storageMethod, LuckPermsPlugin plugin) {
switch (storageMethod) {
case "mysql":
return new MySQLDatastore(plugin, plugin.getConfiguration().getDatabaseValues());
BufferedOutputDatastore bod = BufferedOutputDatastore.wrap(AbstractDatastore.wrap(new MySQLBacking(plugin, plugin.getConfiguration().getDatabaseValues())), 5000L);
plugin.doAsyncRepeating(bod, 20L);
return bod;
case "sqlite":
return new SQLiteDatastore(plugin, new File(plugin.getDataFolder(), "luckperms.sqlite"));
return AbstractDatastore.wrap(new SQLiteBacking(plugin, new File(plugin.getDataFolder(), "luckperms.sqlite")));
case "h2":
return new H2Datastore(plugin, new File(plugin.getDataFolder(), "luckperms.db"));
return AbstractDatastore.wrap(new H2Backing(plugin, new File(plugin.getDataFolder(), "luckperms.db")));
case "mongodb":
return new MongoDBDatastore(plugin, plugin.getConfiguration().getDatabaseValues());
return AbstractDatastore.wrap(new MongoDBBacking(plugin, plugin.getConfiguration().getDatabaseValues()));
case "yaml":
return new YAMLDatastore(plugin, plugin.getDataFolder());
return AbstractDatastore.wrap(new YAMLBacking(plugin, plugin.getDataFolder()));
default:
return new JSONDatastore(plugin, plugin.getDataFolder());
return AbstractDatastore.wrap(new JSONBacking(plugin, plugin.getDataFolder()));
}
}
}

View File

@ -0,0 +1,282 @@
/*
* 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.common.storage;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import me.lucko.luckperms.common.utils.LPFuture;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Phaser;
/**
* A Datastore wrapping that ensures all tasks are completed before {@link Datastore#shutdown()} is called.
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class TolerantDatastore implements Datastore {
public static TolerantDatastore wrap(Datastore datastore) {
return new TolerantDatastore(datastore);
}
private final Datastore backing;
private final Phaser phaser = new Phaser();
@Override
public String getName() {
return backing.getName();
}
@Override
public boolean isAcceptingLogins() {
return backing.isAcceptingLogins();
}
@Override
public void setAcceptingLogins(boolean acceptingLogins) {
backing.setAcceptingLogins(acceptingLogins);
}
@Override
public void doAsync(Runnable r) {
backing.doAsync(r);
}
@Override
public void doSync(Runnable r) {
backing.doSync(r);
}
@Override
public LPFuture<Void> init() {
phaser.register();
try {
return backing.init();
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Void> shutdown() {
phaser.register(); // Register self
phaser.arriveAndAwaitAdvance(); // Wait for other threads to finish.
return backing.shutdown();
}
@Override
public LPFuture<Boolean> logAction(LogEntry entry) {
phaser.register();
try {
return backing.logAction(entry);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Log> getLog() {
phaser.register();
try {
return backing.getLog();
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> loadUser(UUID uuid, String username) {
phaser.register();
try {
return backing.loadUser(uuid, username);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> saveUser(User user) {
phaser.register();
try {
return backing.saveUser(user);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> cleanupUsers() {
phaser.register();
try {
return backing.cleanupUsers();
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Set<UUID>> getUniqueUsers() {
phaser.register();
try {
return backing.getUniqueUsers();
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> createAndLoadGroup(String name) {
phaser.register();
try {
return backing.createAndLoadGroup(name);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> loadGroup(String name) {
phaser.register();
try {
return backing.loadGroup(name);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> loadAllGroups() {
phaser.register();
try {
return backing.loadAllGroups();
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> saveGroup(Group group) {
phaser.register();
try {
return backing.saveGroup(group);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> deleteGroup(Group group) {
phaser.register();
try {
return backing.deleteGroup(group);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> createAndLoadTrack(String name) {
phaser.register();
try {
return backing.createAndLoadTrack(name);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> loadTrack(String name) {
phaser.register();
try {
return backing.loadTrack(name);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> loadAllTracks() {
phaser.register();
try {
return backing.loadAllTracks();
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> saveTrack(Track track) {
phaser.register();
try {
return backing.saveTrack(track);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> deleteTrack(Track track) {
phaser.register();
try {
return backing.deleteTrack(track);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<Boolean> saveUUIDData(String username, UUID uuid) {
phaser.register();
try {
return backing.saveUUIDData(username, uuid);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<UUID> getUUID(String username) {
phaser.register();
try {
return backing.getUUID(username);
} finally {
phaser.arriveAndDeregister();
}
}
@Override
public LPFuture<String> getName(UUID uuid) {
phaser.register();
try {
return backing.getName(uuid);
} finally {
phaser.arriveAndDeregister();
}
}
}

View File

@ -0,0 +1,88 @@
/*
* 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.common.storage.backing;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.users.User;
import java.util.Set;
import java.util.UUID;
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class AbstractBacking {
protected final LuckPermsPlugin plugin;
@Getter
public final String name;
@Getter
@Setter
private boolean acceptingLogins = false;
/**
* Execute a runnable asynchronously
* @param r the task to run
*/
public void doAsync(Runnable r) {
plugin.doAsync(r);
}
/**
* Execute a runnable synchronously
* @param r the task to run
*/
public void doSync(Runnable r) {
plugin.doSync(r);
}
public abstract void init();
public abstract void shutdown();
public abstract boolean logAction(LogEntry entry);
public abstract Log getLog();
public abstract boolean loadUser(UUID uuid, String username);
public abstract boolean saveUser(User user);
public abstract boolean cleanupUsers();
public abstract Set<UUID> getUniqueUsers();
public abstract boolean createAndLoadGroup(String name);
public abstract boolean loadGroup(String name);
public abstract boolean loadAllGroups();
public abstract boolean saveGroup(Group group);
public abstract boolean deleteGroup(Group group);
public abstract boolean createAndLoadTrack(String name);
public abstract boolean loadTrack(String name);
public abstract boolean loadAllTracks();
public abstract boolean saveTrack(Track track);
public abstract boolean deleteTrack(Track track);
public abstract boolean saveUUIDData(String username, UUID uuid);
public abstract UUID getUUID(String username);
public abstract String getName(UUID uuid);
}

View File

@ -20,14 +20,13 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import lombok.Cleanup;
import me.lucko.luckperms.api.LogEntry;
import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.constants.Constants;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.storage.Datastore;
import java.io.*;
import java.util.*;
@ -35,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.*;
import java.util.logging.Formatter;
abstract class FlatfileDatastore extends Datastore {
abstract class FlatfileBacking extends AbstractBacking {
private static final String LOG_FORMAT = "%s(%s): [%s] %s(%s) --> %s";
private final Logger actionLogger = Logger.getLogger("lp_actions");
@ -48,7 +47,7 @@ abstract class FlatfileDatastore extends Datastore {
File groupsDir;
File tracksDir;
FlatfileDatastore(LuckPermsPlugin plugin, String name, File pluginDir) {
FlatfileBacking(LuckPermsPlugin plugin, String name, File pluginDir) {
super(plugin, name);
this.pluginDir = pluginDir;
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import lombok.Cleanup;
import me.lucko.luckperms.common.LuckPermsPlugin;
@ -28,7 +28,7 @@ import me.lucko.luckperms.common.LuckPermsPlugin;
import java.io.File;
import java.sql.*;
public class H2Datastore extends SQLDatastore {
public class H2Backing extends SQLBacking {
private static final String CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;";
private static final String CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`)) DEFAULT CHARSET=utf8;";
@ -40,7 +40,7 @@ public class H2Datastore extends SQLDatastore {
private Connection connection = null;
private final Object connectionLock = new Object();
public H2Datastore(LuckPermsPlugin plugin, File file) {
public H2Backing(LuckPermsPlugin plugin, File file) {
super(plugin, "H2");
this.file = file;
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
@ -42,8 +42,8 @@ import java.util.stream.Collectors;
import static me.lucko.luckperms.common.core.PermissionHolder.exportToLegacy;
@SuppressWarnings("ResultOfMethodCallIgnored")
public class JSONDatastore extends FlatfileDatastore {
public JSONDatastore(LuckPermsPlugin plugin, File pluginDir) {
public class JSONBacking extends FlatfileBacking {
public JSONBacking(LuckPermsPlugin plugin, File pluginDir) {
super(plugin, "Flatfile - JSON", pluginDir);
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
@ -34,7 +34,6 @@ import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.groups.GroupManager;
import me.lucko.luckperms.common.storage.Datastore;
import me.lucko.luckperms.common.storage.DatastoreConfiguration;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.tracks.TrackManager;
@ -49,13 +48,13 @@ import java.util.stream.Collectors;
import static me.lucko.luckperms.common.core.PermissionHolder.exportToLegacy;
@SuppressWarnings("unchecked")
public class MongoDBDatastore extends Datastore {
public class MongoDBBacking extends AbstractBacking {
private final DatastoreConfiguration configuration;
private MongoClient mongoClient;
private MongoDatabase database;
public MongoDBDatastore(LuckPermsPlugin plugin, DatastoreConfiguration configuration) {
public MongoDBBacking(LuckPermsPlugin plugin, DatastoreConfiguration configuration) {
super(plugin, "MongoDB");
this.configuration = configuration;
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@ -33,7 +33,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class MySQLDatastore extends SQLDatastore {
public class MySQLBacking extends SQLBacking {
private static final String CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`)) DEFAULT CHARSET=utf8;";
private static final String CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`)) DEFAULT CHARSET=utf8;";
@ -44,7 +44,7 @@ public class MySQLDatastore extends SQLDatastore {
private final DatastoreConfiguration configuration;
private HikariDataSource hikari;
public MySQLDatastore(LuckPermsPlugin plugin, DatastoreConfiguration configuration) {
public MySQLBacking(LuckPermsPlugin plugin, DatastoreConfiguration configuration) {
super(plugin, "MySQL");
this.configuration = configuration;
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
@ -29,7 +29,6 @@ import me.lucko.luckperms.common.LuckPermsPlugin;
import me.lucko.luckperms.common.data.Log;
import me.lucko.luckperms.common.groups.Group;
import me.lucko.luckperms.common.groups.GroupManager;
import me.lucko.luckperms.common.storage.Datastore;
import me.lucko.luckperms.common.tracks.Track;
import me.lucko.luckperms.common.tracks.TrackManager;
import me.lucko.luckperms.common.users.User;
@ -44,7 +43,7 @@ import java.util.*;
import static me.lucko.luckperms.common.core.PermissionHolder.exportToLegacy;
abstract class SQLDatastore extends Datastore {
abstract class SQLBacking extends AbstractBacking {
private static final QueryPS EMPTY_PS = preparedStatement -> {};
private static final Type NM_TYPE = new TypeToken<Map<String, Boolean>>(){}.getType();
@ -79,7 +78,7 @@ abstract class SQLDatastore extends Datastore {
private final Gson gson;
SQLDatastore(LuckPermsPlugin plugin, String name) {
SQLBacking(LuckPermsPlugin plugin, String name) {
super(plugin, name);
gson = new Gson();
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import lombok.Cleanup;
import me.lucko.luckperms.common.LuckPermsPlugin;
@ -28,7 +28,7 @@ import me.lucko.luckperms.common.LuckPermsPlugin;
import java.io.File;
import java.sql.*;
public class SQLiteDatastore extends SQLDatastore {
public class SQLiteBacking extends SQLBacking {
private static final String CREATETABLE_UUID = "CREATE TABLE IF NOT EXISTS `lp_uuid` (`name` VARCHAR(16) NOT NULL, `uuid` VARCHAR(36) NOT NULL, PRIMARY KEY (`name`));";
private static final String CREATETABLE_USERS = "CREATE TABLE IF NOT EXISTS `lp_users` (`uuid` VARCHAR(36) NOT NULL, `name` VARCHAR(16) NOT NULL, `primary_group` VARCHAR(36) NOT NULL, `perms` TEXT NOT NULL, PRIMARY KEY (`uuid`));";
@ -40,7 +40,7 @@ public class SQLiteDatastore extends SQLDatastore {
private Connection connection = null;
private final Object connectionLock = new Object();
public SQLiteDatastore(LuckPermsPlugin plugin, File file) {
public SQLiteBacking(LuckPermsPlugin plugin, File file) {
super(plugin, "SQLite");
this.file = file;
}

View File

@ -20,7 +20,7 @@
* SOFTWARE.
*/
package me.lucko.luckperms.common.storage.methods;
package me.lucko.luckperms.common.storage.backing;
import lombok.Cleanup;
import me.lucko.luckperms.common.LuckPermsPlugin;
@ -42,8 +42,8 @@ import java.util.stream.Collectors;
import static me.lucko.luckperms.common.core.PermissionHolder.exportToLegacy;
@SuppressWarnings({"unchecked", "ResultOfMethodCallIgnored"})
public class YAMLDatastore extends FlatfileDatastore {
public YAMLDatastore(LuckPermsPlugin plugin, File pluginDir) {
public class YAMLBacking extends FlatfileBacking {
public YAMLBacking(LuckPermsPlugin plugin, File pluginDir) {
super(plugin, "Flatfile - YAML", pluginDir);
}

View File

@ -41,7 +41,7 @@ public class AbstractListener {
final UuidCache cache = plugin.getUuidCache();
if (!cache.isOnlineMode()) {
UUID uuid = plugin.getDatastore().getUUID(username);
UUID uuid = plugin.getDatastore().getUUID(username).getOrDefault(null);
if (uuid != null) {
cache.addToCache(u, uuid);
} else {
@ -51,7 +51,7 @@ public class AbstractListener {
plugin.getDatastore().saveUUIDData(username, u, Callback.empty());
}
} else {
UUID uuid = plugin.getDatastore().getUUID(username);
UUID uuid = plugin.getDatastore().getUUID(username).getOrDefault(null);
if (uuid == null) {
plugin.getApiProvider().fireEventAsync(new UserFirstLoginEvent(u, username));
}

View File

@ -0,0 +1,125 @@
/*
* 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.common.utils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import me.lucko.luckperms.common.storage.AbstractFuture;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
/**
* Holds a buffer of objects to be processed after they've been in the buffer for a given time.
* @param <T> the type of objects in the buffer
* @param <R> the type of result produced by the final process
*/
public abstract class Buffer<T, R> implements Runnable {
private static final long DEFAULT_FLUSH_TIME = 5000; // 5 seconds
private final List<BufferedObject<T, R>> buffer = new LinkedList<>();
public LPFuture<R> enqueue(@NonNull T t) {
synchronized (buffer) {
ListIterator<BufferedObject<T, R>> it = buffer.listIterator();
BufferedObject<T, R> o = null;
while (it.hasNext()) {
BufferedObject<T, R> obj = it.next();
if (obj.getObject().equals(t)) {
o = obj;
it.remove();
break;
}
}
if (o == null) {
o = new BufferedObject<>(System.currentTimeMillis(), t, new AbstractFuture<R>());
} else {
o.setBufferTime(System.currentTimeMillis());
}
buffer.add(o);
return o.getFuture();
}
}
protected abstract R dequeue(T t);
public void flush(long flushTime) {
long time = System.currentTimeMillis();
synchronized (buffer) {
ListIterator<BufferedObject<T, R>> it = buffer.listIterator(buffer.size());
while (it.hasPrevious()) {
BufferedObject<T, R> obj = it.previous();
long bufferedTime = time - obj.getBufferTime();
if (bufferedTime > flushTime) {
// Flush
R r = dequeue(obj.getObject());
obj.getFuture().complete(r);
it.remove();
}
}
}
}
@Override
public void run() {
flush(DEFAULT_FLUSH_TIME);
}
@AllArgsConstructor
private static class BufferedObject<T, R> {
@Getter
@Setter
private long bufferTime;
@Getter
private final T object;
@Getter
private final AbstractFuture<R> future;
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Buffer.BufferedObject)) return false;
final BufferedObject other = (BufferedObject) o;
return this.getObject() == null ? other.getObject() == null : this.getObject().equals(other.getObject());
}
public int hashCode() {
return 59 + (this.getObject() == null ? 43 : this.getObject().hashCode());
}
}
}

View File

@ -0,0 +1,128 @@
/*
* 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.common.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import me.lucko.luckperms.common.storage.AbstractFuture;
import java.lang.ref.WeakReference;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import java.util.function.Supplier;
@RequiredArgsConstructor
public abstract class BufferedRequest<T> {
private final long bufferTimeMillis;
private final Consumer<Runnable> executor;
private WeakReference<Processor<T>> processor = null;
@Getter
private ReentrantLock lock = new ReentrantLock();
public Future<T> request() {
lock.lock();
try {
if (processor != null) {
Processor<T> p = processor.get();
if (p != null && p.isUsable()) {
return p.getAndExtend();
}
}
Processor<T> p = new Processor<>(bufferTimeMillis, this::perform);
executor.accept(p);
processor = new WeakReference<>(p);
return p.get();
} finally {
lock.unlock();
}
}
protected abstract T perform();
@RequiredArgsConstructor
private static class Processor<R> implements Runnable {
private final long delayMillis;
private final Supplier<R> supplier;
@Getter
private boolean usable = true;
private final ReentrantLock lock = new ReentrantLock();
private long executionTime;
private final AbstractFuture<R> future = new AbstractFuture<>();
@Override
public void run() {
lock.lock();
try {
executionTime = System.currentTimeMillis() + delayMillis;
} finally {
lock.unlock();
}
while (true) {
lock.lock();
try {
if (System.currentTimeMillis() > executionTime) {
usable = false;
break;
}
} finally {
lock.unlock();
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
R result = supplier.get();
future.complete(result);
}
public Future<R> get() {
return future;
}
public Future<R> getAndExtend() {
lock.lock();
try {
executionTime = System.currentTimeMillis() + delayMillis;
} finally {
lock.unlock();
}
return future;
}
}
}

View File

@ -0,0 +1,38 @@
/*
* 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.common.utils;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public interface LPFuture<T> extends Future<T> {
default T getOrDefault(T def) {
try {
return get();
} catch (InterruptedException | ExecutionException e) {
return def;
}
}
}

View File

@ -307,6 +307,11 @@ public class LPSpongePlugin implements LuckPermsPlugin {
scheduler.createTaskBuilder().execute(r).submit(this);
}
@Override
public void doAsyncRepeating(Runnable r, long interval) {
scheduler.createTaskBuilder().async().intervalTicks(interval).execute(r).submit(this);
}
private void registerPermission(PermissionService p, String node) {
Optional<PermissionDescription.Builder> builder = p.newDescriptionBuilder(this);
if (!builder.isPresent()) return;