diff --git a/api/src/main/java/net/luckperms/api/context/Context.java b/api/src/main/java/net/luckperms/api/context/Context.java
index e56a85d03..1d26dfaf0 100644
--- a/api/src/main/java/net/luckperms/api/context/Context.java
+++ b/api/src/main/java/net/luckperms/api/context/Context.java
@@ -26,12 +26,62 @@
package net.luckperms.api.context;
import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents an individual context pair.
+ *
+ *
Context keys and values may not be null or empty. A key/value will be
+ * deemed empty if it's length is zero, or if it consists of only space
+ * characters.
*/
public interface Context {
+ /**
+ * Tests whether {@code key} is valid.
+ *
+ * Context keys and values may not be null or empty. A key/value will be
+ * deemed empty if it's length is zero, or if it consists of only space
+ * characters.
+ *
+ * An exception is thrown when an invalid key is added to a {@link ContextSet}.
+ *
+ * @param key the key to test
+ * @return true if valid, false otherwise.
+ * @since 5.1
+ */
+ static boolean isValidKey(@Nullable String key) {
+ if (key == null || key.isEmpty()) {
+ return false;
+ }
+
+ // look for a non-whitespace character
+ for (int i = 0, n = key.length(); i < n; i++) {
+ if (key.charAt(i) != ' ') {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Tests whether {@code value} is valid.
+ *
+ * Context keys and values may not be null or empty. A key/value will be
+ * deemed empty if it's length is zero, or if it consists of only space
+ * characters.
+ *
+ * An exception is thrown when an invalid value is added to a {@link ContextSet}.
+ *
+ * @param value the value to test
+ * @return true if valid, false otherwise.
+ * @since 5.1
+ */
+ static boolean isValidValue(@Nullable String value) {
+ return isValidKey(value); // the same for now...
+ }
+
/**
* Gets the context key.
*
diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java
index 9878b6450..938420829 100644
--- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java
+++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/context/WorldCalculator.java
@@ -29,6 +29,7 @@ import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.context.contextset.ImmutableContextSetImpl;
+import net.luckperms.api.context.Context;
import net.luckperms.api.context.ContextCalculator;
import net.luckperms.api.context.ContextConsumer;
import net.luckperms.api.context.ContextSet;
@@ -54,7 +55,9 @@ public class WorldCalculator implements ContextCalculator {
public void calculate(@NonNull Player subject, @NonNull ContextConsumer consumer) {
Set seen = new HashSet<>();
String world = subject.getWorld().getName().toLowerCase();
- while (seen.add(world)) {
+ // seems like world names can sometimes be the empty string
+ // see: https://github.com/lucko/LuckPerms/issues/2119
+ while (Context.isValidValue(world) && seen.add(world)) {
consumer.accept(DefaultContextKeys.WORLD_KEY, world);
world = this.plugin.getConfiguration().get(ConfigKeys.WORLD_REWRITES).getOrDefault(world, world).toLowerCase();
}
@@ -66,7 +69,7 @@ public class WorldCalculator implements ContextCalculator {
ImmutableContextSet.Builder builder = new ImmutableContextSetImpl.BuilderImpl();
for (World world : worlds) {
String name = world.getName().toLowerCase();
- if (!name.trim().isEmpty()) {
+ if (Context.isValidValue(name)) {
builder.add(DefaultContextKeys.WORLD_KEY, name);
}
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java
index 82f8eb2c1..334e188b8 100644
--- a/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java
+++ b/common/src/main/java/me/lucko/luckperms/common/command/utils/ArgumentParser.java
@@ -39,6 +39,7 @@ import me.lucko.luckperms.common.sender.Sender;
import me.lucko.luckperms.common.storage.misc.DataConstraints;
import me.lucko.luckperms.common.util.DurationParser;
+import net.luckperms.api.context.Context;
import net.luckperms.api.context.DefaultContextKeys;
import net.luckperms.api.context.ImmutableContextSet;
import net.luckperms.api.context.MutableContextSet;
@@ -192,10 +193,8 @@ public class ArgumentParser {
value = entry;
}
- if (AbstractContextSet.stringIsEmpty(key) ||
- AbstractContextSet.stringIsEmpty(value) ||
- // TODO reconsider a better place to insert / avoid this special case
- AbstractContextSet.shouldIgnoreEntry(key, value)) {
+ // TODO reconsider a better place to insert / avoid this special case
+ if (!Context.isValidKey(key) || !Context.isValidValue(value) || AbstractContextSet.isGlobalServerWorldEntry(key, value)) {
continue;
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java b/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java
index 2794fb012..2a73bfb01 100644
--- a/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java
+++ b/common/src/main/java/me/lucko/luckperms/common/context/contextset/AbstractContextSet.java
@@ -28,6 +28,7 @@ package me.lucko.luckperms.common.context.contextset;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
+import net.luckperms.api.context.Context;
import net.luckperms.api.context.ContextSet;
import net.luckperms.api.context.DefaultContextKeys;
@@ -76,7 +77,7 @@ public abstract class AbstractContextSet implements ContextSet {
static String sanitizeKey(String key) {
Objects.requireNonNull(key, "key is null");
- if (stringIsEmpty(key)) {
+ if (!Context.isValidKey(key)) {
throw new IllegalArgumentException("key is (effectively) empty");
}
return key.toLowerCase();
@@ -84,25 +85,13 @@ public abstract class AbstractContextSet implements ContextSet {
static String sanitizeValue(String value) {
Objects.requireNonNull(value, "value is null");
- if (stringIsEmpty(value)) {
+ if (!Context.isValidValue(value)) {
throw new IllegalArgumentException("value is (effectively) empty");
}
return value.toLowerCase();
}
- public static boolean stringIsEmpty(String s) {
- if (s.isEmpty()) {
- return true;
- }
- for (char c : s.toCharArray()) {
- if (c != ' ') {
- return false;
- }
- }
- return true;
- }
-
- public static boolean shouldIgnoreEntry(String key, String value) {
+ public static boolean isGlobalServerWorldEntry(String key, String value) {
return (key.equalsIgnoreCase(DefaultContextKeys.SERVER_KEY) || key.equalsIgnoreCase(DefaultContextKeys.WORLD_KEY)) && value.equalsIgnoreCase("global");
}
diff --git a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java
index b29650fe8..f8df7866e 100644
--- a/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java
+++ b/common/src/main/java/me/lucko/luckperms/common/node/AbstractNodeBuilder.java
@@ -119,7 +119,7 @@ public abstract class AbstractNodeBuilder, B extends
@Override
public @NonNull B withContext(@NonNull String key, @NonNull String value) {
// TODO reconsider a better place to insert / avoid this special case
- if (AbstractContextSet.shouldIgnoreEntry(key, value)) {
+ if (AbstractContextSet.isGlobalServerWorldEntry(key, value)) {
return (B) this;
}
this.context.add(key, value);