mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-11-27 13:15:44 +01:00
Some misc tidying up
This commit is contained in:
parent
5c44333892
commit
8dfeef9575
@ -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> NORMAL_ONLY_LIST = Collections.unmodifiableList(Collections.singletonList(DataType.NORMAL));
|
||||
private static final List<DataType> TRANSIENT_ONLY_LIST = Collections.unmodifiableList(Collections.singletonList(DataType.TRANSIENT));
|
||||
private static final List<DataType> NORMAL_ONLY_LIST = Collections.singletonList(DataType.NORMAL);
|
||||
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}.
|
||||
|
@ -157,24 +157,20 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
|
||||
@Override
|
||||
public String getGroupChatPrefix(String world, String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
Group group = getGroup(name);
|
||||
if (group == null) {
|
||||
MetaCache metaData = getGroupMetaCache(name, world);
|
||||
if (metaData == 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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getGroupChatSuffix(String world, String name) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
Group group = getGroup(name);
|
||||
if (group == null) {
|
||||
MetaCache metaData = getGroupMetaCache(name, world);
|
||||
if (metaData == 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));
|
||||
}
|
||||
|
||||
@ -202,12 +198,10 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
|
||||
public String getGroupMeta(String world, String name, String key) {
|
||||
Objects.requireNonNull(name, "name");
|
||||
Objects.requireNonNull(key, "key");
|
||||
Group group = getGroup(name);
|
||||
if (group == null) {
|
||||
MetaCache metaData = getGroupMetaCache(name, world);
|
||||
if (metaData == 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);
|
||||
}
|
||||
|
||||
@ -228,6 +222,15 @@ public class LuckPermsVaultChat extends AbstractVaultChat {
|
||||
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) {
|
||||
// remove all prefixes/suffixes directly set on the user/group
|
||||
holder.removeIf(DataType.NORMAL, null, type.nodeType()::matches, false);
|
||||
|
@ -1,5 +1,7 @@
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
useJUnitPlatform {
|
||||
excludeTags 'dependency_checksum'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -100,7 +100,7 @@ public class LogDispatcher {
|
||||
try {
|
||||
this.plugin.getStorage().logAction(entry).get();
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warn("Error whilst storing action", e);
|
||||
this.plugin.getLogger().warn("Error whilst storing action", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,6 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class ApiPlayerAdapter<S, P extends S> implements PlayerAdapter<P> {
|
||||
private final UserManager<?> userManager;
|
||||
private final ContextManager<S, P> contextManager;
|
||||
|
@ -263,7 +263,7 @@ public abstract class Exporter implements Runnable {
|
||||
}
|
||||
|
||||
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));
|
||||
} catch (UnsuccessfulRequestException e) {
|
||||
this.log.getListeners().forEach(l -> Message.HTTP_REQUEST_FAILURE.send(l, e.getResponse().code(), e.getResponse().message()));
|
||||
|
@ -124,7 +124,7 @@ public final class ConfigKeys {
|
||||
*/
|
||||
public static final ConfigKey<ContextSatisfyMode> CONTEXT_SATISFY_MODE = key(c -> {
|
||||
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.AT_LEAST_ONE_VALUE_PER_KEY;
|
||||
@ -199,11 +199,11 @@ public final class ConfigKeys {
|
||||
String option = PRIMARY_GROUP_CALCULATION_METHOD.get(c);
|
||||
switch (option) {
|
||||
case "stored":
|
||||
return (Function<User, PrimaryGroupHolder>) PrimaryGroupHolder.Stored::new;
|
||||
return PrimaryGroupHolder.Stored::new;
|
||||
case "parents-by-weight":
|
||||
return (Function<User, PrimaryGroupHolder>) PrimaryGroupHolder.ParentsByWeight::new;
|
||||
return PrimaryGroupHolder.ParentsByWeight::new;
|
||||
default:
|
||||
return (Function<User, PrimaryGroupHolder>) PrimaryGroupHolder.AllParentsByWeight::new;
|
||||
return PrimaryGroupHolder.AllParentsByWeight::new;
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -88,7 +88,7 @@ public abstract class ConfigurateConfigAdapter implements ConfigurationAdapter {
|
||||
@Override
|
||||
public List<String> getStringList(String path, List<String> def) {
|
||||
ConfigurationNode node = resolvePath(path);
|
||||
if (node.isVirtual() || !node.hasListChildren()) {
|
||||
if (node.isVirtual() || !node.isList()) {
|
||||
return def;
|
||||
}
|
||||
|
||||
@ -98,7 +98,7 @@ public abstract class ConfigurateConfigAdapter implements ConfigurationAdapter {
|
||||
@Override
|
||||
public List<String> getKeys(String path, List<String> def) {
|
||||
ConfigurationNode node = resolvePath(path);
|
||||
if (node.isVirtual() || !node.hasMapChildren()) {
|
||||
if (node.isVirtual() || !node.isMap()) {
|
||||
return def;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ import net.luckperms.api.context.ContextSet;
|
||||
import net.luckperms.api.context.MutableContextSet;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.SimpleConfigurationNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -45,7 +44,7 @@ public final class ContextSetConfigurateSerializer {
|
||||
private ContextSetConfigurateSerializer() {}
|
||||
|
||||
public static ConfigurationNode serializeContextSet(ContextSet contextSet) {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
ConfigurationNode data = ConfigurationNode.root();
|
||||
Map<String, Set<String>> map = contextSet.toMap();
|
||||
|
||||
for (Map.Entry<String, Set<String>> entry : map.entrySet()) {
|
||||
@ -63,7 +62,7 @@ public final class ContextSetConfigurateSerializer {
|
||||
}
|
||||
|
||||
public static ContextSet deserializeContextSet(ConfigurationNode data) {
|
||||
Preconditions.checkArgument(data.hasMapChildren());
|
||||
Preconditions.checkArgument(data.isMap());
|
||||
Map<Object, ? extends ConfigurationNode> dataMap = data.getChildrenMap();
|
||||
|
||||
if (dataMap.isEmpty()) {
|
||||
@ -75,9 +74,8 @@ public final class ContextSetConfigurateSerializer {
|
||||
String k = e.getKey().toString();
|
||||
ConfigurationNode v = e.getValue();
|
||||
|
||||
if (v.hasListChildren()) {
|
||||
List<? extends ConfigurationNode> values = v.getChildrenList();
|
||||
for (ConfigurationNode value : values) {
|
||||
if (v.isList()) {
|
||||
for (ConfigurationNode value : v.getChildrenList()) {
|
||||
map.add(k, value.getString());
|
||||
}
|
||||
} else {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
@ -83,11 +83,10 @@ public class BytebinClient extends AbstractHttpClient {
|
||||
*
|
||||
* @param buf the compressed content
|
||||
* @param contentType the type of the content
|
||||
* @param allowModification if the paste should be modifiable
|
||||
* @return the key of the resultant content
|
||||
* @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);
|
||||
|
||||
Request.Builder requestBuilder = new Request.Builder()
|
||||
@ -95,53 +94,15 @@ public class BytebinClient extends AbstractHttpClient {
|
||||
.header("User-Agent", this.userAgent)
|
||||
.header("Content-Encoding", "gzip");
|
||||
|
||||
if (allowModification) {
|
||||
requestBuilder.header("Allow-Modification", "true");
|
||||
}
|
||||
|
||||
Request request = requestBuilder.post(body).build();
|
||||
try (Response response = makeHttpRequest(request)) {
|
||||
String key = response.header("Location");
|
||||
if (key == null) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -173,19 +134,9 @@ public class BytebinClient extends AbstractHttpClient {
|
||||
|
||||
public static final class Content {
|
||||
private final String key;
|
||||
private final boolean modifiable;
|
||||
private final String modificationKey;
|
||||
|
||||
Content(String 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() {
|
||||
|
@ -320,10 +320,5 @@ public class TranslationRepository {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class RedisMessenger implements Messenger {
|
||||
this.jedisPool = new JedisPool(new JedisPoolConfig(), host, port, Protocol.DEFAULT_TIMEOUT, password, ssl);
|
||||
|
||||
this.sub = new Subscription(this);
|
||||
this.plugin.getBootstrap().getScheduler().executeAsync(sub);
|
||||
this.plugin.getBootstrap().getScheduler().executeAsync(this.sub);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,13 +95,13 @@ public class RedisMessenger implements Messenger {
|
||||
while (!Thread.interrupted() && !this.parent.jedisPool.isClosed()) {
|
||||
try (Jedis jedis = this.parent.jedisPool.getResource()) {
|
||||
if (wasBroken) {
|
||||
parent.plugin.getLogger().info("Redis pubsub connection re-established");
|
||||
this.parent.plugin.getLogger().info("Redis pubsub connection re-established");
|
||||
wasBroken = false;
|
||||
}
|
||||
jedis.subscribe(this, CHANNEL);
|
||||
} catch (Exception e) {
|
||||
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 {
|
||||
unsubscribe();
|
||||
} catch (Exception ignored) {
|
||||
|
@ -25,9 +25,7 @@
|
||||
|
||||
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.Maps;
|
||||
|
||||
import me.lucko.luckperms.common.actionlog.Log;
|
||||
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.JsonLoader;
|
||||
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 net.luckperms.api.actionlog.Action;
|
||||
@ -63,53 +60,51 @@ import net.luckperms.api.node.types.InheritanceNode;
|
||||
import net.luckperms.api.node.types.MetaNode;
|
||||
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.SimpleConfigurationNode;
|
||||
import ninja.leaping.configurate.Types;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Abstract implementation using configurate {@link ConfigurationNode}s to serialize and deserialize
|
||||
* data.
|
||||
* Abstract storage implementation using Configurate {@link ConfigurationNode}s to
|
||||
* serialize and deserialize data.
|
||||
*/
|
||||
public abstract class AbstractConfigurateStorage implements StorageImplementation {
|
||||
|
||||
/** The plugin instance */
|
||||
protected final LuckPermsPlugin plugin;
|
||||
|
||||
/** The name of this implementation */
|
||||
private final String implementationName;
|
||||
|
||||
// the loader responsible for i/o
|
||||
/** The Configurate loader used to read/write data */
|
||||
protected final ConfigurateLoader loader;
|
||||
|
||||
// the name of the data directory
|
||||
private final String dataDirectoryName;
|
||||
// the data directory
|
||||
/* The data directory */
|
||||
protected Path dataDirectory;
|
||||
private final String dataDirectoryName;
|
||||
|
||||
// the uuid cache instance
|
||||
private final FileUuidCache uuidCache = new FileUuidCache();
|
||||
// the action logger instance
|
||||
/* The UUID cache */
|
||||
private final FileUuidCache uuidCache;
|
||||
private Path uuidCacheFile;
|
||||
|
||||
/** The action logger */
|
||||
private final FileActionLogger actionLogger;
|
||||
|
||||
// the file used to store uuid data
|
||||
private Path uuidDataFile;
|
||||
|
||||
protected AbstractConfigurateStorage(LuckPermsPlugin plugin, String implementationName, ConfigurateLoader loader, String dataDirectoryName) {
|
||||
this.plugin = plugin;
|
||||
this.implementationName = implementationName;
|
||||
this.loader = loader;
|
||||
this.dataDirectoryName = dataDirectoryName;
|
||||
|
||||
this.uuidCache = new FileUuidCache();
|
||||
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;
|
||||
|
||||
// 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
|
||||
public void init() throws IOException {
|
||||
// init the data directory and ensure it exists
|
||||
this.dataDirectory = this.plugin.getBootstrap().getDataDirectory().resolve(this.dataDirectoryName);
|
||||
MoreFiles.createDirectoriesIfNotExists(this.dataDirectory);
|
||||
|
||||
this.uuidDataFile = MoreFiles.createFileIfNotExists(this.dataDirectory.resolve("uuidcache.txt"));
|
||||
this.uuidCache.load(this.uuidDataFile);
|
||||
// setup the uuid cache
|
||||
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"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
this.uuidCache.save(this.uuidDataFile);
|
||||
this.uuidCache.save(this.uuidCacheFile);
|
||||
this.actionLogger.flush();
|
||||
}
|
||||
|
||||
@ -177,29 +168,19 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
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
|
||||
public User loadUser(UUID uniqueId, String username) {
|
||||
public User loadUser(UUID uniqueId, String username) throws IOException {
|
||||
User user = this.plugin.getUserManager().getOrMake(uniqueId, username);
|
||||
try {
|
||||
ConfigurationNode object = readFile(StorageLocation.USER, uniqueId.toString());
|
||||
if (object != null) {
|
||||
String name = object.getNode("name").getString();
|
||||
user.getPrimaryGroup().setStoredValue(object.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").getString());
|
||||
ConfigurationNode file = readFile(StorageLocation.USERS, uniqueId.toString());
|
||||
if (file != null) {
|
||||
String name = file.getNode("name").getString();
|
||||
String primaryGroup = file.getNode(this.loader instanceof JsonLoader ? "primaryGroup" : "primary-group").getString();
|
||||
|
||||
user.getPrimaryGroup().setStoredValue(primaryGroup);
|
||||
user.setUsername(name, true);
|
||||
|
||||
user.loadNodesFromStorage(readNodes(object));
|
||||
user.loadNodesFromStorage(readNodes(file));
|
||||
this.plugin.getUserManager().giveDefaultIfNeeded(user);
|
||||
|
||||
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) {
|
||||
throw reportException(uniqueId.toString(), e);
|
||||
throw new FileIOException(uniqueId.toString(), e);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveUser(User user) {
|
||||
public void saveUser(User user) throws IOException {
|
||||
user.normalData().discardChanges();
|
||||
try {
|
||||
if (!this.plugin.getUserManager().isNonDefaultUser(user)) {
|
||||
saveFile(StorageLocation.USER, user.getUniqueId().toString(), null);
|
||||
saveFile(StorageLocation.USERS, user.getUniqueId().toString(), null);
|
||||
} else {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
ConfigurationNode file = ConfigurationNode.root();
|
||||
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());
|
||||
saveFile(StorageLocation.USER, user.getUniqueId().toString(), data);
|
||||
String name = user.getUsername().orElse("null");
|
||||
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) {
|
||||
throw reportException(user.getUniqueId().toString(), e);
|
||||
throw new FileIOException(user.getUniqueId().toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Group createAndLoadGroup(String name) {
|
||||
public Group createAndLoadGroup(String name) throws IOException {
|
||||
Group group = this.plugin.getGroupManager().getOrMake(name);
|
||||
try {
|
||||
ConfigurationNode object = readFile(StorageLocation.GROUP, name);
|
||||
ConfigurationNode file = readFile(StorageLocation.GROUPS, name);
|
||||
|
||||
if (object != null) {
|
||||
group.loadNodesFromStorage(readNodes(object));
|
||||
if (file != null) {
|
||||
group.loadNodesFromStorage(readNodes(file));
|
||||
} else {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
file = ConfigurationNode.root();
|
||||
if (this instanceof SeparatedConfigurateStorage) {
|
||||
data.getNode("name").setValue(group.getName());
|
||||
file.getNode("name").setValue(group.getName());
|
||||
}
|
||||
|
||||
writeNodes(data, group.normalData().asList());
|
||||
saveFile(StorageLocation.GROUP, name, data);
|
||||
writeNodes(file, group.normalData().asList());
|
||||
saveFile(StorageLocation.GROUPS, name, file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw reportException(name, e);
|
||||
throw new FileIOException(name, e);
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Group> loadGroup(String name) {
|
||||
public Optional<Group> loadGroup(String name) throws IOException {
|
||||
try {
|
||||
ConfigurationNode object = readFile(StorageLocation.GROUP, name);
|
||||
|
||||
if (object == null) {
|
||||
ConfigurationNode file = readFile(StorageLocation.GROUPS, name);
|
||||
if (file == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Group group = this.plugin.getGroupManager().getOrMake(name);
|
||||
group.loadNodesFromStorage(readNodes(object));
|
||||
group.loadNodesFromStorage(readNodes(file));
|
||||
return Optional.of(group);
|
||||
} catch (Exception e) {
|
||||
throw reportException(name, e);
|
||||
throw new FileIOException(name, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveGroup(Group group) {
|
||||
public void saveGroup(Group group) throws IOException {
|
||||
group.normalData().discardChanges();
|
||||
try {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
ConfigurationNode file = ConfigurationNode.root();
|
||||
if (this instanceof SeparatedConfigurateStorage) {
|
||||
data.getNode("name").setValue(group.getName());
|
||||
file.getNode("name").setValue(group.getName());
|
||||
}
|
||||
|
||||
writeNodes(data, group.normalData().asList());
|
||||
saveFile(StorageLocation.GROUP, group.getName(), data);
|
||||
writeNodes(file, group.normalData().asList());
|
||||
saveFile(StorageLocation.GROUPS, group.getName(), file);
|
||||
} catch (Exception e) {
|
||||
throw reportException(group.getName(), e);
|
||||
throw new FileIOException(group.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteGroup(Group group) {
|
||||
public void deleteGroup(Group group) throws IOException {
|
||||
try {
|
||||
saveFile(StorageLocation.GROUP, group.getName(), null);
|
||||
saveFile(StorageLocation.GROUPS, group.getName(), null);
|
||||
} catch (Exception e) {
|
||||
throw reportException(group.getName(), e);
|
||||
throw new FileIOException(group.getName(), e);
|
||||
}
|
||||
this.plugin.getGroupManager().unload(group.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Track createAndLoadTrack(String name) {
|
||||
public Track createAndLoadTrack(String name) throws IOException {
|
||||
Track track = this.plugin.getTrackManager().getOrMake(name);
|
||||
try {
|
||||
ConfigurationNode object = readFile(StorageLocation.TRACK, name);
|
||||
|
||||
if (object != null) {
|
||||
List<String> groups = object.getNode("groups").getChildrenList().stream()
|
||||
.map(ConfigurationNode::getString)
|
||||
.collect(ImmutableCollectors.toList());
|
||||
|
||||
track.setGroups(groups);
|
||||
ConfigurationNode file = readFile(StorageLocation.TRACKS, name);
|
||||
if (file != null) {
|
||||
track.setGroups(file.getNode("groups").getList(Types::asString));
|
||||
} else {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
file = ConfigurationNode.root();
|
||||
if (this instanceof SeparatedConfigurateStorage) {
|
||||
data.getNode("name").setValue(name);
|
||||
file.getNode("name").setValue(name);
|
||||
}
|
||||
data.getNode("groups").setValue(track.getGroups());
|
||||
saveFile(StorageLocation.TRACK, name, data);
|
||||
file.getNode("groups").setValue(track.getGroups());
|
||||
saveFile(StorageLocation.TRACKS, name, file);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
throw reportException(name, e);
|
||||
throw new FileIOException(name, e);
|
||||
}
|
||||
return track;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Track> loadTrack(String name) {
|
||||
public Optional<Track> loadTrack(String name) throws IOException {
|
||||
try {
|
||||
ConfigurationNode object = readFile(StorageLocation.TRACK, name);
|
||||
|
||||
if (object == null) {
|
||||
ConfigurationNode file = readFile(StorageLocation.TRACKS, name);
|
||||
if (file == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
Track track = this.plugin.getTrackManager().getOrMake(name);
|
||||
List<String> groups = object.getNode("groups").getChildrenList().stream()
|
||||
.map(ConfigurationNode::getString)
|
||||
.collect(ImmutableCollectors.toList());
|
||||
track.setGroups(groups);
|
||||
track.setGroups(file.getNode("groups").getList(Types::asString));
|
||||
return Optional.of(track);
|
||||
} catch (Exception e) {
|
||||
throw reportException(name, e);
|
||||
throw new FileIOException(name, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveTrack(Track track) {
|
||||
public void saveTrack(Track track) throws IOException {
|
||||
try {
|
||||
ConfigurationNode data = SimpleConfigurationNode.root();
|
||||
ConfigurationNode file = ConfigurationNode.root();
|
||||
if (this instanceof SeparatedConfigurateStorage) {
|
||||
data.getNode("name").setValue(track.getName());
|
||||
file.getNode("name").setValue(track.getName());
|
||||
}
|
||||
data.getNode("groups").setValue(track.getGroups());
|
||||
saveFile(StorageLocation.TRACK, track.getName(), data);
|
||||
file.getNode("groups").setValue(track.getGroups());
|
||||
saveFile(StorageLocation.TRACKS, track.getName(), file);
|
||||
} catch (Exception e) {
|
||||
throw reportException(track.getName(), e);
|
||||
throw new FileIOException(track.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteTrack(Track track) {
|
||||
public void deleteTrack(Track track) throws IOException {
|
||||
try {
|
||||
saveFile(StorageLocation.TRACK, track.getName(), null);
|
||||
saveFile(StorageLocation.TRACKS, track.getName(), null);
|
||||
} catch (Exception e) {
|
||||
throw reportException(track.getName(), e);
|
||||
throw new FileIOException(track.getName(), e);
|
||||
}
|
||||
this.plugin.getTrackManager().unload(track.getName());
|
||||
}
|
||||
@ -398,71 +372,54 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
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) {
|
||||
ImmutableContextSet.Builder contextBuilder = new ImmutableContextSetImpl.BuilderImpl();
|
||||
ConfigurationNode contextMap = attributes.getNode("context");
|
||||
if (!contextMap.isVirtual() && contextMap.hasMapChildren()) {
|
||||
if (!contextMap.isVirtual() && contextMap.isMap()) {
|
||||
contextBuilder.addAll(ContextSetConfigurateSerializer.deserializeContextSet(contextMap));
|
||||
}
|
||||
|
||||
String server = attributes.getNode("server").getString("global");
|
||||
if (!server.equals("global")) {
|
||||
contextBuilder.add(DefaultContextKeys.SERVER_KEY, server);
|
||||
}
|
||||
|
||||
String world = attributes.getNode("world").getString("global");
|
||||
if (!world.equals("global")) {
|
||||
contextBuilder.add(DefaultContextKeys.WORLD_KEY, world);
|
||||
}
|
||||
|
||||
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);
|
||||
Instant expiry = expiryVal == 0L ? null : Instant.ofEpochSecond(expiryVal);
|
||||
ImmutableContextSet context = readContexts(attributes);
|
||||
|
||||
return permissionFunction.apply(attributes)
|
||||
.expiry(expiry)
|
||||
.context(context)
|
||||
.build();
|
||||
return builder.expiry(expiry).context(context).build();
|
||||
}
|
||||
|
||||
private static Collection<Node> readAttributes(ConfigurationNode attributes, String permission) {
|
||||
boolean value = attributes.getNode("value").getBoolean(true);
|
||||
long expiryVal = attributes.getNode("expiry").getLong(0L);
|
||||
Instant expiry = expiryVal == 0L ? null : Instant.ofEpochSecond(expiryVal);
|
||||
ImmutableContextSet context = readContexts(attributes);
|
||||
private static final class NodeEntry {
|
||||
final String key;
|
||||
final ConfigurationNode attributes;
|
||||
|
||||
ConfigurationNode batchAttribute = attributes.getNode("permissions");
|
||||
if (permission.startsWith("luckperms.batch") && !batchAttribute.isVirtual() && batchAttribute.hasListChildren()) {
|
||||
List<Node> nodes = new ArrayList<>();
|
||||
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 NodeEntry(String key, ConfigurationNode attributes) {
|
||||
this.key = key;
|
||||
this.attributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
private static Map.Entry<String, ConfigurationNode> parseEntry(ConfigurationNode appended, String keyFieldName) {
|
||||
if (!appended.hasMapChildren()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<Object, ? extends ConfigurationNode> children = appended.getChildrenMap();
|
||||
private static NodeEntry parseNode(ConfigurationNode configNode, String keyFieldName) {
|
||||
Map<Object, ? extends ConfigurationNode> children = configNode.getChildrenMap();
|
||||
if (children.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@ -476,88 +433,93 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
ConfigurationNode attributes = entry.getValue();
|
||||
|
||||
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);
|
||||
if (permission == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Maps.immutableEntry(permission, appended);
|
||||
return new NodeEntry(permission, configNode);
|
||||
}
|
||||
|
||||
protected static Set<Node> readNodes(ConfigurationNode data) {
|
||||
Set<Node> nodes = new HashSet<>();
|
||||
|
||||
if (data.getNode("permissions").hasListChildren()) {
|
||||
List<? extends ConfigurationNode> children = data.getNode("permissions").getChildrenList();
|
||||
for (ConfigurationNode appended : children) {
|
||||
for (ConfigurationNode appended : data.getNode("permissions").getChildrenList()) {
|
||||
String plainValue = appended.getValue(Types::strictAsString);
|
||||
if (plainValue != null) {
|
||||
nodes.add(NodeBuilders.determineMostApplicable(plainValue).build());
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "permission");
|
||||
NodeEntry entry = parseNode(appended, "permission");
|
||||
if (entry == null) {
|
||||
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()) {
|
||||
List<? extends ConfigurationNode> children = data.getNode("parents").getChildrenList();
|
||||
for (ConfigurationNode appended : children) {
|
||||
for (ConfigurationNode appended : data.getNode("parents").getChildrenList()) {
|
||||
String plainValue = appended.getValue(Types::strictAsString);
|
||||
if (plainValue != null) {
|
||||
nodes.add(Inheritance.builder(plainValue).build());
|
||||
continue;
|
||||
}
|
||||
|
||||
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "group");
|
||||
NodeEntry entry = parseNode(appended, "group");
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
nodes.add(readMetaAttributes(entry.getValue(), c -> Inheritance.builder(entry.getKey())));
|
||||
}
|
||||
}
|
||||
|
||||
if (data.getNode("prefixes").hasListChildren()) {
|
||||
List<? extends ConfigurationNode> children = data.getNode("prefixes").getChildrenList();
|
||||
for (ConfigurationNode appended : children) {
|
||||
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))));
|
||||
}
|
||||
nodes.add(readAttributes(
|
||||
Inheritance.builder(entry.key),
|
||||
entry.attributes
|
||||
));
|
||||
}
|
||||
|
||||
if (data.getNode("suffixes").hasListChildren()) {
|
||||
List<? extends ConfigurationNode> children = data.getNode("suffixes").getChildrenList();
|
||||
for (ConfigurationNode appended : children) {
|
||||
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "suffix");
|
||||
for (ConfigurationNode appended : data.getNode("prefixes").getChildrenList()) {
|
||||
NodeEntry entry = parseNode(appended, "prefix");
|
||||
if (entry == null) {
|
||||
continue;
|
||||
}
|
||||
nodes.add(readMetaAttributes(entry.getValue(), c -> Suffix.builder(entry.getKey(), c.getNode("priority").getInt(0))));
|
||||
}
|
||||
}
|
||||
|
||||
if (data.getNode("meta").hasListChildren()) {
|
||||
List<? extends ConfigurationNode> children = data.getNode("meta").getChildrenList();
|
||||
for (ConfigurationNode appended : children) {
|
||||
Map.Entry<String, ConfigurationNode> entry = parseEntry(appended, "key");
|
||||
nodes.add(readAttributes(
|
||||
Prefix.builder(entry.key, entry.attributes.getNode("priority").getInt(0)),
|
||||
entry.attributes
|
||||
));
|
||||
}
|
||||
|
||||
for (ConfigurationNode appended : data.getNode("suffixes").getChildrenList()) {
|
||||
NodeEntry entry = parseNode(appended, "suffix");
|
||||
if (entry == null) {
|
||||
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;
|
||||
@ -582,7 +544,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
}
|
||||
|
||||
private void appendNode(ConfigurationNode base, String key, ConfigurationNode attributes, String keyFieldName) {
|
||||
ConfigurationNode appended = base.getAppendedNode();
|
||||
ConfigurationNode appended = base.appendListNode();
|
||||
if (this.loader instanceof YamlLoader) {
|
||||
// create a map node with a single entry of key --> attributes
|
||||
appended.getNode(key).setValue(attributes);
|
||||
@ -594,7 +556,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
}
|
||||
|
||||
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*
|
||||
// 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());
|
||||
}
|
||||
|
||||
ConfigurationNode parentsSection = SimpleConfigurationNode.root();
|
||||
ConfigurationNode prefixesSection = SimpleConfigurationNode.root();
|
||||
ConfigurationNode suffixesSection = SimpleConfigurationNode.root();
|
||||
ConfigurationNode metaSection = SimpleConfigurationNode.root();
|
||||
ConfigurationNode parentsSection = ConfigurationNode.root();
|
||||
ConfigurationNode prefixesSection = ConfigurationNode.root();
|
||||
ConfigurationNode suffixesSection = ConfigurationNode.root();
|
||||
ConfigurationNode metaSection = ConfigurationNode.root();
|
||||
|
||||
for (Node n : nodes) {
|
||||
// just add a string to the list.
|
||||
if (this.loader instanceof YamlLoader && isPlain(n)) {
|
||||
if (n instanceof InheritanceNode) {
|
||||
parentsSection.getAppendedNode().setValue(((InheritanceNode) n).getGroupName());
|
||||
parentsSection.appendListNode().setValue(((InheritanceNode) n).getGroupName());
|
||||
continue;
|
||||
}
|
||||
if (!NodeType.META_OR_CHAT_META.matches(n)) {
|
||||
permissionsSection.getAppendedNode().setValue(n.getKey());
|
||||
permissionsSection.appendListNode().setValue(n.getKey());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -624,7 +586,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
// handle prefixes / suffixes
|
||||
ChatMetaNode<?, ?> chatMeta = (ChatMetaNode<?, ?>) n;
|
||||
|
||||
ConfigurationNode attributes = SimpleConfigurationNode.root();
|
||||
ConfigurationNode attributes = ConfigurationNode.root();
|
||||
attributes.getNode("priority").setValue(chatMeta.getPriority());
|
||||
writeAttributesTo(attributes, n, false);
|
||||
|
||||
@ -642,7 +604,7 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
// handle meta nodes
|
||||
MetaNode meta = (MetaNode) n;
|
||||
|
||||
ConfigurationNode attributes = SimpleConfigurationNode.root();
|
||||
ConfigurationNode attributes = ConfigurationNode.root();
|
||||
attributes.getNode("value").setValue(meta.getMetaValue());
|
||||
writeAttributesTo(attributes, n, false);
|
||||
|
||||
@ -651,44 +613,44 @@ public abstract class AbstractConfigurateStorage implements StorageImplementatio
|
||||
// handle group nodes
|
||||
InheritanceNode inheritance = (InheritanceNode) n;
|
||||
|
||||
ConfigurationNode attributes = SimpleConfigurationNode.root();
|
||||
ConfigurationNode attributes = ConfigurationNode.root();
|
||||
writeAttributesTo(attributes, n, false);
|
||||
|
||||
appendNode(parentsSection, inheritance.getGroupName(), attributes, "group");
|
||||
} else {
|
||||
// handle regular permissions and negated meta+prefixes+suffixes
|
||||
ConfigurationNode attributes = SimpleConfigurationNode.root();
|
||||
ConfigurationNode attributes = ConfigurationNode.root();
|
||||
writeAttributesTo(attributes, n, true);
|
||||
|
||||
appendNode(permissionsSection, n.getKey(), attributes, "permission");
|
||||
}
|
||||
}
|
||||
|
||||
if (permissionsSection.hasListChildren() || this instanceof CombinedConfigurateStorage) {
|
||||
if (permissionsSection.isList() || this instanceof CombinedConfigurateStorage) {
|
||||
to.getNode("permissions").setValue(permissionsSection);
|
||||
} else {
|
||||
to.removeChild("permissions");
|
||||
}
|
||||
|
||||
if (parentsSection.hasListChildren()) {
|
||||
if (parentsSection.isList()) {
|
||||
to.getNode("parents").setValue(parentsSection);
|
||||
} else {
|
||||
to.removeChild("parents");
|
||||
}
|
||||
|
||||
if (prefixesSection.hasListChildren()) {
|
||||
if (prefixesSection.isList()) {
|
||||
to.getNode("prefixes").setValue(prefixesSection);
|
||||
} else {
|
||||
to.removeChild("prefixes");
|
||||
}
|
||||
|
||||
if (suffixesSection.hasListChildren()) {
|
||||
if (suffixesSection.isList()) {
|
||||
to.getNode("suffixes").setValue(suffixesSection);
|
||||
} else {
|
||||
to.removeChild("suffixes");
|
||||
}
|
||||
|
||||
if (metaSection.hasListChildren()) {
|
||||
if (metaSection.isList()) {
|
||||
to.getNode("meta").setValue(metaSection);
|
||||
} else {
|
||||
to.removeChild("meta");
|
||||
|
@ -52,33 +52,221 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Consumer;
|
||||
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 {
|
||||
private final String fileExtension;
|
||||
|
||||
private Path usersFile;
|
||||
private Path groupsFile;
|
||||
private Path tracksFile;
|
||||
private CachedLoader users;
|
||||
private CachedLoader groups;
|
||||
private CachedLoader tracks;
|
||||
private FileWatcher.WatchedLocation watcher = null;
|
||||
|
||||
private CachedLoader usersLoader;
|
||||
private CachedLoader groupsLoader;
|
||||
private CachedLoader tracksLoader;
|
||||
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 = 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 Path path;
|
||||
|
||||
private final Path file;
|
||||
private final ConfigurationLoader<? extends ConfigurationNode> loader;
|
||||
private ConfigurationNode node = null;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private ConfigurationNode node = null;
|
||||
|
||||
private CachedLoader(Path path) {
|
||||
this.path = path;
|
||||
this.loader = CombinedConfigurateStorage.super.loader.loader(path);
|
||||
private CachedLoader(Path file) {
|
||||
this.file = file;
|
||||
this.loader = CombinedConfigurateStorage.super.loader.loader(file);
|
||||
reload();
|
||||
}
|
||||
|
||||
private void recordChange() {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -59,44 +59,6 @@ public class FileUuidCache {
|
||||
// the lookup map
|
||||
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
|
||||
*
|
||||
@ -154,6 +116,40 @@ public class FileUuidCache {
|
||||
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) {
|
||||
if (entry.contains(":")) {
|
||||
// new format
|
||||
@ -193,37 +189,41 @@ public class FileUuidCache {
|
||||
}
|
||||
}
|
||||
|
||||
public void load(Path file) {
|
||||
if (!Files.exists(file)) {
|
||||
return;
|
||||
}
|
||||
private static final class LookupMap extends ConcurrentHashMap<UUID, String> {
|
||||
private final SetMultimap<String, UUID> reverse = Multimaps.newSetMultimap(new ConcurrentHashMap<>(), ConcurrentHashMap::newKeySet);
|
||||
|
||||
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();
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
this.reverse.put(value.toLowerCase(), key);
|
||||
return existing;
|
||||
}
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
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.model.HolderType;
|
||||
import me.lucko.luckperms.common.model.User;
|
||||
@ -45,7 +47,9 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@ -53,30 +57,38 @@ import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
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 {
|
||||
private final String fileExtension;
|
||||
private final Predicate<Path> fileExtensionFilter;
|
||||
|
||||
private Path usersDirectory;
|
||||
private Path groupsDirectory;
|
||||
private Path tracksDirectory;
|
||||
private final Map<StorageLocation, FileGroup> fileGroups;
|
||||
private final FileGroup users;
|
||||
private final FileGroup groups;
|
||||
private final FileGroup tracks;
|
||||
|
||||
private FileWatcher.WatchedLocation userWatcher = null;
|
||||
private FileWatcher.WatchedLocation groupWatcher = null;
|
||||
private FileWatcher.WatchedLocation trackWatcher = null;
|
||||
private static final class FileGroup {
|
||||
private Path directory;
|
||||
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) {
|
||||
super(plugin, implementationName, loader, dataFolderName);
|
||||
this.fileExtension = 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
|
||||
@ -111,37 +123,13 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
}
|
||||
|
||||
private Path getDirectory(StorageLocation location) {
|
||||
switch (location) {
|
||||
case USER:
|
||||
return this.usersDirectory;
|
||||
case GROUP:
|
||||
return this.groupsDirectory;
|
||||
case TRACK:
|
||||
return this.tracksDirectory;
|
||||
default:
|
||||
throw new RuntimeException();
|
||||
}
|
||||
return this.fileGroups.get(location).directory;
|
||||
}
|
||||
|
||||
private void registerFileAction(StorageLocation type, Path file) {
|
||||
switch (type) {
|
||||
case USER:
|
||||
if (this.userWatcher != null) {
|
||||
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();
|
||||
FileWatcher.WatchedLocation watcher = this.fileGroups.get(type).watcher;
|
||||
if (watcher != null) {
|
||||
watcher.recordChange(file.getFileName().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,22 +137,21 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
public void init() throws IOException {
|
||||
super.init();
|
||||
|
||||
this.usersDirectory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("users"));
|
||||
this.groupsDirectory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("groups"));
|
||||
this.tracksDirectory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("tracks"));
|
||||
this.users.directory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("users"));
|
||||
this.groups.directory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("groups"));
|
||||
this.tracks.directory = MoreFiles.createDirectoryIfNotExists(super.dataDirectory.resolve("tracks"));
|
||||
|
||||
// Listen for file changes.
|
||||
FileWatcher watcher = this.plugin.getFileWatcher().orElse(null);
|
||||
if (watcher != null) {
|
||||
this.userWatcher = watcher.getWatcher(this.usersDirectory);
|
||||
this.userWatcher.addListener(path -> {
|
||||
String s = path.getFileName().toString();
|
||||
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
this.users.watcher = watcher.getWatcher(this.users.directory);
|
||||
this.users.watcher.addListener(path -> {
|
||||
String fileName = path.getFileName().toString();
|
||||
if (!fileName.endsWith(this.fileExtension)) {
|
||||
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);
|
||||
if (uuid == null) {
|
||||
return;
|
||||
@ -177,28 +164,26 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
}
|
||||
});
|
||||
|
||||
this.groupWatcher = watcher.getWatcher(this.groupsDirectory);
|
||||
this.groupWatcher.addListener(path -> {
|
||||
String s = path.getFileName().toString();
|
||||
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
this.groups.watcher = watcher.getWatcher(this.groups.directory);
|
||||
this.groups.watcher.addListener(path -> {
|
||||
String fileName = path.getFileName().toString();
|
||||
if (!fileName.endsWith(this.fileExtension)) {
|
||||
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.getSyncTaskBuffer().request();
|
||||
});
|
||||
|
||||
this.trackWatcher = watcher.getWatcher(this.tracksDirectory);
|
||||
this.trackWatcher.addListener(path -> {
|
||||
String s = path.getFileName().toString();
|
||||
|
||||
if (!s.endsWith(this.fileExtension)) {
|
||||
this.tracks.watcher = watcher.getWatcher(this.tracks.directory);
|
||||
this.tracks.watcher.addListener(path -> {
|
||||
String fileName = path.getFileName().toString();
|
||||
if (!fileName.endsWith(this.fileExtension)) {
|
||||
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.getStorage().loadAllTracks();
|
||||
});
|
||||
@ -208,32 +193,38 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
@Override
|
||||
public void applyBulkUpdate(BulkUpdate bulkUpdate) throws Exception {
|
||||
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 -> {
|
||||
try {
|
||||
registerFileAction(StorageLocation.USER, file);
|
||||
registerFileAction(StorageLocation.USERS, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
if (processBulkUpdate(bulkUpdate, object, HolderType.USER)) {
|
||||
saveFile(file, object);
|
||||
}
|
||||
} 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()) {
|
||||
try (Stream<Path> s = Files.list(getDirectory(StorageLocation.GROUP))) {
|
||||
try (Stream<Path> s = Files.list(getDirectory(StorageLocation.GROUPS))) {
|
||||
s.filter(this.fileExtensionFilter).forEach(file -> {
|
||||
try {
|
||||
registerFileAction(StorageLocation.GROUP, file);
|
||||
registerFileAction(StorageLocation.GROUPS, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
if (processBulkUpdate(bulkUpdate, object, HolderType.GROUP)) {
|
||||
saveFile(file, object);
|
||||
}
|
||||
} 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
|
||||
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)
|
||||
.map(p -> p.getFileName().toString())
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
@ -253,14 +244,14 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
}
|
||||
|
||||
@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<>();
|
||||
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.USER))) {
|
||||
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.USERS))) {
|
||||
stream.filter(this.fileExtensionFilter)
|
||||
.forEach(file -> {
|
||||
String fileName = file.getFileName().toString();
|
||||
try {
|
||||
registerFileAction(StorageLocation.USER, file);
|
||||
registerFileAction(StorageLocation.USERS, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
UUID holder = UUID.fromString(fileName.substring(0, fileName.length() - this.fileExtension.length()));
|
||||
Set<Node> nodes = readNodes(object);
|
||||
@ -271,7 +262,10 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
}
|
||||
}
|
||||
} 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
|
||||
public void loadAllGroups() throws IOException {
|
||||
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)
|
||||
.map(p -> p.getFileName().toString())
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
@ -296,14 +290,14 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
}
|
||||
|
||||
@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<>();
|
||||
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.GROUP))) {
|
||||
try (Stream<Path> stream = Files.list(getDirectory(StorageLocation.GROUPS))) {
|
||||
stream.filter(this.fileExtensionFilter)
|
||||
.forEach(file -> {
|
||||
String fileName = file.getFileName().toString();
|
||||
try {
|
||||
registerFileAction(StorageLocation.GROUP, file);
|
||||
registerFileAction(StorageLocation.GROUPS, file);
|
||||
ConfigurationNode object = readFile(file);
|
||||
String holder = fileName.substring(0, fileName.length() - this.fileExtension.length());
|
||||
Set<Node> nodes = readNodes(object);
|
||||
@ -314,7 +308,10 @@ public class SeparatedConfigurateStorage extends AbstractConfigurateStorage {
|
||||
}
|
||||
}
|
||||
} 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
|
||||
public void loadAllTracks() throws IOException {
|
||||
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)
|
||||
.map(p -> p.getFileName().toString())
|
||||
.map(s -> s.substring(0, s.length() - this.fileExtension.length()))
|
||||
|
@ -27,6 +27,6 @@ package me.lucko.luckperms.common.storage.implementation.file;
|
||||
|
||||
public enum StorageLocation {
|
||||
|
||||
USER, GROUP, TRACK
|
||||
USERS, GROUPS, TRACKS
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import net.luckperms.api.node.Node;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
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) {
|
||||
|
@ -185,7 +185,7 @@ public class TreeView {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key();
|
||||
return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE).key();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ public class VerboseListener {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE, false).key();
|
||||
return bytebin.postContent(bytesOut.toByteArray(), AbstractHttpClient.JSON_TYPE).key();
|
||||
}
|
||||
|
||||
public Sender getNotifiedSender() {
|
||||
|
@ -155,7 +155,7 @@ public class WebEditorRequest {
|
||||
public CommandResult createSession(LuckPermsPlugin plugin, Sender sender) {
|
||||
String pasteId;
|
||||
try {
|
||||
pasteId = plugin.getBytebin().postContent(encode(), AbstractHttpClient.JSON_TYPE, false).key();
|
||||
pasteId = plugin.getBytebin().postContent(encode(), AbstractHttpClient.JSON_TYPE).key();
|
||||
} catch (UnsuccessfulRequestException e) {
|
||||
Message.EDITOR_HTTP_REQUEST_FAILURE.send(sender, e.getResponse().code(), e.getResponse().message());
|
||||
return CommandResult.STATE_ERROR;
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -67,7 +67,7 @@ public class NukkitAutoOpListener implements LuckPermsEventListener {
|
||||
}
|
||||
|
||||
private void refreshAutoOp(Player player) {
|
||||
User user = plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||
User user = this.plugin.getUserManager().getIfLoaded(player.getUniqueId());
|
||||
boolean value;
|
||||
|
||||
if (user != null) {
|
||||
|
@ -25,8 +25,7 @@
|
||||
|
||||
package me.lucko.luckperms.velocity;
|
||||
|
||||
import com.velocitypowered.api.command.Command;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.RawCommand;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
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.sender.Sender;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class VelocityCommandExecutor extends CommandManager implements Command {
|
||||
/** The command aliases */
|
||||
private static final String[] ALIASES = {"luckpermsvelocity", "lpv", "vperm", "vperms", "vpermission", "vpermissions"};
|
||||
public class VelocityCommandExecutor extends CommandManager implements RawCommand {
|
||||
/* The command aliases */
|
||||
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 final LPVelocityPlugin plugin;
|
||||
@ -55,51 +54,46 @@ public class VelocityCommandExecutor extends CommandManager implements Command {
|
||||
|
||||
public void register() {
|
||||
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'.
|
||||
proxy.getCommandManager().register(new ForwardingCommand(this) {
|
||||
proxy.getCommandManager().register(SLASH_PRIMARY_ALIAS, new ForwardingCommand(this) {
|
||||
@Override
|
||||
public boolean hasPermission(CommandSource source, @NonNull String[] args) {
|
||||
return source instanceof ConsoleCommandSource;
|
||||
public boolean hasPermission(Invocation invocation) {
|
||||
return invocation.source() instanceof ConsoleCommandSource;
|
||||
}
|
||||
}, SLASH_ALIASES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(source);
|
||||
List<String> arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(args);
|
||||
public void execute(Invocation invocation) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(invocation.source());
|
||||
List<String> arguments = ArgumentTokenizer.EXECUTE.tokenizeInput(invocation.arguments());
|
||||
executeCommand(wrapped, "lpv", arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(@NonNull CommandSource source, @NonNull String[] args) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(source);
|
||||
List<String> arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(args);
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
Sender wrapped = this.plugin.getSenderFactory().wrap(invocation.source());
|
||||
List<String> arguments = ArgumentTokenizer.TAB_COMPLETE.tokenizeInput(invocation.arguments());
|
||||
return tabCompleteCommand(wrapped, arguments);
|
||||
}
|
||||
|
||||
private static class ForwardingCommand implements Command {
|
||||
private final Command delegate;
|
||||
private static class ForwardingCommand implements RawCommand {
|
||||
private final RawCommand delegate;
|
||||
|
||||
private ForwardingCommand(Command delegate) {
|
||||
private ForwardingCommand(RawCommand delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSource source, @NonNull String[] args) {
|
||||
this.delegate.execute(source, args);
|
||||
public void execute(Invocation invocation) {
|
||||
this.delegate.execute(invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(CommandSource source, @NonNull String[] currentArgs) {
|
||||
return this.delegate.suggest(source, currentArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(CommandSource source, @NonNull String[] args) {
|
||||
return this.delegate.hasPermission(source, args);
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
return this.delegate.suggest(invocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ public final class AdventureCompat {
|
||||
static {
|
||||
String adventurePkg = "net.kyo".concat("ri.adventure.");
|
||||
try {
|
||||
if (classExists(adventurePkg + "audience.Audience")) {
|
||||
Class<?> audienceClass = Class.forName(adventurePkg + "audience.Audience");
|
||||
Class<?> componentClass = Class.forName(adventurePkg + "text.Component");
|
||||
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_COMPONENT_RESULT_DENIED = ComponentResult.class.getMethod("denied", componentClass);
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user