Add force-remove command for Multiverse inventory groups
This commit is contained in:
11
README.md
11
README.md
@@ -28,12 +28,12 @@ silently replace that inventory.
|
|||||||
mvn clean package
|
mvn clean package
|
||||||
```
|
```
|
||||||
|
|
||||||
The finished plugin is `target/AntiInventories-1.0.0.jar`.
|
The finished plugin is `target/AntiInventories-1.1.0.jar`.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
1. Back up the Multiverse-Inventories data directory.
|
1. Back up the Multiverse-Inventories data directory.
|
||||||
2. Put `AntiInventories-1.0.0.jar` in the server's `plugins` directory.
|
2. Put `AntiInventories-1.1.0.jar` in the server's `plugins` directory.
|
||||||
3. Make sure Multiverse-Core and Multiverse-Inventories are installed.
|
3. Make sure Multiverse-Core and Multiverse-Inventories are installed.
|
||||||
4. Start the server.
|
4. Start the server.
|
||||||
5. Edit `plugins/AntiInventories/config.yml`, or use the commands below.
|
5. Edit `plugins/AntiInventories/config.yml`, or use the commands below.
|
||||||
@@ -44,12 +44,19 @@ The finished plugin is `target/AntiInventories-1.0.0.jar`.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `/antiinv add <world>` | Add a bypass world |
|
| `/antiinv add <world>` | Add a bypass world |
|
||||||
| `/antiinv remove <world>` | Remove a bypass world |
|
| `/antiinv remove <world>` | Remove a bypass world |
|
||||||
|
| `/antiinv forceremove <world>` | Remove the world from every explicit Multiverse-Inventories group and AntiInventories, then reload both plugins |
|
||||||
| `/antiinv list` | Show bypass worlds and direction |
|
| `/antiinv list` | Show bypass worlds and direction |
|
||||||
| `/antiinv reload` | Reload `config.yml` |
|
| `/antiinv reload` | Reload `config.yml` |
|
||||||
|
|
||||||
The aliases `/antiinventories`, `/antiinv`, and `/ai` are available. Commands
|
The aliases `/antiinventories`, `/antiinv`, and `/ai` are available. Commands
|
||||||
require `antiinventories.admin`, which defaults to server operators.
|
require `antiinventories.admin`, which defaults to server operators.
|
||||||
|
|
||||||
|
`/antiinv list` also shows a clickable **Force remove** control beside each
|
||||||
|
bypass world. It places the force-removal command in chat for confirmation.
|
||||||
|
If a world belongs to a Multiverse-Inventories group through a wildcard or
|
||||||
|
regular expression, the command stops atomically and reports that pattern
|
||||||
|
instead of changing it and potentially removing other worlds.
|
||||||
|
|
||||||
## Direction setting
|
## Direction setting
|
||||||
|
|
||||||
`bypass-direction` controls which crossings are ignored:
|
`bypass-direction` controls which crossings are ignored:
|
||||||
|
|||||||
2
pom.xml
2
pom.xml
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>dev.antiinventories</groupId>
|
<groupId>dev.antiinventories</groupId>
|
||||||
<artifactId>anti-inventories</artifactId>
|
<artifactId>anti-inventories</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.1.0</version>
|
||||||
<name>AntiInventories</name>
|
<name>AntiInventories</name>
|
||||||
<description>Bypass Multiverse-Inventories for selected worlds.</description>
|
<description>Bypass Multiverse-Inventories for selected worlds.</description>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package dev.antiinventories;
|
package dev.antiinventories;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.event.ClickEvent;
|
||||||
|
import net.kyori.adventure.text.event.HoverEvent;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
@@ -15,14 +19,19 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
||||||
private static final List<String> SUBCOMMANDS = List.of("add", "remove", "list", "reload");
|
private static final List<String> SUBCOMMANDS = List.of("add", "remove", "forceremove", "list", "reload");
|
||||||
|
|
||||||
private final AntiInventoriesPlugin plugin;
|
private final AntiInventoriesPlugin plugin;
|
||||||
private final BypassSettings settings;
|
private final BypassSettings settings;
|
||||||
|
private final MultiverseInventoriesHook multiverseHook;
|
||||||
|
|
||||||
AntiInventoriesCommand(AntiInventoriesPlugin plugin, BypassSettings settings) {
|
AntiInventoriesCommand(
|
||||||
|
AntiInventoriesPlugin plugin,
|
||||||
|
BypassSettings settings,
|
||||||
|
MultiverseInventoriesHook multiverseHook) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.multiverseHook = multiverseHook;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -39,6 +48,7 @@ final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
|||||||
return switch (args[0].toLowerCase(Locale.ROOT)) {
|
return switch (args[0].toLowerCase(Locale.ROOT)) {
|
||||||
case "add" -> addWorld(sender, label, args);
|
case "add" -> addWorld(sender, label, args);
|
||||||
case "remove" -> removeWorld(sender, label, args);
|
case "remove" -> removeWorld(sender, label, args);
|
||||||
|
case "forceremove" -> forceRemoveWorld(sender, label, args);
|
||||||
case "list" -> listWorlds(sender);
|
case "list" -> listWorlds(sender);
|
||||||
case "reload" -> reload(sender);
|
case "reload" -> reload(sender);
|
||||||
default -> {
|
default -> {
|
||||||
@@ -84,6 +94,59 @@ final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean forceRemoveWorld(CommandSender sender, String label, String[] args) {
|
||||||
|
if (args.length != 2) {
|
||||||
|
sender.sendMessage(Component.text("Usage: /" + label + " forceremove <world>", NamedTextColor.RED));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String worldName = canonicalWorldName(args[1]);
|
||||||
|
MultiverseInventoriesHook.ForceRemovalResult result;
|
||||||
|
try {
|
||||||
|
result = multiverseHook.forceRemoveWorld(worldName);
|
||||||
|
} catch (ReflectiveOperationException | LinkageError exception) {
|
||||||
|
plugin.getLogger().log(
|
||||||
|
java.util.logging.Level.SEVERE,
|
||||||
|
"Could not force-remove " + worldName + " from Multiverse-Inventories.",
|
||||||
|
exception);
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
"Force removal failed. Check the server console; no AntiInventories setting was changed.",
|
||||||
|
NamedTextColor.RED));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.patternGroups().isEmpty()) {
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
"Force removal stopped: " + worldName + " is matched by a wildcard or regex in group(s): "
|
||||||
|
+ String.join(", ", result.patternGroups()) + ".",
|
||||||
|
NamedTextColor.RED));
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
"No AntiInventories or explicit Multiverse-Inventories setting was changed.",
|
||||||
|
NamedTextColor.YELLOW));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean removedFromAntiInventories = settings.remove(worldName);
|
||||||
|
plugin.saveBypassWorlds();
|
||||||
|
boolean multiverseReloaded = multiverseHook.reloadMultiverseInventories();
|
||||||
|
plugin.reloadPluginConfig();
|
||||||
|
|
||||||
|
sender.sendMessage(Component.text("Force removal completed for " + worldName + ".", NamedTextColor.GREEN));
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
"Multiverse groups removed: "
|
||||||
|
+ (result.removedGroups().isEmpty() ? "(none)" : String.join(", ", result.removedGroups())),
|
||||||
|
NamedTextColor.YELLOW));
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
"Removed from AntiInventories: " + (removedFromAntiInventories ? "yes" : "not previously listed"),
|
||||||
|
NamedTextColor.YELLOW));
|
||||||
|
sender.sendMessage(Component.text(
|
||||||
|
"Reloaded Multiverse-Inventories: " + (multiverseReloaded ? "yes" : "command was not available"),
|
||||||
|
multiverseReloaded ? NamedTextColor.GREEN : NamedTextColor.RED));
|
||||||
|
sender.sendMessage(Component.text("Reloaded AntiInventories: yes", NamedTextColor.GREEN));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean listWorlds(CommandSender sender) {
|
private boolean listWorlds(CommandSender sender) {
|
||||||
List<String> worlds = settings.worlds();
|
List<String> worlds = settings.worlds();
|
||||||
String formattedWorlds = worlds.isEmpty() ? "(none)" : String.join(", ", worlds);
|
String formattedWorlds = worlds.isEmpty() ? "(none)" : String.join(", ", worlds);
|
||||||
@@ -93,6 +156,15 @@ final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
|||||||
sender.sendMessage(
|
sender.sendMessage(
|
||||||
Component.text("Direction: ", NamedTextColor.GOLD)
|
Component.text("Direction: ", NamedTextColor.GOLD)
|
||||||
.append(Component.text(settings.direction().name(), NamedTextColor.WHITE)));
|
.append(Component.text(settings.direction().name(), NamedTextColor.WHITE)));
|
||||||
|
for (String world : worlds) {
|
||||||
|
sender.sendMessage(
|
||||||
|
Component.text("• " + world + " ", NamedTextColor.WHITE)
|
||||||
|
.append(Component.text("[Force remove]", NamedTextColor.RED)
|
||||||
|
.clickEvent(ClickEvent.suggestCommand("/antiinv forceremove " + world))
|
||||||
|
.hoverEvent(HoverEvent.showText(Component.text(
|
||||||
|
"Remove from Multiverse-Inventories and AntiInventories, then reload both.",
|
||||||
|
NamedTextColor.YELLOW)))));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +178,7 @@ final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
|||||||
sender.sendMessage(Component.text("AntiInventories commands:", NamedTextColor.GOLD));
|
sender.sendMessage(Component.text("AntiInventories commands:", NamedTextColor.GOLD));
|
||||||
sender.sendMessage(Component.text("/" + label + " add <world>", NamedTextColor.YELLOW));
|
sender.sendMessage(Component.text("/" + label + " add <world>", NamedTextColor.YELLOW));
|
||||||
sender.sendMessage(Component.text("/" + label + " remove <world>", NamedTextColor.YELLOW));
|
sender.sendMessage(Component.text("/" + label + " remove <world>", NamedTextColor.YELLOW));
|
||||||
|
sender.sendMessage(Component.text("/" + label + " forceremove <world>", NamedTextColor.RED));
|
||||||
sender.sendMessage(Component.text("/" + label + " list", NamedTextColor.YELLOW));
|
sender.sendMessage(Component.text("/" + label + " list", NamedTextColor.YELLOW));
|
||||||
sender.sendMessage(Component.text("/" + label + " reload", NamedTextColor.YELLOW));
|
sender.sendMessage(Component.text("/" + label + " reload", NamedTextColor.YELLOW));
|
||||||
}
|
}
|
||||||
@@ -130,6 +203,11 @@ final class AntiInventoriesCommand implements CommandExecutor, TabCompleter {
|
|||||||
if (args.length == 2 && args[0].equalsIgnoreCase("remove")) {
|
if (args.length == 2 && args[0].equalsIgnoreCase("remove")) {
|
||||||
return filter(settings.worlds(), args[1]);
|
return filter(settings.worlds(), args[1]);
|
||||||
}
|
}
|
||||||
|
if (args.length == 2 && args[0].equalsIgnoreCase("forceremove")) {
|
||||||
|
Set<String> candidates = new LinkedHashSet<>(settings.worlds());
|
||||||
|
Bukkit.getWorlds().stream().map(World::getName).forEach(candidates::add);
|
||||||
|
return filter(List.copyOf(candidates), args[1]);
|
||||||
|
}
|
||||||
return List.of();
|
return List.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public final class AntiInventoriesPlugin extends JavaPlugin {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AntiInventoriesCommand commandHandler = new AntiInventoriesCommand(this, settings);
|
AntiInventoriesCommand commandHandler = new AntiInventoriesCommand(this, settings, hook);
|
||||||
PluginCommand command = Objects.requireNonNull(
|
PluginCommand command = Objects.requireNonNull(
|
||||||
getCommand("antiinventories"),
|
getCommand("antiinventories"),
|
||||||
"antiinventories command is missing from plugin.yml");
|
"antiinventories command is missing from plugin.yml");
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ package dev.antiinventories;
|
|||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.Cancellable;
|
import org.bukkit.event.Cancellable;
|
||||||
@@ -20,11 +25,16 @@ final class MultiverseInventoriesHook {
|
|||||||
"org.mvplugins.multiverse.inventories.event.ReadOnlyShareHandlingEvent";
|
"org.mvplugins.multiverse.inventories.event.ReadOnlyShareHandlingEvent";
|
||||||
private static final String WRITE_ONLY_EVENT =
|
private static final String WRITE_ONLY_EVENT =
|
||||||
"org.mvplugins.multiverse.inventories.event.WriteOnlyShareHandlingEvent";
|
"org.mvplugins.multiverse.inventories.event.WriteOnlyShareHandlingEvent";
|
||||||
|
private static final String INVENTORIES_API =
|
||||||
|
"org.mvplugins.multiverse.inventories.MultiverseInventoriesApi";
|
||||||
|
private static final String WORLD_GROUP =
|
||||||
|
"org.mvplugins.multiverse.inventories.profile.group.WorldGroup";
|
||||||
|
|
||||||
private final AntiInventoriesPlugin plugin;
|
private final AntiInventoriesPlugin plugin;
|
||||||
private final BypassSettings settings;
|
private final BypassSettings settings;
|
||||||
private final Listener listener = new Listener() {
|
private final Listener listener = new Listener() {
|
||||||
};
|
};
|
||||||
|
private Plugin inventories;
|
||||||
|
|
||||||
MultiverseInventoriesHook(AntiInventoriesPlugin plugin, BypassSettings settings) {
|
MultiverseInventoriesHook(AntiInventoriesPlugin plugin, BypassSettings settings) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
@@ -33,7 +43,7 @@ final class MultiverseInventoriesHook {
|
|||||||
|
|
||||||
boolean register() {
|
boolean register() {
|
||||||
PluginManager pluginManager = plugin.getServer().getPluginManager();
|
PluginManager pluginManager = plugin.getServer().getPluginManager();
|
||||||
Plugin inventories = pluginManager.getPlugin("Multiverse-Inventories");
|
inventories = pluginManager.getPlugin("Multiverse-Inventories");
|
||||||
if (inventories == null || !inventories.isEnabled()) {
|
if (inventories == null || !inventories.isEnabled()) {
|
||||||
plugin.getLogger().severe("Multiverse-Inventories is not installed or enabled.");
|
plugin.getLogger().severe("Multiverse-Inventories is not installed or enabled.");
|
||||||
return false;
|
return false;
|
||||||
@@ -54,6 +64,58 @@ final class MultiverseInventoriesHook {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForceRemovalResult forceRemoveWorld(String worldName) throws ReflectiveOperationException {
|
||||||
|
ClassLoader classLoader = inventories.getClass().getClassLoader();
|
||||||
|
Class<?> apiClass = Class.forName(INVENTORIES_API, true, classLoader);
|
||||||
|
Object api = apiClass.getMethod("get").invoke(null);
|
||||||
|
Method getWorldGroupManager = apiClass.getMethod("getWorldGroupManager");
|
||||||
|
Object groupManager = getWorldGroupManager.invoke(api);
|
||||||
|
Class<?> groupManagerType = getWorldGroupManager.getReturnType();
|
||||||
|
Method getGroupsForWorld = groupManagerType.getMethod("getGroupsForWorld", String.class);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Object> groups = new ArrayList<>((List<Object>) getGroupsForWorld.invoke(groupManager, worldName));
|
||||||
|
Class<?> worldGroupType = Class.forName(WORLD_GROUP, true, classLoader);
|
||||||
|
Method getName = worldGroupType.getMethod("getName");
|
||||||
|
Method getConfigWorlds = worldGroupType.getMethod("getConfigWorlds");
|
||||||
|
Method removeWorlds = worldGroupType.getMethod("removeWorlds", Collection.class);
|
||||||
|
|
||||||
|
List<String> removedGroups = new ArrayList<>();
|
||||||
|
List<String> patternGroups = new ArrayList<>();
|
||||||
|
List<Object> explicitGroups = new ArrayList<>();
|
||||||
|
String normalizedWorld = worldName.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
for (Object group : groups) {
|
||||||
|
String groupName = (String) getName.invoke(group);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Set<String> configuredWorlds = (Set<String>) getConfigWorlds.invoke(group);
|
||||||
|
if (!configuredWorlds.contains(normalizedWorld)) {
|
||||||
|
patternGroups.add(groupName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
explicitGroups.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!patternGroups.isEmpty()) {
|
||||||
|
return new ForceRemovalResult(List.of(), List.copyOf(patternGroups));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Object group : explicitGroups) {
|
||||||
|
boolean removed = (boolean) removeWorlds.invoke(group, Set.of(worldName));
|
||||||
|
if (removed) {
|
||||||
|
removedGroups.add((String) getName.invoke(group));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ForceRemovalResult(List.copyOf(removedGroups), List.copyOf(patternGroups));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean reloadMultiverseInventories() {
|
||||||
|
return plugin.getServer().dispatchCommand(
|
||||||
|
plugin.getServer().getConsoleSender(),
|
||||||
|
"mvinv reload");
|
||||||
|
}
|
||||||
|
|
||||||
private void registerWorldChangeHook(PluginManager manager, ClassLoader classLoader)
|
private void registerWorldChangeHook(PluginManager manager, ClassLoader classLoader)
|
||||||
throws ReflectiveOperationException {
|
throws ReflectiveOperationException {
|
||||||
Class<? extends Event> eventClass = loadEventClass(WORLD_CHANGE_EVENT, classLoader);
|
Class<? extends Event> eventClass = loadEventClass(WORLD_CHANGE_EVENT, classLoader);
|
||||||
@@ -136,4 +198,7 @@ final class MultiverseInventoriesHook {
|
|||||||
private interface PlayerRule {
|
private interface PlayerRule {
|
||||||
boolean shouldBypass(Player player);
|
boolean shouldBypass(Player player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record ForceRemovalResult(List<String> removedGroups, List<String> patternGroups) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: AntiInventories
|
name: AntiInventories
|
||||||
version: '1.0.0'
|
version: '1.1.0'
|
||||||
main: dev.antiinventories.AntiInventoriesPlugin
|
main: dev.antiinventories.AntiInventoriesPlugin
|
||||||
description: Keeps the current inventory when players cross selected world borders.
|
description: Keeps the current inventory when players cross selected world borders.
|
||||||
api-version: '1.21.11'
|
api-version: '1.21.11'
|
||||||
@@ -10,7 +10,7 @@ depend:
|
|||||||
commands:
|
commands:
|
||||||
antiinventories:
|
antiinventories:
|
||||||
description: Manage worlds that bypass Multiverse-Inventories.
|
description: Manage worlds that bypass Multiverse-Inventories.
|
||||||
usage: /antiinventories <add|remove|list|reload> [world]
|
usage: /antiinventories <add|remove|forceremove|list|reload> [world]
|
||||||
aliases:
|
aliases:
|
||||||
- antiinv
|
- antiinv
|
||||||
- ai
|
- ai
|
||||||
@@ -20,4 +20,3 @@ permissions:
|
|||||||
antiinventories.admin:
|
antiinventories.admin:
|
||||||
description: Allows management of AntiInventories.
|
description: Allows management of AntiInventories.
|
||||||
default: op
|
default: op
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,15 @@ class BypassSettingsTest {
|
|||||||
assertTrue(settings.shouldBypass("AutoMine", "Wild_Forest1"));
|
assertTrue(settings.shouldBypass("AutoMine", "Wild_Forest1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void removesConfiguredWorldCaseInsensitively() {
|
||||||
|
BypassSettings settings = settings(BypassDirection.EITHER, "AutoMine", "Wild_Forest1");
|
||||||
|
|
||||||
|
assertTrue(settings.remove("automine"));
|
||||||
|
assertFalse(settings.contains("AutoMine"));
|
||||||
|
assertTrue(settings.contains("Wild_Forest1"));
|
||||||
|
}
|
||||||
|
|
||||||
private static BypassSettings settings(BypassDirection direction, String... worlds) {
|
private static BypassSettings settings(BypassDirection direction, String... worlds) {
|
||||||
BypassSettings settings = new BypassSettings();
|
BypassSettings settings = new BypassSettings();
|
||||||
settings.load(List.of(worlds), direction.name(), true, true);
|
settings.load(List.of(worlds), direction.name(), true, true);
|
||||||
|
|||||||
Reference in New Issue
Block a user