Some misc tidying up

This commit is contained in:
Luck 2020-12-23 12:16:14 +00:00
parent 5c44333892
commit 8dfeef9575
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
28 changed files with 792 additions and 771 deletions

View File

@ -93,8 +93,8 @@ public enum DataTypeFilter implements Predicate<DataType> {
}; };
private static final List<DataType> ALL_LIST = Collections.unmodifiableList(Arrays.asList(DataType.NORMAL, DataType.TRANSIENT)); private static final List<DataType> ALL_LIST = Collections.unmodifiableList(Arrays.asList(DataType.NORMAL, DataType.TRANSIENT));
private static final List<DataType> NORMAL_ONLY_LIST = Collections.unmodifiableList(Collections.singletonList(DataType.NORMAL)); private static final List<DataType> NORMAL_ONLY_LIST = Collections.singletonList(DataType.NORMAL);
private static final List<DataType> TRANSIENT_ONLY_LIST = Collections.unmodifiableList(Collections.singletonList(DataType.TRANSIENT)); private static final List<DataType> TRANSIENT_ONLY_LIST = Collections.singletonList(DataType.TRANSIENT);
/** /**
* Gets a {@link List} of all {@link DataType}s, filtered by the {@code predicate}. * Gets a {@link List} of all {@link DataType}s, filtered by the {@code predicate}.

View File

@ -157,24 +157,20 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
@Override @Override
public String getGroupChatPrefix(String world, String name) { public String getGroupChatPrefix(String world, String name) {
Objects.requireNonNull(name, "name"); Objects.requireNonNull(name, "name");
Group group = getGroup(name); MetaCache metaData = getGroupMetaCache(name, world);
if (group == null) { if (metaData == null) {
return null; return null;
} }
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(null, world);
MetaCache metaData = group.getCachedData().getMetaData(queryOptions);
return Strings.nullToEmpty(metaData.getPrefix(MetaCheckEvent.Origin.THIRD_PARTY_API)); return Strings.nullToEmpty(metaData.getPrefix(MetaCheckEvent.Origin.THIRD_PARTY_API));
} }
@Override @Override
public String getGroupChatSuffix(String world, String name) { public String getGroupChatSuffix(String world, String name) {
Objects.requireNonNull(name, "name"); Objects.requireNonNull(name, "name");
Group group = getGroup(name); MetaCache metaData = getGroupMetaCache(name, world);
if (group == null) { if (metaData == null) {
return null; return null;
} }
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(null, world);
MetaCache metaData = group.getCachedData().getMetaData(queryOptions);
return Strings.nullToEmpty(metaData.getSuffix(MetaCheckEvent.Origin.THIRD_PARTY_API)); return Strings.nullToEmpty(metaData.getSuffix(MetaCheckEvent.Origin.THIRD_PARTY_API));
} }
@ -202,12 +198,10 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
public String getGroupMeta(String world, String name, String key) { public String getGroupMeta(String world, String name, String key) {
Objects.requireNonNull(name, "name"); Objects.requireNonNull(name, "name");
Objects.requireNonNull(key, "key"); Objects.requireNonNull(key, "key");
Group group = getGroup(name); MetaCache metaData = getGroupMetaCache(name, world);
if (group == null) { if (metaData == null) {
return null; return null;
} }
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(null, world);
MetaCache metaData = group.getCachedData().getMetaData(queryOptions);
return metaData.getMetaValue(key, MetaCheckEvent.Origin.THIRD_PARTY_API); return metaData.getMetaValue(key, MetaCheckEvent.Origin.THIRD_PARTY_API);
} }
@ -228,6 +222,15 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
return this.plugin.getGroupManager().getByDisplayName(name); return this.plugin.getGroupManager().getByDisplayName(name);
} }
private MetaCache getGroupMetaCache(String name, String world) {
Group group = getGroup(name);
if (group == null) {
return null;
}
QueryOptions queryOptions = this.vaultPermission.getQueryOptions(null, world);
return group.getCachedData().getMetaData(queryOptions);
}
private void setChatMeta(PermissionHolder holder, ChatMetaType type, String value, String world) { private void setChatMeta(PermissionHolder holder, ChatMetaType type, String value, String world) {
// remove all prefixes/suffixes directly set on the user/group // remove all prefixes/suffixes directly set on the user/group
holder.removeIf(DataType.NORMAL, null, type.nodeType()::matches, false); holder.removeIf(DataType.NORMAL, null, type.nodeType()::matches, false);

View File

@ -1,5 +1,7 @@
test { test {
useJUnitPlatform() useJUnitPlatform {
excludeTags 'dependency_checksum'
}
} }
dependencies { dependencies {

View File

@ -100,7 +100,7 @@ public class LogDispatcher {
try { try {
this.plugin.getStorage().logAction(entry).get(); this.plugin.getStorage().logAction(entry).get();
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().warn("Error whilst storing action", e); this.plugin.getLogger().warn("Error whilst storing action", e);
} }
} }

View File

@ -37,7 +37,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Objects; import java.util.Objects;
@SuppressWarnings({"unchecked", "rawtypes"})
public class ApiPlayerAdapter<S, P extends S> implements PlayerAdapter<P> { public class ApiPlayerAdapter<S, P extends S> implements PlayerAdapter<P> {
private final UserManager<?> userManager; private final UserManager<?> userManager;
private final ContextManager<S, P> contextManager; private final ContextManager<S, P> contextManager;

View File

@ -263,7 +263,7 @@ public abstract class Exporter implements Runnable {
} }
try { try {
String pasteId = this.plugin.getBytebin().postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); String pasteId = this.plugin.getBytebin().postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE).key();
this.log.getListeners().forEach(l -> Message.EXPORT_WEB_SUCCESS.send(l, pasteId, this.label)); this.log.getListeners().forEach(l -> Message.EXPORT_WEB_SUCCESS.send(l, pasteId, this.label));
} catch (UnsuccessfulRequestException e) { } catch (UnsuccessfulRequestException e) {
this.log.getListeners().forEach(l -> Message.HTTP_REQUEST_FAILURE.send(l, e.getResponse().code(), e.getResponse().message())); this.log.getListeners().forEach(l -> Message.HTTP_REQUEST_FAILURE.send(l, e.getResponse().code(), e.getResponse().message()));

View File

@ -124,7 +124,7 @@ public final class ConfigKeys {
*/ */
public static final ConfigKey<ContextSatisfyMode> CONTEXT_SATISFY_MODE = key(c -> { public static final ConfigKey<ContextSatisfyMode> CONTEXT_SATISFY_MODE = key(c -> {
String value = c.getString("context-satisfy-mode", "at-least-one-value-per-key"); String value = c.getString("context-satisfy-mode", "at-least-one-value-per-key");
if (value.toLowerCase().equals("all-values-per-key")) { if (value.equalsIgnoreCase("all-values-per-key")) {
return ContextSatisfyMode.ALL_VALUES_PER_KEY; return ContextSatisfyMode.ALL_VALUES_PER_KEY;
} }
return ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY; return ContextSatisfyMode.AT_LEAST_ONE_VALUE_PER_KEY;
@ -199,11 +199,11 @@ public final class ConfigKeys {
String option = PRIMARY_GROUP_CALCULATION_METHOD.get(c); String option = PRIMARY_GROUP_CALCULATION_METHOD.get(c);
switch (option) { switch (option) {
case "stored": case "stored":
return (Function<User, PrimaryGroupHolder>) PrimaryGroupHolder.Stored::new; return PrimaryGroupHolder.Stored::new;
case "parents-by-weight": case "parents-by-weight":
return (Function<User, PrimaryGroupHolder>) PrimaryGroupHolder.ParentsByWeight::new; return PrimaryGroupHolder.ParentsByWeight::new;
default: default:
return (Function<User, PrimaryGroupHolder>) PrimaryGroupHolder.AllParentsByWeight::new; return PrimaryGroupHolder.AllParentsByWeight::new;
} }
})); }));

View File

@ -88,7 +88,7 @@ public abstract class ConfigurateConfigAdapter implements ConfigurationAdapter {
@Override @Override
public List<String> getStringList(String path, List<String> def) { public List<String> getStringList(String path, List<String> def) {
ConfigurationNode node = resolvePath(path); ConfigurationNode node = resolvePath(path);
if (node.isVirtual() || !node.hasListChildren()) { if (node.isVirtual() || !node.isList()) {
return def; return def;
} }
@ -98,7 +98,7 @@ public abstract class ConfigurateConfigAdapter implements ConfigurationAdapter {
@Override @Override
public List<String> getKeys(String path, List<String> def) { public List<String> getKeys(String path, List<String> def) {
ConfigurationNode node = resolvePath(path); ConfigurationNode node = resolvePath(path);
if (node.isVirtual() || !node.hasMapChildren()) { if (node.isVirtual() || !node.isMap()) {
return def; return def;
} }

View File

@ -34,7 +34,6 @@ import net.luckperms.api.context.ContextSet;
import net.luckperms.api.context.MutableContextSet; import net.luckperms.api.context.MutableContextSet;
import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.SimpleConfigurationNode;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -45,7 +44,7 @@ public final class ContextSetConfigurateSerializer {
private ContextSetConfigurateSerializer() {} private ContextSetConfigurateSerializer() {}
public static ConfigurationNode serializeContextSet(ContextSet contextSet) { public static ConfigurationNode serializeContextSet(ContextSet contextSet) {
ConfigurationNode data = SimpleConfigurationNode.root(); ConfigurationNode data = ConfigurationNode.root();
Map<String, Set<String>> map = contextSet.toMap(); Map<String, Set<String>> map = contextSet.toMap();
for (Map.Entry<String, Set<String>> entry : map.entrySet()) { for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
@ -63,7 +62,7 @@ public final class ContextSetConfigurateSerializer {
} }
public static ContextSet deserializeContextSet(ConfigurationNode data) { public static ContextSet deserializeContextSet(ConfigurationNode data) {
Preconditions.checkArgument(data.hasMapChildren()); Preconditions.checkArgument(data.isMap());
Map<Object, ? extends ConfigurationNode> dataMap = data.getChildrenMap(); Map<Object, ? extends ConfigurationNode> dataMap = data.getChildrenMap();
if (dataMap.isEmpty()) { if (dataMap.isEmpty()) {
@ -75,9 +74,8 @@ public final class ContextSetConfigurateSerializer {
String k = e.getKey().toString(); String k = e.getKey().toString();
ConfigurationNode v = e.getValue(); ConfigurationNode v = e.getValue();
if (v.hasListChildren()) { if (v.isList()) {
List<? extends ConfigurationNode> values = v.getChildrenList(); for (ConfigurationNode value : v.getChildrenList()) {
for (ConfigurationNode value : values) {
map.add(k, value.getString()); map.add(k, value.getString());
} }
} else { } else {

View File

@ -340,37 +340,4 @@ public enum Dependency {
} }
} }
/*
public static void main(String[] args) {
Dependency[] dependencies = values();
DependencyRepository[] repos = DependencyRepository.values();
java.util.concurrent.ExecutorService pool = java.util.concurrent.Executors.newCachedThreadPool();
for (Dependency dependency : dependencies) {
for (DependencyRepository repo : repos) {
pool.submit(() -> {
try {
byte[] hash = createDigest().digest(repo.downloadRaw(dependency));
if (!dependency.checksumMatches(hash)) {
System.out.println("NO MATCH - " + repo.name() + " - " + dependency.name() + ": " + Base64.getEncoder().encodeToString(hash));
} else {
System.out.println("OK - " + repo.name() + " - " + dependency.name());
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
pool.shutdown();
try {
pool.awaitTermination(1, java.util.concurrent.TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
*/
} }

View File

@ -83,11 +83,10 @@ public class BytebinClient extends AbstractHttpClient {
* *
* @param buf the compressed content * @param buf the compressed content
* @param contentType the type of the content * @param contentType the type of the content
* @param allowModification if the paste should be modifiable
* @return the key of the resultant content * @return the key of the resultant content
* @throws IOException if an error occurs * @throws IOException if an error occurs
*/ */
public Content postContent(byte[] buf, MediaType contentType, boolean allowModification) throws IOException, UnsuccessfulRequestException { public Content postContent(byte[] buf, MediaType contentType) throws IOException, UnsuccessfulRequestException {
RequestBody body = RequestBody.create(contentType, buf); RequestBody body = RequestBody.create(contentType, buf);
Request.Builder requestBuilder = new Request.Builder() Request.Builder requestBuilder = new Request.Builder()
@ -95,53 +94,15 @@ public class BytebinClient extends AbstractHttpClient {
.header("User-Agent", this.userAgent) .header("User-Agent", this.userAgent)
.header("Content-Encoding", "gzip"); .header("Content-Encoding", "gzip");
if (allowModification) {
requestBuilder.header("Allow-Modification", "true");
}
Request request = requestBuilder.post(body).build(); Request request = requestBuilder.post(body).build();
try (Response response = makeHttpRequest(request)) { try (Response response = makeHttpRequest(request)) {
String key = response.header("Location"); String key = response.header("Location");
if (key == null) { if (key == null) {
throw new IllegalStateException("Key not returned"); throw new IllegalStateException("Key not returned");
} }
if (allowModification) {
String modificationKey = response.header("Modification-Key");
if (modificationKey == null) {
throw new IllegalStateException("Modification key not returned");
}
return new Content(key, modificationKey);
} else {
return new Content(key); return new Content(key);
} }
} }
}
/**
* PUTs modified GZIP compressed content to bytebin in place of existing content.
*
* @param existingContent the existing content
* @param buf the compressed content to put
* @param contentType the type of the content
* @throws IOException if an error occurs
*/
public void modifyContent(Content existingContent, byte[] buf, MediaType contentType) throws IOException, UnsuccessfulRequestException {
if (!existingContent.modifiable) {
throw new IllegalArgumentException("Existing content is not modifiable");
}
RequestBody body = RequestBody.create(contentType, buf);
Request.Builder requestBuilder = new Request.Builder()
.url(this.url + existingContent.key())
.header("User-Agent", this.userAgent)
.header("Content-Encoding", "gzip")
.header("Modification-Key", existingContent.modificationKey);
Request request = requestBuilder.put(body).build();
makeHttpRequest(request).close();
}
/** /**
* GETs json content from bytebin * GETs json content from bytebin
@ -173,19 +134,9 @@ public class BytebinClient extends AbstractHttpClient {
public static final class Content { public static final class Content {
private final String key; private final String key;
private final boolean modifiable;
private final String modificationKey;
Content(String key) { Content(String key) {
this.key = key; this.key = key;
this.modifiable = false;
this.modificationKey = null;
}
Content(String key, String modificationKey) {
this.key = key;
this.modifiable = true;
this.modificationKey = modificationKey;
} }
public String key() { public String key() {

View File

@ -320,10 +320,5 @@ public class TranslationRepository {
} }
return res; return res;
} }
@Override
public void close() throws IOException {
super.close();
}
} }
} }

View File

@ -64,7 +64,7 @@ public class RedisMessenger implements Messenger {
this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, password, ssl); this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, password, ssl);
this.sub = new Subscription(this); this.sub = new Subscription(this);
this.plugin.getBootstrap().getScheduler().executeAsync(sub); this.plugin.getBootstrap().getScheduler().executeAsync(this.sub);
} }
@Override @Override
@ -95,13 +95,13 @@ public class RedisMessenger implements Messenger {
while (!Thread.interrupted() && !this.parent.jedisPool.isClosed()) { while (!Thread.interrupted() && !this.parent.jedisPool.isClosed()) {
try (Jedis jedis = this.parent.jedisPool.getResource()) { try (Jedis jedis = this.parent.jedisPool.getResource()) {
if (wasBroken) { if (wasBroken) {
parent.plugin.getLogger().info("Redis pubsub connection re-established"); this.parent.plugin.getLogger().info("Redis pubsub connection re-established");
wasBroken = false; wasBroken = false;
} }
jedis.subscribe(this, CHANNEL); jedis.subscribe(this, CHANNEL);
} catch (Exception e) { } catch (Exception e) {
wasBroken = true; wasBroken = true;
parent.plugin.getLogger().warn("Redis pubsub connection dropped, trying to re-open the connection: " + e.getMessage()); this.parent.plugin.getLogger().warn("Redis pubsub connection dropped, trying to re-open the connection: " + e.getMessage());
try { try {
unsubscribe(); unsubscribe();
} catch (Exception ignored) { } catch (Exception ignored) {

View File

@ -25,9 +25,7 @@
package me.lucko.luckperms.common.storage.implementation.file; package me.lucko.luckperms.common.storage.implementation.file;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import me.lucko.luckperms.common.actionlog.Log; import me.lucko.luckperms.common.actionlog.Log;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
@ -48,7 +46,6 @@ import me.lucko.luckperms.common.storage.implementation.StorageImplementation;
import me.lucko.luckperms.common.storage.implementation.file.loader.ConfigurateLoader; import me.lucko.luckperms.common.storage.implementation.file.loader.ConfigurateLoader;
import me.lucko.luckperms.common.storage.implementation.file.loader.JsonLoader; import me.lucko.luckperms.common.storage.implementation.file.loader.JsonLoader;
import me.lucko.luckperms.common.storage.implementation.file.loader.YamlLoader; import me.lucko.luckperms.common.storage.implementation.file.loader.YamlLoader;
import me.lucko.luckperms.common.util.ImmutableCollectors;
import me.lucko.luckperms.common.util.MoreFiles; import me.lucko.luckperms.common.util.MoreFiles;
import net.luckperms.api.actionlog.Action; import net.luckperms.api.actionlog.Action;
@ -63,53 +60,51 @@ import net.luckperms.api.node.types.InheritanceNode;
import net.luckperms.api.node.types.MetaNode; import net.luckperms.api.node.types.MetaNode;
import ninja.leaping.configurate.ConfigurationNode; import ninja.leaping.configurate.ConfigurationNode;
import ninja.leaping.configurate.SimpleConfigurationNode;
import ninja.leaping.configurate.Types; import ninja.leaping.configurate.Types;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Function;
/** /**
* Abstract implementation using configurate {@link ConfigurationNode}s to serialize and deserialize * Abstract storage implementation using Configurate {@link ConfigurationNode}s to
* data. * serialize and deserialize data.
*/ */
public abstract class AbstractConfigurateStorage implements StorageImplementation { public abstract class AbstractConfigurateStorage implements StorageImplementation {
/** The plugin instance */
protected final LuckPermsPlugin plugin; protected final LuckPermsPlugin plugin;
/** The name of this implementation */
private final String implementationName; private final String implementationName;
// the loader responsible for i/o /** The Configurate loader used to read/write data */
protected final ConfigurateLoader loader; protected final ConfigurateLoader loader;
// the name of the data directory /* The data directory */
private final String dataDirectoryName;
// the data directory
protected Path dataDirectory; protected Path dataDirectory;
private final String dataDirectoryName;
// the uuid cache instance /* The UUID cache */
private final FileUuidCache uuidCache = new FileUuidCache(); private final FileUuidCache uuidCache;
// the action logger instance private Path uuidCacheFile;
/** The action logger */
private final FileActionLogger actionLogger; private final FileActionLogger actionLogger;
// the file used to store uuid data
private Path uuidDataFile;
protected AbstractConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String dataDirectoryName) { protected AbstractConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String dataDirectoryName) {
this.plugin = plugin; this.plugin = plugin;
this.implementationName = implementationName; this.implementationName = implementationName;
this.loader = loader; this.loader = loader;
this.dataDirectoryName = dataDirectoryName; this.dataDirectoryName = dataDirectoryName;
this.uuidCache = new FileUuidCache();
this.actionLogger = new FileActionLogger(plugin); this.actionLogger = new FileActionLogger(plugin);
} }
@ -143,27 +138,23 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
*/ */
protected abstract void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException; protected abstract void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException;
// used to report i/o exceptions which took place in a specific file
protected RuntimeException reportException(String file, Exception ex) throws RuntimeException {
this.plugin.getLogger().warn("Exception thrown whilst performing i/o: " + file, ex);
Throwables.throwIfUnchecked(ex);
throw new RuntimeException(ex);
}
@Override @Override
public void init() throws IOException { public void init() throws IOException {
// init the data directory and ensure it exists
this.dataDirectory = this.plugin.getBootstrap().getDataDirectory().resolve(this.dataDirectoryName); this.dataDirectory = this.plugin.getBootstrap().getDataDirectory().resolve(this.dataDirectoryName);
MoreFiles.createDirectoriesIfNotExists(this.dataDirectory); MoreFiles.createDirectoriesIfNotExists(this.dataDirectory);
this.uuidDataFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("uuidcache.txt")); // setup the uuid cache
this.uuidCache.load(this.uuidDataFile); this.uuidCacheFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("uuidcache.txt"));
this.uuidCache.load(this.uuidCacheFile);
// setup the action logger
this.actionLogger.init(this.dataDirectory.resolve("actions.txt"), this.dataDirectory.resolve("actions.json")); this.actionLogger.init(this.dataDirectory.resolve("actions.txt"), this.dataDirectory.resolve("actions.json"));
} }
@Override @Override
public void shutdown() { public void shutdown() {
this.uuidCache.save(this.uuidDataFile); this.uuidCache.save(this.uuidCacheFile);
this.actionLogger.flush(); this.actionLogger.flush();
} }
@ -177,29 +168,19 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
return this.actionLogger.getLog(); return this.actionLogger.getLog();
} }
protected boolean processBulkUpdate(BulkUpdate bulkUpdate, ConfigurationNode node, HolderType holderType) {
Set<Node> nodes = readNodes(node);
Set<Node> results = bulkUpdate.apply(nodes, holderType);
if (results == null) {
return false;
}
writeNodes(node, results);
return true;
}
@Override @Override
public User loadUser(UUID uniqueId, String username) { public User loadUser(UUID uniqueId, String username) throws IOException {
User user = this.plugin.getUserManager().getOrMake(uniqueId, username); User user = this.plugin.getUserManager().getOrMake(uniqueId, username);
try { try {
ConfigurationNode object = readFile(StorageLocation.USER, uniqueId.toString()); ConfigurationNode file = readFile(StorageLocation.USERS, uniqueId.toString());
if (object != null) { if (file != null) {
String name = object.getNode("name").getString(); String name = file.getNode("name").getString();
user.getPrimaryGroup().setStoredValue(object.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").getString()); String primaryGroup = file.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").getString();
user.getPrimaryGroup().setStoredValue(primaryGroup);
user.setUsername(name, true); user.setUsername(name, true);
user.loadNodesFromStorage(readNodes(object)); user.loadNodesFromStorage(readNodes(file));
this.plugin.getUserManager().giveDefaultIfNeeded(user); this.plugin.getUserManager().giveDefaultIfNeeded(user);
boolean updatedUsername = user.getUsername().isPresent() && (name == null || !user.getUsername().get().equalsIgnoreCase(name)); boolean updatedUsername = user.getUsername().isPresent() && (name == null || !user.getUsername().get().equalsIgnoreCase(name));
@ -214,166 +195,159 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(uniqueId.toString(), e); throw new FileIOException(uniqueId.toString(), e);
} }
return user; return user;
} }
@Override @Override
public void saveUser(User user) { public void saveUser(User user) throws IOException {
user.normalData().discardChanges(); user.normalData().discardChanges();
try { try {
if (!this.plugin.getUserManager().isNonDefaultUser(user)) { if (!this.plugin.getUserManager().isNonDefaultUser(user)) {
saveFile(StorageLocation.USER, user.getUniqueId().toString(), null); saveFile(StorageLocation.USERS, user.getUniqueId().toString(), null);
} else { } else {
ConfigurationNode data = SimpleConfigurationNode.root(); ConfigurationNode file = ConfigurationNode.root();
if (this instanceof SeparatedConfigurateStorage) { if (this instanceof SeparatedConfigurateStorage) {
data.getNode("uuid").setValue(user.getUniqueId().toString()); file.getNode("uuid").setValue(user.getUniqueId().toString());
} }
data.getNode("name").setValue(user.getUsername().orElse("null"));
data.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").setValue(user.getPrimaryGroup().getStoredValue().orElse(GroupManager.DEFAULT_GROUP_NAME));
writeNodes(data, user.normalData().asList()); String name = user.getUsername().orElse("null");
saveFile(StorageLocation.USER, user.getUniqueId().toString(), data); String primaryGroup = user.getPrimaryGroup().getStoredValue().orElse(GroupManager.DEFAULT_GROUP_NAME);
file.getNode("name").setValue(name);
file.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").setValue(primaryGroup);
writeNodes(file, user.normalData().asList());
saveFile(StorageLocation.USERS, user.getUniqueId().toString(), file);
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(user.getUniqueId().toString(), e); throw new FileIOException(user.getUniqueId().toString(), e);
} }
} }
@Override @Override
public Group createAndLoadGroup(String name) { public Group createAndLoadGroup(String name) throws IOException {
Group group = this.plugin.getGroupManager().getOrMake(name); Group group = this.plugin.getGroupManager().getOrMake(name);
try { try {
ConfigurationNode object = readFile(StorageLocation.GROUP, name); ConfigurationNode file = readFile(StorageLocation.GROUPS, name);
if (object != null) { if (file != null) {
group.loadNodesFromStorage(readNodes(object)); group.loadNodesFromStorage(readNodes(file));
} else { } else {
ConfigurationNode data = SimpleConfigurationNode.root(); file = ConfigurationNode.root();
if (this instanceof SeparatedConfigurateStorage) { if (this instanceof SeparatedConfigurateStorage) {
data.getNode("name").setValue(group.getName()); file.getNode("name").setValue(group.getName());
} }
writeNodes(data, group.normalData().asList()); writeNodes(file, group.normalData().asList());
saveFile(StorageLocation.GROUP, name, data); saveFile(StorageLocation.GROUPS, name, file);
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(name, e); throw new FileIOException(name, e);
} }
return group; return group;
} }
@Override @Override
public Optional<Group> loadGroup(String name) { public Optional<Group> loadGroup(String name) throws IOException {
try { try {
ConfigurationNode object = readFile(StorageLocation.GROUP, name); ConfigurationNode file = readFile(StorageLocation.GROUPS, name);
if (file == null) {
if (object == null) {
return Optional.empty(); return Optional.empty();
} }
Group group = this.plugin.getGroupManager().getOrMake(name); Group group = this.plugin.getGroupManager().getOrMake(name);
group.loadNodesFromStorage(readNodes(object)); group.loadNodesFromStorage(readNodes(file));
return Optional.of(group); return Optional.of(group);
} catch (Exception e) { } catch (Exception e) {
throw reportException(name, e); throw new FileIOException(name, e);
} }
} }
@Override @Override
public void saveGroup(Group group) { public void saveGroup(Group group) throws IOException {
group.normalData().discardChanges(); group.normalData().discardChanges();
try { try {
ConfigurationNode data = SimpleConfigurationNode.root(); ConfigurationNode file = ConfigurationNode.root();
if (this instanceof SeparatedConfigurateStorage) { if (this instanceof SeparatedConfigurateStorage) {
data.getNode("name").setValue(group.getName()); file.getNode("name").setValue(group.getName());
} }
writeNodes(data, group.normalData().asList()); writeNodes(file, group.normalData().asList());
saveFile(StorageLocation.GROUP, group.getName(), data); saveFile(StorageLocation.GROUPS, group.getName(), file);
} catch (Exception e) { } catch (Exception e) {
throw reportException(group.getName(), e); throw new FileIOException(group.getName(), e);
} }
} }
@Override @Override
public void deleteGroup(Group group) { public void deleteGroup(Group group) throws IOException {
try { try {
saveFile(StorageLocation.GROUP, group.getName(), null); saveFile(StorageLocation.GROUPS, group.getName(), null);
} catch (Exception e) { } catch (Exception e) {
throw reportException(group.getName(), e); throw new FileIOException(group.getName(), e);
} }
this.plugin.getGroupManager().unload(group.getName()); this.plugin.getGroupManager().unload(group.getName());
} }
@Override @Override
public Track createAndLoadTrack(String name) { public Track createAndLoadTrack(String name) throws IOException {
Track track = this.plugin.getTrackManager().getOrMake(name); Track track = this.plugin.getTrackManager().getOrMake(name);
try { try {
ConfigurationNode object = readFile(StorageLocation.TRACK, name); ConfigurationNode file = readFile(StorageLocation.TRACKS, name);
if (file != null) {
if (object != null) { track.setGroups(file.getNode("groups").getList(Types::asString));
List<String> groups = object.getNode("groups").getChildrenList().stream()
.map(ConfigurationNode::getString)
.collect(ImmutableCollectors.toList());
track.setGroups(groups);
} else { } else {
ConfigurationNode data = SimpleConfigurationNode.root(); file = ConfigurationNode.root();
if (this instanceof SeparatedConfigurateStorage) { if (this instanceof SeparatedConfigurateStorage) {
data.getNode("name").setValue(name); file.getNode("name").setValue(name);
} }
data.getNode("groups").setValue(track.getGroups()); file.getNode("groups").setValue(track.getGroups());
saveFile(StorageLocation.TRACK, name, data); saveFile(StorageLocation.TRACKS, name, file);
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(name, e); throw new FileIOException(name, e);
} }
return track; return track;
} }
@Override @Override
public Optional<Track> loadTrack(String name) { public Optional<Track> loadTrack(String name) throws IOException {
try { try {
ConfigurationNode object = readFile(StorageLocation.TRACK, name); ConfigurationNode file = readFile(StorageLocation.TRACKS, name);
if (file == null) {
if (object == null) {
return Optional.empty(); return Optional.empty();
} }
Track track = this.plugin.getTrackManager().getOrMake(name); Track track = this.plugin.getTrackManager().getOrMake(name);
List<String> groups = object.getNode("groups").getChildrenList().stream() track.setGroups(file.getNode("groups").getList(Types::asString));
.map(ConfigurationNode::getString)
.collect(ImmutableCollectors.toList());
track.setGroups(groups);
return Optional.of(track); return Optional.of(track);
} catch (Exception e) { } catch (Exception e) {
throw reportException(name, e); throw new FileIOException(name, e);
} }
} }
@Override @Override
public void saveTrack(Track track) { public void saveTrack(Track track) throws IOException {
try { try {
ConfigurationNode data = SimpleConfigurationNode.root(); ConfigurationNode file = ConfigurationNode.root();
if (this instanceof SeparatedConfigurateStorage) { if (this instanceof SeparatedConfigurateStorage) {
data.getNode("name").setValue(track.getName()); file.getNode("name").setValue(track.getName());
} }
data.getNode("groups").setValue(track.getGroups()); file.getNode("groups").setValue(track.getGroups());
saveFile(StorageLocation.TRACK, track.getName(), data); saveFile(StorageLocation.TRACKS, track.getName(), file);
} catch (Exception e) { } catch (Exception e) {
throw reportException(track.getName(), e); throw new FileIOException(track.getName(), e);
} }
} }
@Override @Override
public void deleteTrack(Track track) { public void deleteTrack(Track track) throws IOException {
try { try {
saveFile(StorageLocation.TRACK, track.getName(), null); saveFile(StorageLocation.TRACKS, track.getName(), null);
} catch (Exception e) { } catch (Exception e) {
throw reportException(track.getName(), e); throw new FileIOException(track.getName(), e);
} }
this.plugin.getTrackManager().unload(track.getName()); this.plugin.getTrackManager().unload(track.getName());
} }
@ -398,71 +372,54 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
return this.uuidCache.lookupUsername(uniqueId); return this.uuidCache.lookupUsername(uniqueId);
} }
protected boolean processBulkUpdate(BulkUpdate bulkUpdate, ConfigurationNode node, HolderType holderType) {
Set<Node> nodes = readNodes(node);
Set<Node> results = bulkUpdate.apply(nodes, holderType);
if (results == null) {
return false;
}
writeNodes(node, results);
return true;
}
private static ImmutableContextSet readContexts(ConfigurationNode attributes) { private static ImmutableContextSet readContexts(ConfigurationNode attributes) {
ImmutableContextSet.Builder contextBuilder = new ImmutableContextSetImpl.BuilderImpl(); ImmutableContextSet.Builder contextBuilder = new ImmutableContextSetImpl.BuilderImpl();
ConfigurationNode contextMap = attributes.getNode("context"); ConfigurationNode contextMap = attributes.getNode("context");
if (!contextMap.isVirtual() && contextMap.hasMapChildren()) { if (!contextMap.isVirtual() && contextMap.isMap()) {
contextBuilder.addAll(ContextSetConfigurateSerializer.deserializeContextSet(contextMap)); contextBuilder.addAll(ContextSetConfigurateSerializer.deserializeContextSet(contextMap));
} }
String server = attributes.getNode("server").getString("global"); String server = attributes.getNode("server").getString("global");
if (!server.equals("global")) {
contextBuilder.add(DefaultContextKeys.SERVER_KEY, server); contextBuilder.add(DefaultContextKeys.SERVER_KEY, server);
}
String world = attributes.getNode("world").getString("global"); String world = attributes.getNode("world").getString("global");
if (!world.equals("global")) {
contextBuilder.add(DefaultContextKeys.WORLD_KEY, world); contextBuilder.add(DefaultContextKeys.WORLD_KEY, world);
}
return contextBuilder.build(); return contextBuilder.build();
} }
private static Node readMetaAttributes(ConfigurationNode attributes, Function<ConfigurationNode, NodeBuilder<?, ?>> permissionFunction) { private static Node readAttributes(NodeBuilder<?, ?> builder, ConfigurationNode attributes) {
long expiryVal = attributes.getNode("expiry").getLong(0L); long expiryVal = attributes.getNode("expiry").getLong(0L);
Instant expiry = expiryVal == 0L ? null : Instant.ofEpochSecond(expiryVal); Instant expiry = expiryVal == 0L ? null : Instant.ofEpochSecond(expiryVal);
ImmutableContextSet context = readContexts(attributes); ImmutableContextSet context = readContexts(attributes);
return permissionFunction.apply(attributes) return builder.expiry(expiry).context(context).build();
.expiry(expiry)
.context(context)
.build();
} }
private static Collection<Node> readAttributes(ConfigurationNode attributes, String permission) { private static final class NodeEntry {
boolean value = attributes.getNode("value").getBoolean(true); final String key;
long expiryVal = attributes.getNode("expiry").getLong(0L); final ConfigurationNode attributes;
Instant expiry = expiryVal == 0L ? null : Instant.ofEpochSecond(expiryVal);
ImmutableContextSet context = readContexts(attributes);
ConfigurationNode batchAttribute = attributes.getNode("permissions"); private NodeEntry(String key, ConfigurationNode attributes) {
if (permission.startsWith("luckperms.batch") && !batchAttribute.isVirtual() && batchAttribute.hasListChildren()) { this.key = key;
List<Node> nodes = new ArrayList<>(); this.attributes = attributes;
for (ConfigurationNode element : batchAttribute.getChildrenList()) {
Node node = NodeBuilders.determineMostApplicable(element.getString())
.value(value)
.expiry(expiry)
.context(context)
.build();
nodes.add(node);
}
return nodes;
} else {
Node node = NodeBuilders.determineMostApplicable(permission)
.value(value)
.expiry(expiry)
.context(context)
.build();
return Collections.singleton(node);
} }
} }
private static Map.Entry<String, ConfigurationNode> parseEntry(ConfigurationNode appended, String keyFieldName) { private static NodeEntry parseNode(ConfigurationNode configNode, String keyFieldName) {
if (!appended.hasMapChildren()) { Map<Object, ? extends ConfigurationNode> children = configNode.getChildrenMap();
return null;
}
Map<Object, ? extends ConfigurationNode> children = appended.getChildrenMap();
if (children.isEmpty()) { if (children.isEmpty()) {
return null; return null;
} }
@ -476,88 +433,93 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
ConfigurationNode attributes = entry.getValue(); ConfigurationNode attributes = entry.getValue();
if (!permission.equals(keyFieldName)) { if (!permission.equals(keyFieldName)) {
return Maps.immutableEntry(permission, attributes); return new NodeEntry(permission, attributes);
} }
} }
} }
// assume 'appended' is the actual entry. // assume 'configNode' is the actual entry.
String permission = children.get(keyFieldName).getString(null); String permission = children.get(keyFieldName).getString(null);
if (permission == null) { if (permission == null) {
return null; return null;
} }
return Maps.immutableEntry(permission, appended); return new NodeEntry(permission, configNode);
} }
protected static Set<Node> readNodes(ConfigurationNode data) { protected static Set<Node> readNodes(ConfigurationNode data) {
Set<Node> nodes = new HashSet<>(); Set<Node> nodes = new HashSet<>();
if (data.getNode("permissions").hasListChildren()) { for (ConfigurationNode appended : data.getNode("permissions").getChildrenList()) {
List<? extends ConfigurationNode> children = data.getNode("permissions").getChildrenList();
for (ConfigurationNode appended : children) {
String plainValue = appended.getValue(Types::strictAsString); String plainValue = appended.getValue(Types::strictAsString);
if (plainValue != null) { if (plainValue != null) {
nodes.add(NodeBuilders.determineMostApplicable(plainValue).build()); nodes.add(NodeBuilders.determineMostApplicable(plainValue).build());
continue; continue;
} }
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "permission"); NodeEntry entry = parseNode(appended, "permission");
if (entry == null) { if (entry == null) {
continue; continue;
} }
nodes.addAll(readAttributes(entry.getValue(), entry.getKey()));
} nodes.add(readAttributes(
NodeBuilders.determineMostApplicable(entry.key).value(entry.attributes.getNode("value").getBoolean(true)),
entry.attributes
));
} }
if (data.getNode("parents").hasListChildren()) { for (ConfigurationNode appended : data.getNode("parents").getChildrenList()) {
List<? extends ConfigurationNode> children = data.getNode("parents").getChildrenList();
for (ConfigurationNode appended : children) {
String plainValue = appended.getValue(Types::strictAsString); String plainValue = appended.getValue(Types::strictAsString);
if (plainValue != null) { if (plainValue != null) {
nodes.add(Inheritance.builder(plainValue).build()); nodes.add(Inheritance.builder(plainValue).build());
continue; continue;
} }
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "group"); NodeEntry entry = parseNode(appended, "group");
if (entry == null) { if (entry == null) {
continue; continue;
} }
nodes.add(readMetaAttributes(entry.getValue(), c -> Inheritance.builder(entry.getKey())));
}
}
if (data.getNode("prefixes").hasListChildren()) { nodes.add(readAttributes(
List<? extends ConfigurationNode> children = data.getNode("prefixes").getChildrenList(); Inheritance.builder(entry.key),
for (ConfigurationNode appended : children) { entry.attributes
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "prefix"); ));
if (entry == null) {
continue;
}
nodes.add(readMetaAttributes(entry.getValue(), c -> Prefix.builder(entry.getKey(), c.getNode("priority").getInt(0))));
}
} }
if (data.getNode("suffixes").hasListChildren()) { for (ConfigurationNode appended : data.getNode("prefixes").getChildrenList()) {
List<? extends ConfigurationNode> children = data.getNode("suffixes").getChildrenList(); NodeEntry entry = parseNode(appended, "prefix");
for (ConfigurationNode appended : children) {
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "suffix");
if (entry == null) { if (entry == null) {
continue; continue;
} }
nodes.add(readMetaAttributes(entry.getValue(), c -> Suffix.builder(entry.getKey(), c.getNode("priority").getInt(0))));
}
}
if (data.getNode("meta").hasListChildren()) { nodes.add(readAttributes(
List<? extends ConfigurationNode> children = data.getNode("meta").getChildrenList(); Prefix.builder(entry.key, entry.attributes.getNode("priority").getInt(0)),
for (ConfigurationNode appended : children) { entry.attributes
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "key"); ));
}
for (ConfigurationNode appended : data.getNode("suffixes").getChildrenList()) {
NodeEntry entry = parseNode(appended, "suffix");
if (entry == null) { if (entry == null) {
continue; continue;
} }
nodes.add(readMetaAttributes(entry.getValue(), c -> Meta.builder(entry.getKey(), c.getNode("value").getString("null"))));
nodes.add(readAttributes(
Suffix.builder(entry.key, entry.attributes.getNode("priority").getInt(0)),
entry.attributes
));
} }
for (ConfigurationNode appended : data.getNode("meta").getChildrenList()) {
NodeEntry entry = parseNode(appended, "key");
if (entry == null) {
continue;
}
nodes.add(readAttributes(
Meta.builder(entry.key, entry.attributes.getNode("value").getString("null")),
entry.attributes
));
} }
return nodes; return nodes;
@ -582,7 +544,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
} }
private void appendNode(ConfigurationNode base, String key, ConfigurationNode attributes, String keyFieldName) { private void appendNode(ConfigurationNode base, String key, ConfigurationNode attributes, String keyFieldName) {
ConfigurationNode appended = base.getAppendedNode(); ConfigurationNode appended = base.appendListNode();
if (this.loader instanceof YamlLoader) { if (this.loader instanceof YamlLoader) {
// create a map node with a single entry of key --> attributes // create a map node with a single entry of key --> attributes
appended.getNode(key).setValue(attributes); appended.getNode(key).setValue(attributes);
@ -594,7 +556,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
} }
private void writeNodes(ConfigurationNode to, Collection<Node> nodes) { private void writeNodes(ConfigurationNode to, Collection<Node> nodes) {
ConfigurationNode permissionsSection = SimpleConfigurationNode.root(); ConfigurationNode permissionsSection = ConfigurationNode.root();
// ensure for CombinedConfigurateStorage that there's at least *something* // ensure for CombinedConfigurateStorage that there's at least *something*
// to save to the file even if it's just an empty list. // to save to the file even if it's just an empty list.
@ -602,20 +564,20 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
permissionsSection.setValue(Collections.emptyList()); permissionsSection.setValue(Collections.emptyList());
} }
ConfigurationNode parentsSection = SimpleConfigurationNode.root(); ConfigurationNode parentsSection = ConfigurationNode.root();
ConfigurationNode prefixesSection = SimpleConfigurationNode.root(); ConfigurationNode prefixesSection = ConfigurationNode.root();
ConfigurationNode suffixesSection = SimpleConfigurationNode.root(); ConfigurationNode suffixesSection = ConfigurationNode.root();
ConfigurationNode metaSection = SimpleConfigurationNode.root(); ConfigurationNode metaSection = ConfigurationNode.root();
for (Node n : nodes) { for (Node n : nodes) {
// just add a string to the list. // just add a string to the list.
if (this.loader instanceof YamlLoader && isPlain(n)) { if (this.loader instanceof YamlLoader && isPlain(n)) {
if (n instanceof InheritanceNode) { if (n instanceof InheritanceNode) {
parentsSection.getAppendedNode().setValue(((InheritanceNode) n).getGroupName()); parentsSection.appendListNode().setValue(((InheritanceNode) n).getGroupName());
continue; continue;
} }
if (!NodeType.META_OR_CHAT_META.matches(n)) { if (!NodeType.META_OR_CHAT_META.matches(n)) {
permissionsSection.getAppendedNode().setValue(n.getKey()); permissionsSection.appendListNode().setValue(n.getKey());
continue; continue;
} }
} }
@ -624,7 +586,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
// handle prefixes / suffixes // handle prefixes / suffixes
ChatMetaNode<?, ?> chatMeta = (ChatMetaNode<?, ?>) n; ChatMetaNode<?, ?> chatMeta = (ChatMetaNode<?, ?>) n;
ConfigurationNode attributes = SimpleConfigurationNode.root(); ConfigurationNode attributes = ConfigurationNode.root();
attributes.getNode("priority").setValue(chatMeta.getPriority()); attributes.getNode("priority").setValue(chatMeta.getPriority());
writeAttributesTo(attributes, n, false); writeAttributesTo(attributes, n, false);
@ -642,7 +604,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
// handle meta nodes // handle meta nodes
MetaNode meta = (MetaNode) n; MetaNode meta = (MetaNode) n;
ConfigurationNode attributes = SimpleConfigurationNode.root(); ConfigurationNode attributes = ConfigurationNode.root();
attributes.getNode("value").setValue(meta.getMetaValue()); attributes.getNode("value").setValue(meta.getMetaValue());
writeAttributesTo(attributes, n, false); writeAttributesTo(attributes, n, false);
@ -651,44 +613,44 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
// handle group nodes // handle group nodes
InheritanceNode inheritance = (InheritanceNode) n; InheritanceNode inheritance = (InheritanceNode) n;
ConfigurationNode attributes = SimpleConfigurationNode.root(); ConfigurationNode attributes = ConfigurationNode.root();
writeAttributesTo(attributes, n, false); writeAttributesTo(attributes, n, false);
appendNode(parentsSection, inheritance.getGroupName(), attributes, "group"); appendNode(parentsSection, inheritance.getGroupName(), attributes, "group");
} else { } else {
// handle regular permissions and negated meta+prefixes+suffixes // handle regular permissions and negated meta+prefixes+suffixes
ConfigurationNode attributes = SimpleConfigurationNode.root(); ConfigurationNode attributes = ConfigurationNode.root();
writeAttributesTo(attributes, n, true); writeAttributesTo(attributes, n, true);
appendNode(permissionsSection, n.getKey(), attributes, "permission"); appendNode(permissionsSection, n.getKey(), attributes, "permission");
} }
} }
if (permissionsSection.hasListChildren() || this instanceof CombinedConfigurateStorage) { if (permissionsSection.isList() || this instanceof CombinedConfigurateStorage) {
to.getNode("permissions").setValue(permissionsSection); to.getNode("permissions").setValue(permissionsSection);
} else { } else {
to.removeChild("permissions"); to.removeChild("permissions");
} }
if (parentsSection.hasListChildren()) { if (parentsSection.isList()) {
to.getNode("parents").setValue(parentsSection); to.getNode("parents").setValue(parentsSection);
} else { } else {
to.removeChild("parents"); to.removeChild("parents");
} }
if (prefixesSection.hasListChildren()) { if (prefixesSection.isList()) {
to.getNode("prefixes").setValue(prefixesSection); to.getNode("prefixes").setValue(prefixesSection);
} else { } else {
to.removeChild("prefixes"); to.removeChild("prefixes");
} }
if (suffixesSection.hasListChildren()) { if (suffixesSection.isList()) {
to.getNode("suffixes").setValue(suffixesSection); to.getNode("suffixes").setValue(suffixesSection);
} else { } else {
to.removeChild("suffixes"); to.removeChild("suffixes");
} }
if (metaSection.hasListChildren()) { if (metaSection.isList()) {
to.getNode("meta").setValue(metaSection); to.getNode("meta").setValue(metaSection);
} else { } else {
to.removeChild("meta"); to.removeChild("meta");

View File

@ -52,33 +52,221 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/**
* Flat-file storage using Configurate {@link ConfigurationNode}s.
* The data for users/groups/tracks is stored in a single shared file.
*/
public class CombinedConfigurateStorage extends AbstractConfigurateStorage { public class CombinedConfigurateStorage extends AbstractConfigurateStorage {
private final String fileExtension; private final String fileExtension;
private Path usersFile; private CachedLoader users;
private Path groupsFile; private CachedLoader groups;
private Path tracksFile; private CachedLoader tracks;
private FileWatcher.WatchedLocation watcher = null;
private CachedLoader usersLoader; public CombinedConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String fileExtension, String dataFolderName) {
private CachedLoader groupsLoader; super(plugin, implementationName, loader, dataFolderName);
private CachedLoader tracksLoader; this.fileExtension = fileExtension;
}
@Override
protected ConfigurationNode readFile(StorageLocation location, String name) throws IOException {
ConfigurationNode root = getLoader(location).getNode();
ConfigurationNode node = root.getNode(name);
return node.isVirtual() ? null : node;
}
@Override
protected void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException {
getLoader(location).apply(true, false, root -> root.getNode(name).setValue(node));
}
private CachedLoader getLoader(StorageLocation location) {
switch (location) {
case USERS:
return this.users;
case GROUPS:
return this.groups;
case TRACKS:
return this.tracks;
default:
throw new RuntimeException();
}
}
@Override
public void init() throws IOException {
super.init();
this.users = new CachedLoader(super.dataDirectory.resolve("users" + this.fileExtension));
this.groups = new CachedLoader(super.dataDirectory.resolve("groups" + this.fileExtension));
this.tracks = new CachedLoader(super.dataDirectory.resolve("tracks" + this.fileExtension));
// Listen for file changes.
FileWatcher watcher = this.plugin.getFileWatcher().orElse(null);
if (watcher != null) {
this.watcher = watcher.getWatcher(super.dataDirectory);
this.watcher.addListener(path -> {
if (path.getFileName().equals(this.users.file.getFileName())) {
this.plugin.getLogger().info("[FileWatcher] Detected change in users file - reloading...");
this.users.reload();
this.plugin.getSyncTaskBuffer().request();
} else if (path.getFileName().equals(this.groups.file.getFileName())) {
this.plugin.getLogger().info("[FileWatcher] Detected change in groups file - reloading...");
this.groups.reload();
this.plugin.getSyncTaskBuffer().request();
} else if (path.getFileName().equals(this.tracks.file.getFileName())) {
this.plugin.getLogger().info("[FileWatcher] Detected change in tracks file - reloading...");
this.tracks.reload();
this.plugin.getStorage().loadAllTracks();
}
});
}
}
@Override
public void shutdown() {
try {
this.users.save();
} catch (IOException e) {
e.printStackTrace();
}
try {
this.groups.save();
} catch (IOException e) {
e.printStackTrace();
}
try {
this.tracks.save();
} catch (IOException e) {
e.printStackTrace();
}
super.shutdown();
}
@Override
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
if (bulkUpdate.getDataType().isIncludingUsers()) {
this.users.apply(true, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
processBulkUpdate(bulkUpdate, entry.getValue(), HolderType.USER);
}
});
}
if (bulkUpdate.getDataType().isIncludingGroups()) {
this.groups.apply(true, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
processBulkUpdate(bulkUpdate, entry.getValue(), HolderType.GROUP);
}
});
}
}
@Override
public Set<UUID> getUniqueUsers() throws IOException {
return this.users.getNode().getChildrenMap().keySet().stream()
.map(Object::toString)
.map(Uuids::fromString)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
@Override
public <N extends Node> List<NodeEntry<UUID, N>> searchUserNodes(ConstraintNodeMatcher<N> constraint) throws Exception {
List<NodeEntry<UUID, N>> held = new ArrayList<>();
this.users.apply(false, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
try {
UUID holder = UUID.fromString(entry.getKey().toString());
ConfigurationNode object = entry.getValue();
Set<Node> nodes = readNodes(object);
for (Node e : nodes) {
N match = constraint.match(e);
if (match != null) {
held.add(NodeEntry.of(holder, match));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
return held;
}
@Override
public void loadAllGroups() throws IOException {
List<String> groups = new ArrayList<>();
this.groups.apply(false, true, root -> {
groups.addAll(root.getChildrenMap().keySet().stream()
.map(Object::toString)
.collect(Collectors.toList()));
});
if (!Iterators.tryIterate(groups, this::loadGroup)) {
throw new RuntimeException("Exception occurred whilst loading a group");
}
this.plugin.getGroupManager().retainAll(groups);
}
@Override
public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws Exception {
List<NodeEntry<String, N>> held = new ArrayList<>();
this.groups.apply(false, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
try {
String holder = entry.getKey().toString();
ConfigurationNode object = entry.getValue();
Set<Node> nodes = readNodes(object);
for (Node e : nodes) {
N match = constraint.match(e);
if (match != null) {
held.add(NodeEntry.of(holder, match));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
return held;
}
@Override
public void loadAllTracks() throws IOException {
List<String> tracks = new ArrayList<>();
this.tracks.apply(false, true, root -> {
tracks.addAll(root.getChildrenMap().keySet().stream()
.map(Object::toString)
.collect(Collectors.toList()));
});
if (!Iterators.tryIterate(tracks, this::loadTrack)) {
throw new RuntimeException("Exception occurred whilst loading a track");
}
this.plugin.getTrackManager().retainAll(tracks);
}
private final class CachedLoader { private final class CachedLoader {
private final Path path; private final Path file;
private final ConfigurationLoader<? extends ConfigurationNode> loader; private final ConfigurationLoader<? extends ConfigurationNode> loader;
private ConfigurationNode node = null;
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
private ConfigurationNode node = null;
private CachedLoader(Path path) { private CachedLoader(Path file) {
this.path = path; this.file = file;
this.loader = CombinedConfigurateStorage.super.loader.loader(path); this.loader = CombinedConfigurateStorage.super.loader.loader(file);
reload(); reload();
} }
private void recordChange() { private void recordChange() {
if (CombinedConfigurateStorage.this.watcher != null) { if (CombinedConfigurateStorage.this.watcher != null) {
CombinedConfigurateStorage.this.watcher.recordChange(this.path.getFileName().toString()); CombinedConfigurateStorage.this.watcher.recordChange(this.file.getFileName().toString());
} }
} }
@ -142,206 +330,4 @@ public class CombinedConfigurateStorage extends AbstractConfigurateStorage {
} }
} }
private FileWatcher.WatchedLocation watcher = null;
/**
* Creates a new configurate storage implementation
*
* @param plugin the plugin instance
* @param implementationName the name of this implementation
* @param fileExtension the file extension used by this instance, including a "." at the start
* @param dataFolderName the name of the folder used to store data
*/
public CombinedConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String fileExtension, String dataFolderName) {
super(plugin, implementationName, loader, dataFolderName);
this.fileExtension = fileExtension;
}
@Override
protected ConfigurationNode readFile(StorageLocation location, String name) throws IOException {
ConfigurationNode root = getStorageLoader(location).getNode();
ConfigurationNode node = root.getNode(name);
return node.isVirtual() ? null : node;
}
@Override
protected void saveFile(StorageLocation location, String name, ConfigurationNode node) throws IOException {
getStorageLoader(location).apply(true, false, root -> root.getNode(name).setValue(node));
}
private CachedLoader getStorageLoader(StorageLocation location) {
switch (location) {
case USER:
return this.usersLoader;
case GROUP:
return this.groupsLoader;
case TRACK:
return this.tracksLoader;
default:
throw new RuntimeException();
}
}
@Override
public void init() throws IOException {
super.init();
this.usersFile = super.dataDirectory.resolve("users" + this.fileExtension);
this.groupsFile = super.dataDirectory.resolve("groups" + this.fileExtension);
this.tracksFile = super.dataDirectory.resolve("tracks" + this.fileExtension);
this.usersLoader = new CachedLoader(this.usersFile);
this.groupsLoader = new CachedLoader(this.groupsFile);
this.tracksLoader = new CachedLoader(this.tracksFile);
// Listen for file changes.
FileWatcher watcher = this.plugin.getFileWatcher().orElse(null);
if (watcher != null) {
this.watcher = watcher.getWatcher(super.dataDirectory);
this.watcher.addListener(path -> {
if (path.getFileName().equals(this.usersFile.getFileName())) {
this.plugin.getLogger().info("[FileWatcher] Detected change in users file - reloading...");
this.usersLoader.reload();
this.plugin.getSyncTaskBuffer().request();
} else if (path.getFileName().equals(this.groupsFile.getFileName())) {
this.plugin.getLogger().info("[FileWatcher] Detected change in groups file - reloading...");
this.groupsLoader.reload();
this.plugin.getSyncTaskBuffer().request();
} else if (path.getFileName().equals(this.tracksFile.getFileName())) {
this.plugin.getLogger().info("[FileWatcher] Detected change in tracks file - reloading...");
this.tracksLoader.reload();
this.plugin.getStorage().loadAllTracks();
}
});
}
}
@Override
public void shutdown() {
try {
this.usersLoader.save();
} catch (IOException e) {
e.printStackTrace();
}
try {
this.groupsLoader.save();
} catch (IOException e) {
e.printStackTrace();
}
try {
this.tracksLoader.save();
} catch (IOException e) {
e.printStackTrace();
}
super.shutdown();
}
@Override
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
if (bulkUpdate.getDataType().isIncludingUsers()) {
this.usersLoader.apply(true, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
processBulkUpdate(bulkUpdate, entry.getValue(), HolderType.USER);
}
});
}
if (bulkUpdate.getDataType().isIncludingGroups()) {
this.groupsLoader.apply(true, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
processBulkUpdate(bulkUpdate, entry.getValue(), HolderType.GROUP);
}
});
}
}
@Override
public Set<UUID> getUniqueUsers() throws IOException {
return this.usersLoader.getNode().getChildrenMap().keySet().stream()
.map(Object::toString)
.map(Uuids::fromString)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
@Override
public <N extends Node> List<NodeEntry<UUID, N>> searchUserNodes(ConstraintNodeMatcher<N> constraint) throws Exception {
List<NodeEntry<UUID, N>> held = new ArrayList<>();
this.usersLoader.apply(false, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
try {
UUID holder = UUID.fromString(entry.getKey().toString());
ConfigurationNode object = entry.getValue();
Set<Node> nodes = readNodes(object);
for (Node e : nodes) {
N match = constraint.match(e);
if (match != null) {
held.add(NodeEntry.of(holder, match));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
return held;
}
@Override
public void loadAllGroups() throws IOException {
List<String> groups = new ArrayList<>();
this.groupsLoader.apply(false, true, root -> {
groups.addAll(root.getChildrenMap().keySet().stream()
.map(Object::toString)
.collect(Collectors.toList()));
});
if (!Iterators.tryIterate(groups, this::loadGroup)) {
throw new RuntimeException("Exception occurred whilst loading a group");
}
this.plugin.getGroupManager().retainAll(groups);
}
@Override
public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws Exception {
List<NodeEntry<String, N>> held = new ArrayList<>();
this.groupsLoader.apply(false, true, root -> {
for (Map.Entry<Object, ? extends ConfigurationNode> entry : root.getChildrenMap().entrySet()) {
try {
String holder = entry.getKey().toString();
ConfigurationNode object = entry.getValue();
Set<Node> nodes = readNodes(object);
for (Node e : nodes) {
N match = constraint.match(e);
if (match != null) {
held.add(NodeEntry.of(holder, match));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
return held;
}
@Override
public void loadAllTracks() throws IOException {
List<String> tracks = new ArrayList<>();
this.tracksLoader.apply(false, true, root -> {
tracks.addAll(root.getChildrenMap().keySet().stream()
.map(Object::toString)
.collect(Collectors.toList()));
});
if (!Iterators.tryIterate(tracks, this::loadTrack)) {
throw new RuntimeException("Exception occurred whilst loading a track");
}
this.plugin.getTrackManager().retainAll(tracks);
}
} }

View File

@ -0,0 +1,36 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.implementation.file;
import java.io.IOException;
public class FileIOException extends IOException {
public FileIOException(String fileName, Throwable cause) {
super("Exception thrown whilst reading/writing file: " + fileName, cause);
}
}

View File

@ -59,44 +59,6 @@ public class FileUuidCache {
// the lookup map // the lookup map
private final LookupMap lookupMap = new LookupMap(); private final LookupMap lookupMap = new LookupMap();
private static final class LookupMap extends ConcurrentHashMap<UUID, String> {
private final SetMultimap<String, UUID> reverse = Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet);
@Override
public String put(@NonNull UUID key, @NonNull String value) {
String existing = super.put(key, value);
// check if we need to remove a reverse entry which has been replaced
// existing might be null
if (!value.equalsIgnoreCase(existing)) {
if (existing != null) {
this.reverse.remove(existing.toLowerCase(), key);
}
}
this.reverse.put(value.toLowerCase(), key);
return existing;
}
@Override
public String remove(@NonNull Object k) {
UUID key = (UUID) k;
String username = super.remove(key);
if (username != null) {
this.reverse.remove(username.toLowerCase(), key);
}
return username;
}
public String lookupUsername(UUID uuid) {
return super.get(uuid);
}
public Set<UUID> lookupUuid(String name) {
return this.reverse.get(name.toLowerCase());
}
}
/** /**
* Adds a mapping to the cache * Adds a mapping to the cache
* *
@ -154,6 +116,40 @@ public class FileUuidCache {
return this.lookupMap.lookupUsername(uuid); return this.lookupMap.lookupUsername(uuid);
} }
public void load(Path file) {
if (!Files.exists(file)) {
return;
}
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
String entry;
while ((entry = reader.readLine()) != null) {
entry = entry.trim();
if (entry.isEmpty() || entry.startsWith("#")) {
continue;
}
loadEntry(entry);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void save(Path file) {
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
writer.write("# LuckPerms UUID lookup cache");
writer.newLine();
for (Map.Entry<UUID, String> ent : this.lookupMap.entrySet()) {
String out = ent.getKey() + ":" + ent.getValue();
writer.write(out);
writer.newLine();
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
private void loadEntry(String entry) { private void loadEntry(String entry) {
if (entry.contains(":")) { if (entry.contains(":")) {
// new format // new format
@ -193,37 +189,41 @@ public class FileUuidCache {
} }
} }
public void load(Path file) { private static final class LookupMap extends ConcurrentHashMap<UUID, String> {
if (!Files.exists(file)) { private final SetMultimap<String, UUID> reverse = Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet);
return;
}
try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { @Override
String entry; public String put(@NonNull UUID key, @NonNull String value) {
while ((entry = reader.readLine()) != null) { String existing = super.put(key, value);
entry = entry.trim();
if (entry.isEmpty() || entry.startsWith("#")) { // check if we need to remove a reverse entry which has been replaced
continue; // existing might be null
} if (!value.equalsIgnoreCase(existing)) {
loadEntry(entry); if (existing != null) {
} this.reverse.remove(existing.toLowerCase(), key);
} catch (IOException e) {
e.printStackTrace();
} }
} }
public void save(Path file) { this.reverse.put(value.toLowerCase(), key);
try (BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { return existing;
writer.write("# LuckPerms UUID lookup cache");
writer.newLine();
for (Map.Entry<UUID, String> ent : this.lookupMap.entrySet()) {
String out = ent.getKey() + ":" + ent.getValue();
writer.write(out);
writer.newLine();
} }
writer.flush();
} catch (IOException e) { @Override
e.printStackTrace(); public String remove(@NonNull Object k) {
UUID key = (UUID) k;
String username = super.remove(key);
if (username != null) {
this.reverse.remove(username.toLowerCase(), key);
}
return username;
}
public String lookupUsername(UUID uuid) {
return super.get(uuid);
}
public Set<UUID> lookupUuid(String name) {
return this.reverse.get(name.toLowerCase());
} }
} }

View File

@ -25,6 +25,8 @@
package me.lucko.luckperms.common.storage.implementation.file; package me.lucko.luckperms.common.storage.implementation.file;
import com.google.common.collect.ImmutableMap;
import me.lucko.luckperms.common.bulkupdate.BulkUpdate; import me.lucko.luckperms.common.bulkupdate.BulkUpdate;
import me.lucko.luckperms.common.model.HolderType; import me.lucko.luckperms.common.model.HolderType;
import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.model.User;
@ -45,7 +47,9 @@ import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -53,30 +57,38 @@ import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
/**
* Flat-file storage using Configurate {@link ConfigurationNode}s.
* The data for each user/group/track is stored in a separate file.
*/
public class SeparatedConfigurateStorage extends AbstractConfigurateStorage { public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
private final String fileExtension; private final String fileExtension;
private final Predicate<Path> fileExtensionFilter; private final Predicate<Path> fileExtensionFilter;
private Path usersDirectory; private final Map<StorageLocation, FileGroup> fileGroups;
private Path groupsDirectory; private final FileGroup users;
private Path tracksDirectory; private final FileGroup groups;
private final FileGroup tracks;
private FileWatcher.WatchedLocation userWatcher = null; private static final class FileGroup {
private FileWatcher.WatchedLocation groupWatcher = null; private Path directory;
private FileWatcher.WatchedLocation trackWatcher = null; private FileWatcher.WatchedLocation watcher;
}
/**
* Creates a new configurate storage implementation
*
* @param plugin the plugin instance
* @param implementationName the name of this implementation
* @param fileExtension the file extension used by this instance, including a "." at the start
* @param dataFolderName the name of the folder used to store data
*/
public SeparatedConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String fileExtension, String dataFolderName) { public SeparatedConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String fileExtension, String dataFolderName) {
super(plugin, implementationName, loader, dataFolderName); super(plugin, implementationName, loader, dataFolderName);
this.fileExtension = fileExtension; this.fileExtension = fileExtension;
this.fileExtensionFilter = path -> path.getFileName().toString().endsWith(this.fileExtension); this.fileExtensionFilter = path -> path.getFileName().toString().endsWith(this.fileExtension);
this.users = new FileGroup();
this.groups = new FileGroup();
this.tracks = new FileGroup();
EnumMap<StorageLocation, FileGroup> fileGroups = new EnumMap<>(StorageLocation.class);
fileGroups.put(StorageLocation.USERS, this.users);
fileGroups.put(StorageLocation.GROUPS, this.groups);
fileGroups.put(StorageLocation.TRACKS, this.tracks);
this.fileGroups = ImmutableMap.copyOf(fileGroups);
} }
@Override @Override
@ -111,37 +123,13 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
} }
private Path getDirectory(StorageLocation location) { private Path getDirectory(StorageLocation location) {
switch (location) { return this.fileGroups.get(location).directory;
case USER:
return this.usersDirectory;
case GROUP:
return this.groupsDirectory;
case TRACK:
return this.tracksDirectory;
default:
throw new RuntimeException();
}
} }
private void registerFileAction(StorageLocation type, Path file) { private void registerFileAction(StorageLocation type, Path file) {
switch (type) { FileWatcher.WatchedLocation watcher = this.fileGroups.get(type).watcher;
case USER: if (watcher != null) {
if (this.userWatcher != null) { watcher.recordChange(file.getFileName().toString());
this.userWatcher.recordChange(file.getFileName().toString());
}
break;
case GROUP:
if (this.groupWatcher != null) {
this.groupWatcher.recordChange(file.getFileName().toString());
}
break;
case TRACK:
if (this.trackWatcher != null) {
this.trackWatcher.recordChange(file.getFileName().toString());
}
break;
default:
throw new RuntimeException();
} }
} }
@ -149,22 +137,21 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
public void init() throws IOException { public void init() throws IOException {
super.init(); super.init();
this.usersDirectory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("users")); this.users.directory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("users"));
this.groupsDirectory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("groups")); this.groups.directory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("groups"));
this.tracksDirectory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("tracks")); this.tracks.directory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("tracks"));
// Listen for file changes. // Listen for file changes.
FileWatcher watcher = this.plugin.getFileWatcher().orElse(null); FileWatcher watcher = this.plugin.getFileWatcher().orElse(null);
if (watcher != null) { if (watcher != null) {
this.userWatcher = watcher.getWatcher(this.usersDirectory); this.users.watcher = watcher.getWatcher(this.users.directory);
this.userWatcher.addListener(path -> { this.users.watcher.addListener(path -> {
String s = path.getFileName().toString(); String fileName = path.getFileName().toString();
if (!fileName.endsWith(this.fileExtension)) {
if (!s.endsWith(this.fileExtension)) {
return; return;
} }
String user = s.substring(0, s.length() - this.fileExtension.length()); String user = fileName.substring(0, fileName.length() - this.fileExtension.length());
UUID uuid = Uuids.parse(user); UUID uuid = Uuids.parse(user);
if (uuid == null) { if (uuid == null) {
return; return;
@ -177,28 +164,26 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
} }
}); });
this.groupWatcher = watcher.getWatcher(this.groupsDirectory); this.groups.watcher = watcher.getWatcher(this.groups.directory);
this.groupWatcher.addListener(path -> { this.groups.watcher.addListener(path -> {
String s = path.getFileName().toString(); String fileName = path.getFileName().toString();
if (!fileName.endsWith(this.fileExtension)) {
if (!s.endsWith(this.fileExtension)) {
return; return;
} }
String groupName = s.substring(0, s.length() - this.fileExtension.length()); String groupName = fileName.substring(0, fileName.length() - this.fileExtension.length());
this.plugin.getLogger().info("[FileWatcher] Detected change in group file for " + groupName + " - reloading..."); this.plugin.getLogger().info("[FileWatcher] Detected change in group file for " + groupName + " - reloading...");
this.plugin.getSyncTaskBuffer().request(); this.plugin.getSyncTaskBuffer().request();
}); });
this.trackWatcher = watcher.getWatcher(this.tracksDirectory); this.tracks.watcher = watcher.getWatcher(this.tracks.directory);
this.trackWatcher.addListener(path -> { this.tracks.watcher.addListener(path -> {
String s = path.getFileName().toString(); String fileName = path.getFileName().toString();
if (!fileName.endsWith(this.fileExtension)) {
if (!s.endsWith(this.fileExtension)) {
return; return;
} }
String trackName = s.substring(0, s.length() - this.fileExtension.length()); String trackName = fileName.substring(0, fileName.length() - this.fileExtension.length());
this.plugin.getLogger().info("[FileWatcher] Detected change in track file for " + trackName + " - reloading..."); this.plugin.getLogger().info("[FileWatcher] Detected change in track file for " + trackName + " - reloading...");
this.plugin.getStorage().loadAllTracks(); this.plugin.getStorage().loadAllTracks();
}); });
@ -208,32 +193,38 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
@Override @Override
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception { public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
if (bulkUpdate.getDataType().isIncludingUsers()) { if (bulkUpdate.getDataType().isIncludingUsers()) {
try (Stream<Path> s = Files.list(getDirectory(StorageLocation.USER))) { try (Stream<Path> s = Files.list(getDirectory(StorageLocation.USERS))) {
s.filter(this.fileExtensionFilter).forEach(file -> { s.filter(this.fileExtensionFilter).forEach(file -> {
try { try {
registerFileAction(StorageLocation.USER, file); registerFileAction(StorageLocation.USERS, file);
ConfigurationNode object = readFile(file); ConfigurationNode object = readFile(file);
if (processBulkUpdate(bulkUpdate, object, HolderType.USER)) { if (processBulkUpdate(bulkUpdate, object, HolderType.USER)) {
saveFile(file, object); saveFile(file, object);
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(file.getFileName().toString(), e); this.plugin.getLogger().severe(
"Exception whilst performing bulkupdate",
new FileIOException(file.getFileName().toString(), e)
);
} }
}); });
} }
} }
if (bulkUpdate.getDataType().isIncludingGroups()) { if (bulkUpdate.getDataType().isIncludingGroups()) {
try (Stream<Path> s = Files.list(getDirectory(StorageLocation.GROUP))) { try (Stream<Path> s = Files.list(getDirectory(StorageLocation.GROUPS))) {
s.filter(this.fileExtensionFilter).forEach(file -> { s.filter(this.fileExtensionFilter).forEach(file -> {
try { try {
registerFileAction(StorageLocation.GROUP, file); registerFileAction(StorageLocation.GROUPS, file);
ConfigurationNode object = readFile(file); ConfigurationNode object = readFile(file);
if (processBulkUpdate(bulkUpdate, object, HolderType.GROUP)) { if (processBulkUpdate(bulkUpdate, object, HolderType.GROUP)) {
saveFile(file, object); saveFile(file, object);
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(file.getFileName().toString(), e); this.plugin.getLogger().severe(
"Exception whilst performing bulkupdate",
new FileIOException(file.getFileName().toString(), e)
);
} }
}); });
} }
@ -242,7 +233,7 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
@Override @Override
public Set<UUID> getUniqueUsers() throws IOException { public Set<UUID> getUniqueUsers() throws IOException {
try (Stream<Path> stream = Files.list(this.usersDirectory)) { try (Stream<Path> stream = Files.list(this.users.directory)) {
return stream.filter(this.fileExtensionFilter) return stream.filter(this.fileExtensionFilter)
.map(p -> p.getFileName().toString()) .map(p -> p.getFileName().toString())
.map(s -> s.substring(0, s.length() - this.fileExtension.length())) .map(s -> s.substring(0, s.length() - this.fileExtension.length()))
@ -253,14 +244,14 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
} }
@Override @Override
public <N extends Node> List<NodeEntry<UUID, N>> searchUserNodes(ConstraintNodeMatcher<N> constraint) throws Exception { public <N extends Node> List<NodeEntry<UUID, N>> searchUserNodes(ConstraintNodeMatcher<N> constraint) throws IOException {
List<NodeEntry<UUID, N>> held = new ArrayList<>(); List<NodeEntry<UUID, N>> held = new ArrayList<>();
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.USER))) { try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.USERS))) {
stream.filter(this.fileExtensionFilter) stream.filter(this.fileExtensionFilter)
.forEach(file -> { .forEach(file -> {
String fileName = file.getFileName().toString(); String fileName = file.getFileName().toString();
try { try {
registerFileAction(StorageLocation.USER, file); registerFileAction(StorageLocation.USERS, file);
ConfigurationNode object = readFile(file); ConfigurationNode object = readFile(file);
UUID holder = UUID.fromString(fileName.substring(0, fileName.length() - this.fileExtension.length())); UUID holder = UUID.fromString(fileName.substring(0, fileName.length() - this.fileExtension.length()));
Set<Node> nodes = readNodes(object); Set<Node> nodes = readNodes(object);
@ -271,7 +262,10 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(file.getFileName().toString(), e); this.plugin.getLogger().severe(
"Exception whilst searching user nodes",
new FileIOException(file.getFileName().toString(), e)
);
} }
}); });
} }
@ -281,7 +275,7 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
@Override @Override
public void loadAllGroups() throws IOException { public void loadAllGroups() throws IOException {
List<String> groups; List<String> groups;
try (Stream<Path> stream = Files.list(this.groupsDirectory)) { try (Stream<Path> stream = Files.list(this.groups.directory)) {
groups = stream.filter(this.fileExtensionFilter) groups = stream.filter(this.fileExtensionFilter)
.map(p -> p.getFileName().toString()) .map(p -> p.getFileName().toString())
.map(s -> s.substring(0, s.length() - this.fileExtension.length())) .map(s -> s.substring(0, s.length() - this.fileExtension.length()))
@ -296,14 +290,14 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
} }
@Override @Override
public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws Exception { public <N extends Node> List<NodeEntry<String, N>> searchGroupNodes(ConstraintNodeMatcher<N> constraint) throws IOException {
List<NodeEntry<String, N>> held = new ArrayList<>(); List<NodeEntry<String, N>> held = new ArrayList<>();
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.GROUP))) { try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.GROUPS))) {
stream.filter(this.fileExtensionFilter) stream.filter(this.fileExtensionFilter)
.forEach(file -> { .forEach(file -> {
String fileName = file.getFileName().toString(); String fileName = file.getFileName().toString();
try { try {
registerFileAction(StorageLocation.GROUP, file); registerFileAction(StorageLocation.GROUPS, file);
ConfigurationNode object = readFile(file); ConfigurationNode object = readFile(file);
String holder = fileName.substring(0, fileName.length() - this.fileExtension.length()); String holder = fileName.substring(0, fileName.length() - this.fileExtension.length());
Set<Node> nodes = readNodes(object); Set<Node> nodes = readNodes(object);
@ -314,7 +308,10 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw reportException(file.getFileName().toString(), e); this.plugin.getLogger().severe(
"Exception whilst searching group nodes",
new FileIOException(file.getFileName().toString(), e)
);
} }
}); });
} }
@ -324,7 +321,7 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
@Override @Override
public void loadAllTracks() throws IOException { public void loadAllTracks() throws IOException {
List<String> tracks; List<String> tracks;
try (Stream<Path> stream = Files.list(this.tracksDirectory)) { try (Stream<Path> stream = Files.list(this.tracks.directory)) {
tracks = stream.filter(this.fileExtensionFilter) tracks = stream.filter(this.fileExtensionFilter)
.map(p -> p.getFileName().toString()) .map(p -> p.getFileName().toString())
.map(s -> s.substring(0, s.length() - this.fileExtension.length())) .map(s -> s.substring(0, s.length() - this.fileExtension.length()))

View File

@ -27,6 +27,6 @@ package me.lucko.luckperms.common.storage.implementation.file;
public enum StorageLocation { public enum StorageLocation {
USER, GROUP, TRACK USERS, GROUPS, TRACKS
} }

View File

@ -30,6 +30,7 @@ import net.luckperms.api.node.Node;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
@SuppressWarnings("deprecation")
public final class NodeEntry<H extends Comparable<H>, N extends Node> implements HeldNode<H> { public final class NodeEntry<H extends Comparable<H>, N extends Node> implements HeldNode<H> {
public static <H extends Comparable<H>, N extends Node> NodeEntry<H, N> of(H holder, N node) { public static <H extends Comparable<H>, N extends Node> NodeEntry<H, N> of(H holder, N node) {

View File

@ -185,7 +185,7 @@ public class TreeView {
e.printStackTrace(); e.printStackTrace();
} }
return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE).key();
} }
} }

View File

@ -319,7 +319,7 @@ public class VerboseListener {
e.printStackTrace(); e.printStackTrace();
} }
return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key(); return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE).key();
} }
public Sender getNotifiedSender() { public Sender getNotifiedSender() {

View File

@ -155,7 +155,7 @@ public class WebEditorRequest {
public CommandResult createSession(LuckPermsPlugin plugin, Sender sender) { public CommandResult createSession(LuckPermsPlugin plugin, Sender sender) {
String pasteId; String pasteId;
try { try {
pasteId = plugin.getBytebin().postContent(encode(), AbstractHttpClient.JSON_TYPE, false).key(); pasteId = plugin.getBytebin().postContent(encode(), AbstractHttpClient.JSON_TYPE).key();
} catch (UnsuccessfulRequestException e) { } catch (UnsuccessfulRequestException e) {
Message.EDITOR_HTTP_REQUEST_FAILURE.send(sender, e.getResponse().code(), e.getResponse().message()); Message.EDITOR_HTTP_REQUEST_FAILURE.send(sender, e.getResponse().code(), e.getResponse().message());
return CommandResult.STATE_ERROR; return CommandResult.STATE_ERROR;

View File

@ -0,0 +1,79 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.dependencies;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.util.Base64;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class DependencyChecksumTest {
@Test
@Tag("dependency_checksum")
public void check() {
Dependency[] dependencies = Dependency.values();
DependencyRepository[] repos = DependencyRepository.values();
ExecutorService pool = Executors.newCachedThreadPool();
AtomicBoolean failed = new AtomicBoolean(false);
for (Dependency dependency : dependencies) {
for (DependencyRepository repo : repos) {
pool.submit(() -> {
try {
byte[] hash = Dependency.createDigest().digest(repo.downloadRaw(dependency));
if (!dependency.checksumMatches(hash)) {
System.out.println("NO MATCH - " + repo.name() + " - " + dependency.name() + ": " + Base64.getEncoder().encodeToString(hash));
failed.set(true);
} else {
System.out.println("OK - " + repo.name() + " - " + dependency.name());
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
pool.shutdown();
try {
pool.awaitTermination(1, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (failed.get()) {
Assertions.fail("Some dependency checksums did not match");
}
}
}

View File

@ -0,0 +1,70 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* Copyright (c) contributors
*
* 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.node.utils;
import com.google.common.collect.ImmutableSet;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ShorthandParserTest {
private static void test(String shorthand, String... expected) {
assertEquals(ImmutableSet.copyOf(expected), ShorthandParser.expandShorthand(shorthand));
}
@Test
void testNumericRange() {
test("{2-4}", "2", "3", "4");
}
@Test
void testCharacterRange() {
test("{a-d}", "a", "b", "c", "d");
test("{A-D}", "A", "B", "C", "D");
}
@Test
void testList() {
test("{aa,bb,cc}", "aa", "bb", "cc");
test("{aa|bb|cc}", "aa", "bb", "cc");
test("{aa,bb|cc}", "aa", "bb", "cc");
}
@Test
void testGroups() {
test("he{y|llo} {1-2}", "hey 1", "hey 2", "hello 1", "hello 2");
test("my.permission.{test,hi}", "my.permission.test", "my.permission.hi");
test("my.permission.{a-c}", "my.permission.a", "my.permission.b", "my.permission.c");
// use ( ) instead
test("he(y|llo) (1-2)", "hey 1", "hey 2", "hello 1", "hello 2");
test("my.permission.(test,hi)", "my.permission.test", "my.permission.hi");
test("my.permission.(a-c)", "my.permission.a", "my.permission.b", "my.permission.c");
}
}

View File

@ -67,7 +67,7 @@ public class NukkitAutoOpListener implements LuckPermsEventListener {
} }
private void refreshAutoOp(Player player) { private void refreshAutoOp(Player player) {
User user = plugin.getUserManager().getIfLoaded(player.getUniqueId()); User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
boolean value; boolean value;
if (user != null) { if (user != null) {

View File

@ -25,8 +25,7 @@
package me.lucko.luckperms.velocity; package me.lucko.luckperms.velocity;
import com.velocitypowered.api.command.Command; import com.velocitypowered.api.command.RawCommand;
import com.velocitypowered.api.command.CommandSource;
import com.velocitypowered.api.proxy.ConsoleCommandSource; import com.velocitypowered.api.proxy.ConsoleCommandSource;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
@ -34,16 +33,16 @@ import me.lucko.luckperms.common.command.CommandManager;
import me.lucko.luckperms.common.command.utils.ArgumentTokenizer; import me.lucko.luckperms.common.command.utils.ArgumentTokenizer;
import me.lucko.luckperms.common.sender.Sender; import me.lucko.luckperms.common.sender.Sender;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
public class VelocityCommandExecutor extends CommandManager implements Command { public class VelocityCommandExecutor extends CommandManager implements RawCommand {
/** The command aliases */ /* The command aliases */
private static final String[] ALIASES = {"luckpermsvelocity", "lpv", "vperm", "vperms", "vpermission", "vpermissions"}; private static final String PRIMARY_ALIAS = "luckpermsvelocity";
private static final String[] ALIASES = {"lpv", "vperm", "vperms", "vpermission", "vpermissions"};
/** The command aliases, prefixed with '/' */ /* The command aliases, prefixed with '/' */
private static final String SLASH_PRIMARY_ALIAS = "/luckpermsvelocity";
private static final String[] SLASH_ALIASES = Arrays.stream(ALIASES).map(s -> '/' + s).toArray(String[]::new); private static final String[] SLASH_ALIASES = Arrays.stream(ALIASES).map(s -> '/' + s).toArray(String[]::new);
private final LPVelocityPlugin plugin; private final LPVelocityPlugin plugin;
@ -55,51 +54,46 @@ public class VelocityCommandExecutor extends CommandManager implements Command {
public void register() { public void register() {
ProxyServer proxy = this.plugin.getBootstrap().getProxy(); ProxyServer proxy = this.plugin.getBootstrap().getProxy();
proxy.getCommandManager().register(this, ALIASES); proxy.getCommandManager().register(PRIMARY_ALIAS, this, ALIASES);
// register slash aliases so the console can run '/lpv' in the same way as 'lpv'. // register slash aliases so the console can run '/lpv' in the same way as 'lpv'.
proxy.getCommandManager().register(new ForwardingCommand(this) { proxy.getCommandManager().register(SLASH_PRIMARY_ALIAS, new ForwardingCommand(this) {
@Override @Override
public boolean hasPermission(CommandSource source, @NonNull String[] args) { public boolean hasPermission(Invocation invocation) {
return source instanceof ConsoleCommandSource; return invocation.source() instanceof ConsoleCommandSource;
} }
}, SLASH_ALIASES); }, SLASH_ALIASES);
} }
@Override @Override
public void execute(@NonNull CommandSource source, @NonNull String[] args) { public void execute(Invocation invocation) {
Sender wrapped = this.plugin.getSenderFactory().wrap(source); Sender wrapped = this.plugin.getSenderFactory().wrap(invocation.source());
List<String> arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(args); List<String> arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(invocation.arguments());
executeCommand(wrapped, "lpv", arguments); executeCommand(wrapped, "lpv", arguments);
} }
@Override @Override
public List<String> suggest(@NonNull CommandSource source, @NonNull String[] args) { public List<String> suggest(Invocation invocation) {
Sender wrapped = this.plugin.getSenderFactory().wrap(source); Sender wrapped = this.plugin.getSenderFactory().wrap(invocation.source());
List<String> arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(args); List<String> arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(invocation.arguments());
return tabCompleteCommand(wrapped, arguments); return tabCompleteCommand(wrapped, arguments);
} }
private static class ForwardingCommand implements Command { private static class ForwardingCommand implements RawCommand {
private final Command delegate; private final RawCommand delegate;
private ForwardingCommand(Command delegate) { private ForwardingCommand(RawCommand delegate) {
this.delegate = delegate; this.delegate = delegate;
} }
@Override @Override
public void execute(CommandSource source, @NonNull String[] args) { public void execute(Invocation invocation) {
this.delegate.execute(source, args); this.delegate.execute(invocation);
} }
@Override @Override
public List<String> suggest(CommandSource source, @NonNull String[] currentArgs) { public List<String> suggest(Invocation invocation) {
return this.delegate.suggest(source, currentArgs); return this.delegate.suggest(invocation);
}
@Override
public boolean hasPermission(CommandSource source, @NonNull String[] args) {
return this.delegate.hasPermission(source, args);
} }
} }
} }

View File

@ -51,7 +51,6 @@ public final class AdventureCompat {
static { static {
String adventurePkg = "net.kyo".concat("ri.adventure."); String adventurePkg = "net.kyo".concat("ri.adventure.");
try { try {
if (classExists(adventurePkg + "audience.Audience")) {
Class<?> audienceClass = Class.forName(adventurePkg + "audience.Audience"); Class<?> audienceClass = Class.forName(adventurePkg + "audience.Audience");
Class<?> componentClass = Class.forName(adventurePkg + "text.Component"); Class<?> componentClass = Class.forName(adventurePkg + "text.Component");
Class<?> serializerClass = Class.forName(adventurePkg + "text.serializer.gson.GsonComponentSerializer"); Class<?> serializerClass = Class.forName(adventurePkg + "text.serializer.gson.GsonComponentSerializer");
@ -60,15 +59,6 @@ public final class AdventureCompat {
PLATFORM_SEND_MESSAGE = audienceClass.getMethod("sendMessage", componentClass); PLATFORM_SEND_MESSAGE = audienceClass.getMethod("sendMessage", componentClass);
PLATFORM_COMPONENT_RESULT_DENIED = ComponentResult.class.getMethod("denied", componentClass); PLATFORM_COMPONENT_RESULT_DENIED = ComponentResult.class.getMethod("denied", componentClass);
PLATFORM_SERIALIZER_INSTANCE = serializerClass.getMethod("gson").invoke(null); PLATFORM_SERIALIZER_INSTANCE = serializerClass.getMethod("gson").invoke(null);
} else {
Class<?> componentClass = Class.forName("net.kyori.text.Component");
Class<?> serializerClass = Class.forName("net.kyori.text.serializer.gson.GsonComponentSerializer");
PLATFORM_SERIALIZER_DESERIALIZE = serializerClass.getMethod("deserialize", String.class);
PLATFORM_SEND_MESSAGE = CommandSource.class.getMethod("sendMessage", componentClass);
PLATFORM_COMPONENT_RESULT_DENIED = ComponentResult.class.getMethod("denied", componentClass);
PLATFORM_SERIALIZER_INSTANCE = serializerClass.getField("INSTANCE").get(null);
}
} catch (ReflectiveOperationException e) { } catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e); throw new ExceptionInInitializerError(e);
} }
@ -99,13 +89,4 @@ public final class AdventureCompat {
} }
} }
private static boolean classExists(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
} }