Merge pull request #3013 from Multiverse/world-revamp-continue

Continue revamping World/WorldManager.
This commit is contained in:
Jeremy Wood 2023-09-11 10:42:47 -04:00 committed by GitHub
commit d3fa42a035
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
114 changed files with 6571 additions and 1123 deletions

View File

@ -8,11 +8,15 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
continue-on-error: true
steps:
- uses: actions/checkout@v3
- uses: dbelyaev/action-checkstyle@v0.8.4
continue-on-error: true
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
checkstyle_version: 10.12.2
checkstyle_config: ./config/mv_checks.xml
level: warning

View File

@ -442,7 +442,7 @@
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$|^[A-Z][A-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>

View File

@ -11,8 +11,6 @@ import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.anchor.AnchorManager;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.MVCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.PluginLocales;
@ -26,10 +24,14 @@ import com.onarandombox.MultiverseCore.utils.TestingMode;
import com.onarandombox.MultiverseCore.utils.metrics.MetricsConfigurator;
import com.onarandombox.MultiverseCore.world.WorldProperties;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.config.NullLocation;
import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import me.main__.util.SerializationConfig.SerializationConfig;
import org.bukkit.Bukkit;
import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.java.JavaPluginLoader;
@ -57,9 +59,7 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
@Inject
private Provider<MVCoreConfig> configProvider;
@Inject
private Provider<MVWorldManager> worldManagerProvider;
@Inject
private Provider<WorldManager> newWorldManagerProvider;
private Provider<WorldManager> worldManagerProvider;
@Inject
private Provider<AnchorManager> anchorManagerProvider;
@Inject
@ -102,6 +102,8 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
@Override
public void onEnable() {
initializeDependencyInjection();
ConfigurationSerialization.registerClass(NullLocation.class);
ConfigurationSerialization.registerClass(SpawnLocation.class);
// Load our configs first as we need them for everything else.
var config = configProvider.get();
@ -111,39 +113,28 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
this.getServer().getPluginManager().disablePlugin(this);
return;
}
Logging.setShowingConfig(shouldShowConfig());
var worldManager = worldManagerProvider.get();
// Initialize the worlds
worldManagerProvider.get().initAllWorlds().andThenTry(() -> {
// Setup economy here so vault is loaded
loadEconomist();
worldManager.loadWorldsConfig();
worldManager.getDefaultWorldGenerators();
worldManager.loadDefaultWorlds();
worldManager.loadWorlds(true);
// Now set the firstspawnworld (after the worlds are loaded):
worldManager.setFirstSpawnWorld(config.getFirstSpawnLocation());
MVWorld firstSpawnWorld = worldManager.getFirstSpawnWorld();
if (firstSpawnWorld != null) {
config.setFirstSpawnLocation(firstSpawnWorld.getName());
}
var newWorldManager = newWorldManagerProvider.get();
newWorldManager.loadAllWorlds(); // TODO: Possibly move this to constructor of WorldManager
//Setup economy here so vault is loaded
this.loadEconomist();
// Init all the other stuff
this.loadAnchors();
this.registerEvents();
this.setUpLocales();
this.registerCommands();
this.registerDestinations();
this.setupMetrics();
this.loadPlaceholderAPIIntegration();
this.saveAllConfigs();
this.logEnableMessage();
// Init all the other stuff
loadAnchors();
registerEvents();
setUpLocales();
registerCommands();
registerDestinations();
setupMetrics();
loadPlaceholderAPIIntegration();
saveAllConfigs();
logEnableMessage();
}).onFailure(e -> {
Logging.severe("Failed to multiverse core! Disabling...");
e.printStackTrace();
getServer().getPluginManager().disablePlugin(this);
});
}
/**
@ -151,7 +142,7 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
*/
@Override
public void onDisable() {
this.saveAllConfigs();
saveAllConfigs();
shutdownDependencyInjection();
Logging.shutdown();
}
@ -372,12 +363,12 @@ public class MultiverseCore extends JavaPlugin implements MVCore {
*/
@Override
public boolean saveAllConfigs() {
return configProvider.get().save()
// TODO: Make this all Try<Void>
return configProvider.get().save().isSuccess()
&& worldManagerProvider.get().saveWorldsConfig()
&& anchorManagerProvider.get().saveAnchors();
}
/**
* Gets the best service from this plugin that implements the given contract or has the given implementation.
*

View File

@ -12,7 +12,7 @@ public interface MVConfig {
* Loads the config from disk.
* @return True if the config was loaded successfully.
*/
boolean load();
Try<Void> load();
/**
* Whether the config has been loaded.
@ -23,7 +23,7 @@ public interface MVConfig {
/**
* Saves the config to disk.
*/
boolean save();
Try<Void> save();
/**
* Gets the nodes for the config.

View File

@ -1,18 +1,23 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.options.CloneWorldOptions;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -21,12 +26,24 @@ import org.jvnet.hk2.annotations.Service;
@CommandAlias("mv")
public class CloneCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public CloneCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
public CloneCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvclone")
.add(CommandFlag.builder("--reset-world-config")
.addAlias("-wc")
.build())
.add(CommandFlag.builder("--reset-gamerules")
.addAlias("-gm")
.build())
.add(CommandFlag.builder("--reset-world-border")
.addAlias("-wb")
.build())
.build());
}
@Subcommand("clone")
@ -34,28 +51,35 @@ public class CloneCommand extends MultiverseCommand {
@CommandCompletion("@mvworlds:scope=both @empty")
@Syntax("<world> <new world name>")
@Description("{@@mv-core.clone.description}")
public void onCloneCommand(CommandIssuer issuer,
void onCloneCommand(
MVCommandIssuer issuer,
@Conditions("worldname:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.clone.world.description}")
String worldName,
@Syntax("<world>")
@Description("{@@mv-core.clone.world.description}")
LoadedMultiverseWorld world,
@Single
@Conditions("worldname:scope=new")
@Syntax("<new world name>")
@Description("{@@mv-core.clone.newWorld.description}")
String newWorldName
) {
issuer.sendInfo(MVCorei18n.CLONE_CLONING,
"{world}", worldName,
"{newWorld}", newWorldName);
@Syntax("<new world name>")
@Description("{@@mv-core.clone.newWorld.description}")
String newWorldName,
if (!this.worldManager.cloneWorld(worldName, newWorldName)) {
issuer.sendError(MVCorei18n.CLONE_FAILED);
return;
}
issuer.sendInfo(MVCorei18n.CLONE_SUCCESS,
"{world}", newWorldName);
@Optional
@Syntax(/* TODO */ "")
@Description("{@@mv-core.regen.other.description}")
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.CLONE_CLONING, "{world}", world.getName(), "{newworld}", newWorldName);
CloneWorldOptions cloneWorldOptions = CloneWorldOptions.fromTo(world, newWorldName)
.keepWorldConfig(!parsedFlags.hasFlag("--reset-world-config"))
.keepGameRule(!parsedFlags.hasFlag("--reset-gamerules"))
.keepWorldBorder(!parsedFlags.hasFlag("--reset-world-border"));
worldManager.cloneWorld(cloneWorldOptions)
.onSuccess(newWorld -> {
Logging.fine("World clone success: " + newWorld);
issuer.sendInfo(MVCorei18n.CLONE_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World clone failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}

View File

@ -7,10 +7,10 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Subcommand;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import jakarta.inject.Inject;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@ -40,12 +40,12 @@ public class CoordinatesCommand extends MultiverseCommand {
Player player,
@Flags("resolve=issuerOnly")
MVWorld world
LoadedMultiverseWorld world
) {
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_TITLE);
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLD, "{world}", world.getName());
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_ALIAS, "{alias}", world.getColoredWorldString());
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLDSCALE, "{scale}", String.valueOf(world.getScaling()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_ALIAS, "{alias}", world.getAlias());
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_WORLDSCALE, "{scale}", String.valueOf(world.getScale()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_COORDINATES, "{coordinates}", locationManipulation.strCoords(player.getLocation()));
issuer.sendInfo(MVCorei18n.COORDINATES_INFO_DIRECTION, "{direction}", locationManipulation.getDirection(player.getLocation()));
}

View File

@ -1,23 +1,14 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
@ -25,52 +16,46 @@ import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.generators.GeneratorProvider;
import com.onarandombox.MultiverseCore.worldnew.options.CreateWorldOptions;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.util.Collections;
import java.util.Random;
@Service
@CommandAlias("mv")
public class CreateCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public CreateCommand(
@NotNull MVCommandManager commandManager,
@NotNull MVWorldManager worldManager,
@NotNull UnsafeCallWrapper unsafeCallWrapper
@NotNull WorldManager worldManager,
@NotNull GeneratorProvider generatorProvider
) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvcreate")
.add(CommandValueFlag.builder("--seed", String.class)
.addAlias("-s")
.completion(() -> Collections.singleton(String.valueOf(new Random().nextLong())))
.completion((input) -> Collections.singleton(String.valueOf(new Random().nextLong())))
.build())
.add(CommandValueFlag.builder("--generator", String.class)
.addAlias("-g")
.completion(() -> Arrays.stream(Bukkit.getServer().getPluginManager().getPlugins())
.filter(Plugin::isEnabled)
.filter(genplugin -> unsafeCallWrapper.wrap(
() -> genplugin.getDefaultWorldGenerator("world", ""),
genplugin.getName(),
"Get generator"
) != null)
.map(genplugin -> genplugin.getDescription().getName())
.collect(Collectors.toList()))
.completion(generatorProvider::suggestGeneratorString)
.build())
.add(CommandValueFlag.enumBuilder("--world-type", WorldType.class)
.addAlias("-t")
.build())
.add(CommandFlag.builder("--adjust-spawn")
.add(CommandFlag.builder("--no-adjust-spawn")
.addAlias("-n")
.build())
.add(CommandFlag.builder("--no-structures")
@ -84,9 +69,8 @@ public class CreateCommand extends MultiverseCommand {
@CommandCompletion("@empty @flags:groupName=mvcreate")
@Syntax("<name> <environment> --seed [seed] --generator [generator[:id]] --world-type [worldtype] --adjust-spawn --no-structures")
@Description("{@@mv-core.create.description}")
public void onCreateCommand(BukkitCommandIssuer issuer,
public void onCreateCommand(MVCommandIssuer issuer,
@Conditions("worldname:scope=new")
@Syntax("<name>")
@Description("{@@mv-core.create.name.description}")
String worldName,
@ -98,32 +82,32 @@ public class CreateCommand extends MultiverseCommand {
@Optional
@Syntax("--seed [seed] --generator [generator[:id]] --world-type [worldtype] --adjust-spawn --no-structures")
@Description("{@@mv-core.create.flags.description}")
String[] flags
) {
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES, "{worldName}", worldName);
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ENVIRONMENT, "{environment}", environment.name());
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_SEED, "{seed}", parsedFlags.flagValue("--seed", "RANDOM", String.class));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_WORLDTYPE, "{worldType}", parsedFlags.flagValue("--world-type", WorldType.NORMAL, WorldType.class).name());
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN, "{adjustSpawn}", String.valueOf(parsedFlags.hasFlag("--adjust-spawn")));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR, "{generator}", parsedFlags.flagValue("--generator", "null", String.class));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_ADJUSTSPAWN, "{adjustSpawn}", String.valueOf(!parsedFlags.hasFlag("--no-adjust-spawn")));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_GENERATOR, "{generator}", parsedFlags.flagValue("--generator", "", String.class));
issuer.sendInfo(MVCorei18n.CREATE_PROPERTIES_STRUCTURES, "{structures}", String.valueOf(!parsedFlags.hasFlag("--no-structures")));
issuer.sendInfo(MVCorei18n.CREATE_LOADING);
if (!worldManager.addWorld(
worldName,
environment,
parsedFlags.flagValue("--seed", String.class),
parsedFlags.flagValue("--world-type", WorldType.NORMAL, WorldType.class),
parsedFlags.hasFlag("--adjust-spawn"),
parsedFlags.flagValue("--generator", String.class),
parsedFlags.hasFlag("--no-structures")
)) {
issuer.sendError(MVCorei18n.CREATE_FAILED, "{worldName}", worldName);
return;
}
issuer.sendInfo(MVCorei18n.CREATE_SUCCESS, "{worldName}", worldName);
worldManager.createWorld(CreateWorldOptions.worldName(worldName)
.environment(environment)
.seed(parsedFlags.flagValue("--seed", String.class))
.worldType(parsedFlags.flagValue("--world-type", WorldType.NORMAL, WorldType.class))
.useSpawnAdjust(!parsedFlags.hasFlag("--no-adjust-spawn"))
.generator(parsedFlags.flagValue("--generator", "", String.class))
.generateStructures(!parsedFlags.hasFlag("--no-structures")))
.onSuccess(newWorld -> {
Logging.fine("World create success: " + newWorld);
issuer.sendInfo(MVCorei18n.CREATE_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World create failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}

View File

@ -1,6 +1,5 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.MessageType;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
@ -10,11 +9,13 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -23,10 +24,10 @@ import org.jvnet.hk2.annotations.Service;
@CommandAlias("mv")
public class DeleteCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public DeleteCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
public DeleteCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@ -36,7 +37,7 @@ public class DeleteCommand extends MultiverseCommand {
@CommandCompletion("@mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.delete.description}")
public void onDeleteCommand(BukkitCommandIssuer issuer,
public void onDeleteCommand(MVCommandIssuer issuer,
@Single
@Conditions("worldname:scope=both")
@ -47,15 +48,15 @@ public class DeleteCommand extends MultiverseCommand {
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
issuer.getIssuer(),
() -> {
issuer.sendInfo(MVCorei18n.DELETE_DELETING,
"{world}", worldName);
if (!this.worldManager.deleteWorld(worldName)) {
issuer.sendError(MVCorei18n.DELETE_FAILED,
"{world}", worldName);
return;
}
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS,
"{world}", worldName);
issuer.sendInfo(MVCorei18n.DELETE_DELETING, "{world}", worldName);
worldManager.deleteWorld(worldName)
.onSuccess(deletedWorldName -> {
Logging.fine("World delete success: " + deletedWorldName);
issuer.sendInfo(MVCorei18n.DELETE_SUCCESS, "{world}", deletedWorldName);
}).onFailure(failure -> {
Logging.fine("World delete failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
},
this.commandManager.formatMessage(
issuer,

View File

@ -10,7 +10,6 @@ import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
@ -23,6 +22,7 @@ import com.onarandombox.MultiverseCore.utils.webpaste.PasteFailedException;
import com.onarandombox.MultiverseCore.utils.webpaste.PasteService;
import com.onarandombox.MultiverseCore.utils.webpaste.PasteServiceFactory;
import com.onarandombox.MultiverseCore.utils.webpaste.PasteServiceType;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.bukkit.scheduler.BukkitRunnable;
@ -45,12 +45,12 @@ import static com.onarandombox.MultiverseCore.utils.file.FileUtils.getServerProp
public class DumpsCommand extends MultiverseCommand {
private final MultiverseCore plugin;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public DumpsCommand(@NotNull MVCommandManager commandManager,
@NotNull MultiverseCore plugin,
@NotNull MVWorldManager worldManager) {
@NotNull WorldManager worldManager) {
super(commandManager);
this.plugin = plugin;
this.worldManager = worldManager;
@ -184,7 +184,7 @@ public class DumpsCommand extends MultiverseCommand {
return "# Multiverse-Core Version info" + "\n\n"
+ " - Multiverse-Core Version: " + this.plugin.getDescription().getVersion() + '\n'
+ " - Bukkit Version: " + this.plugin.getServer().getVersion() + '\n'
+ " - Loaded Worlds: " + worldManager.getMVWorlds() + '\n'
+ " - Loaded Worlds: " + worldManager.getLoadedWorlds() + '\n'
+ " - Multiverse Plugins Loaded: " + this.plugin.getPluginCount() + '\n';
}

View File

@ -8,13 +8,14 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.context.GameRuleValue;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -45,13 +46,14 @@ public class GameruleCommand extends MultiverseCommand {
@Flags("resolve=issuerAware")
@Syntax("[World or *]")
@Description("{@@mv-core.gamerule.world.description}")
MVWorld[] worlds
LoadedMultiverseWorld[] worlds
) {
Object value = gameRuleValue.getValue();
boolean success = true;
for(MVWorld world : worlds) {
for(LoadedMultiverseWorld world : worlds) {
// Set gamerules and add false to list if it fails
if (!world.getCBWorld().setGameRule(gamerule, value)) {
World bukkitWorld = world.getBukkitWorld().getOrNull();
if (bukkitWorld == null || !bukkitWorld.setGameRule(gamerule, value)) {
issuer.sendError(MVCorei18n.GAMERULE_FAILED,
"{gamerule}", gamerule.getName(),
"{value}", value.toString(),

View File

@ -11,7 +11,6 @@ import co.aikar.commands.annotation.Flags;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
@ -24,6 +23,7 @@ import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.display.handlers.PagedSendHandler;
import com.onarandombox.MultiverseCore.display.parsers.MapContentProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
@ -81,7 +81,7 @@ public class GamerulesCommand extends MultiverseCommand {
@Flags("resolve=issuerAware")
@Syntax("<world>")
@Description("{@@mv-core.gamerules.description.world}")
MVWorld world,
LoadedMultiverseWorld world,
@Optional
@Syntax("[--page <page>] [--filter <filter>]")
@ -91,11 +91,11 @@ public class GamerulesCommand extends MultiverseCommand {
ParsedCommandFlags parsedFlags = parseFlags(flags);
ContentDisplay.create()
.addContent(new MapContentProvider<>(getGameRuleMap(world.getCBWorld()))
.addContent(new MapContentProvider<>(getGameRuleMap(world.getBukkitWorld().getOrNull())) // TODO: Handle null
.withKeyColor(ChatColor.AQUA)
.withValueColor(ChatColor.WHITE))
.withSendHandler(new PagedSendHandler()
.withHeader(this.getTitle(issuer, world.getCBWorld()))
.withHeader(this.getTitle(issuer, world.getBukkitWorld().getOrNull()))
.doPagination(true)
.withTargetPage(parsedFlags.flagValue(PAGE_FLAG, 1))
.withFilter(parsedFlags.flagValue(FILTER_FLAG, DefaultContentFilter.get())))

View File

@ -1,9 +1,5 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.Arrays;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@ -12,7 +8,8 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
@ -20,11 +17,11 @@ import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.generators.GeneratorProvider;
import com.onarandombox.MultiverseCore.worldnew.options.ImportWorldOptions;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -32,29 +29,21 @@ import org.jvnet.hk2.annotations.Service;
@CommandAlias("mv")
public class ImportCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public ImportCommand(
@NotNull MVCommandManager commandManager,
@NotNull MVWorldManager worldManager,
@NotNull UnsafeCallWrapper unsafeCallWrapper
) {
@NotNull WorldManager worldManager,
@NotNull GeneratorProvider generatorProvider
) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvimport")
.add(CommandValueFlag.builder("--generator", String.class)
.addAlias("-g")
.completion(() -> Arrays.stream(Bukkit.getServer().getPluginManager().getPlugins())
.filter(Plugin::isEnabled)
.filter(genplugin -> unsafeCallWrapper.wrap(
() -> genplugin.getDefaultWorldGenerator("world", ""),
genplugin.getName(),
"Get generator"
) != null)
.map(genplugin -> genplugin.getDescription().getName())
.collect(Collectors.toList()))
.completion(generatorProvider::suggestGeneratorString)
.build())
.add(CommandFlag.builder("--adjust-spawn")
.addAlias("-a")
@ -67,7 +56,7 @@ public class ImportCommand extends MultiverseCommand {
@CommandCompletion("@mvworlds:scope=potential @flags:groupName=mvimport")
@Syntax("<name> <env> --generator [generator[:id]] --adjust-spawn")
@Description("{@@mv-core.import.description")
public void onImportCommand(BukkitCommandIssuer issuer,
public void onImportCommand(MVCommandIssuer issuer,
@Conditions("worldname:scope=new")
@Syntax("<name>")
@ -85,20 +74,18 @@ public class ImportCommand extends MultiverseCommand {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING,
"{world}", worldName);
if (!this.worldManager.addWorld(
worldName, environment,
null,
null,
null,
parsedFlags.flagValue("--generator", String.class),
parsedFlags.hasFlag("--adjust-spawn"))
) {
issuer.sendError(MVCorei18n.IMPORT_FAILED);
return;
}
issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS);
issuer.sendInfo(MVCorei18n.IMPORT_IMPORTING, "{world}", worldName);
worldManager.importWorld(ImportWorldOptions.worldName(worldName)
.environment(environment)
.generator(parsedFlags.flagValue("--generator", String.class))
.useSpawnAdjust(parsedFlags.hasFlag("--adjust-spawn")))
.onSuccess(newWorld -> {
Logging.fine("World import success: " + newWorld);
issuer.sendInfo(MVCorei18n.IMPORT_SUCCESS, "{world}", newWorld.getName());
})
.onFailure(failure -> {
Logging.fine("World import failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}

View File

@ -1,9 +1,5 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.ArrayList;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
@ -11,9 +7,7 @@ import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagGroup;
@ -25,26 +19,30 @@ import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.display.handlers.PagedSendHandler;
import com.onarandombox.MultiverseCore.display.parsers.ListContentProvider;
import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper;
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
import com.onarandombox.MultiverseCore.worldnew.entrycheck.WorldEntryChecker;
import com.onarandombox.MultiverseCore.worldnew.entrycheck.WorldEntryCheckerProvider;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.util.ArrayList;
import java.util.List;
@Service
@CommandAlias("mv")
public class ListCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
@Inject
public ListCommand(
@NotNull MVCommandManager commandManager,
@NotNull MVWorldManager worldManager,
@NotNull WorldManager worldManager,
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider
) {
super(commandManager);
@ -80,7 +78,7 @@ public class ListCommand extends MultiverseCommand {
@CommandCompletion("@flags:groupName=mvlist")
@Syntax("--filter [filter] --page [page]")
@Description("Displays a listing of all worlds that you can enter.")
public void onListCommand(BukkitCommandIssuer issuer,
public void onListCommand(MVCommandIssuer issuer,
@Syntax("--filter [filter] --page [page]")
@Description("Filters the list of worlds by the given regex and displays the given page.")
@ -96,49 +94,42 @@ public class ListCommand extends MultiverseCommand {
.send(issuer);
}
private List<String> getListContents(BukkitCommandIssuer issuer) {
Player player = issuer.isPlayer() ? issuer.getPlayer() : null;
private List<String> getListContents(MVCommandIssuer issuer) {
List<String> worldList = new ArrayList<>();
WorldEntryChecker worldEntryChecker = worldEntryCheckerProvider.forSender(issuer.getIssuer());
worldManager.getMVWorlds().stream()
.filter(world -> player == null || worldEntryCheckerProvider.forSender(player).canAccessWorld(world).isSuccess())
.filter(world -> canSeeWorld(player, world))
.map(world -> hiddenText(world) + world.getColoredWorldString() + " - " + parseColouredEnvironment(world.getEnvironment()))
worldManager.getLoadedWorlds().stream()
.filter(world -> worldEntryChecker.canAccessWorld(world).isSuccess())
.filter(world -> canSeeWorld(issuer, world))
.map(world -> hiddenText(world) + world.getAlias() + " - " + parseColouredEnvironment(world.getEnvironment()))
.sorted()
.forEach(worldList::add);
worldManager.getUnloadedWorlds().stream()
.filter(world -> issuer.hasPermission("multiverse.access." + world)) // TODO: Refactor stray permission check
.map(world -> ChatColor.GRAY + world + " - UNLOADED")
.filter(world -> worldEntryChecker.canAccessWorld(world).isSuccess())
.map(world -> ChatColor.GRAY + world.getAlias() + " - UNLOADED")
.sorted()
.forEach(worldList::add);
return worldList;
}
private boolean canSeeWorld(Player player, MVWorld world) {
private boolean canSeeWorld(MVCommandIssuer issuer, LoadedMultiverseWorld world) {
return !world.isHidden()
|| player == null
|| player.hasPermission("multiverse.core.modify"); // TODO: Refactor stray permission check
|| issuer.hasPermission("multiverse.core.modify"); // TODO: Refactor stray permission check
}
private String hiddenText(MVWorld world) {
private String hiddenText(LoadedMultiverseWorld world) {
return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : "";
}
private String parseColouredEnvironment(World.Environment env) {
ChatColor color = ChatColor.GOLD;
switch (env) {
case NETHER:
color = ChatColor.RED;
break;
case NORMAL:
color = ChatColor.GREEN;
break;
case THE_END:
color = ChatColor.AQUA;
break;
}
ChatColor color = switch (env) {
case NETHER -> ChatColor.RED;
case NORMAL -> ChatColor.GREEN;
case THE_END -> ChatColor.AQUA;
default -> ChatColor.GOLD;
};
return color + env.toString();
}
}

View File

@ -1,6 +1,5 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@ -9,10 +8,12 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -21,10 +22,10 @@ import org.jvnet.hk2.annotations.Service;
@CommandAlias("mv")
public class LoadCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public LoadCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
public LoadCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@ -34,7 +35,7 @@ public class LoadCommand extends MultiverseCommand {
@CommandCompletion("@mvworlds:scope=unloaded")
@Syntax("<world>")
@Description("{@@mv-core.load.description}")
public void onLoadCommand(BukkitCommandIssuer issuer,
public void onLoadCommand(MVCommandIssuer issuer,
@Single
@Conditions("worldname:scope=unloaded")
@ -42,15 +43,14 @@ public class LoadCommand extends MultiverseCommand {
@Description("{@@mv-core.load.world.description}")
String worldName
) {
issuer.sendInfo(MVCorei18n.LOAD_LOADING,
"{world}", worldName);
if (!this.worldManager.loadWorld(worldName)) {
issuer.sendError(MVCorei18n.LOAD_FAILED,
"{world}", worldName);
return;
}
issuer.sendInfo(MVCorei18n.LOAD_SUCCESS,
"{world}", worldName);
issuer.sendInfo(MVCorei18n.LOAD_LOADING, "{world}", worldName);
worldManager.loadWorld(worldName)
.onSuccess(newWorld -> {
Logging.fine("World load success: " + newWorld);
issuer.sendInfo(MVCorei18n.LOAD_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World load failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}

View File

@ -1,19 +1,15 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.Collections;
import java.util.Random;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.MessageType;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Conditions;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
@ -22,47 +18,58 @@ import com.onarandombox.MultiverseCore.commandtools.flags.CommandValueFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.commandtools.queue.QueuedCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.options.RegenWorldOptions;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.util.Collections;
import java.util.Random;
@Service
@CommandAlias("mv")
public class RegenCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public RegenCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
public RegenCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
registerFlagGroup(CommandFlagGroup.builder("mvregen")
.add(CommandValueFlag.builder("--seed", String.class)
.addAlias("-s")
.completion(() -> Collections.singleton(String.valueOf(new Random().nextLong())))
.completion((input) -> Collections.singleton(String.valueOf(new Random().nextLong())))
.optional()
.build())
.add(CommandFlag.builder("--keep-gamerules")
.addAlias("-k")
.add(CommandFlag.builder("--reset-world-config")
.addAlias("-wc")
.build())
.add(CommandFlag.builder("--reset-gamerules")
.addAlias("-gm")
.build())
.add(CommandFlag.builder("--reset-world-border")
.addAlias("-wb")
.build())
.build());
}
@Subcommand("regen")
@CommandPermission("multiverse.core.regen")
@CommandCompletion("@mvworlds:scope=both @flags:groupName=mvregen")
@CommandCompletion("@mvworlds:scope=loaded @flags:groupName=mvregen")
@Syntax("<world> --seed [seed] --keep-gamerules")
@Description("{@@mv-core.regen.description}")
public void onRegenCommand(BukkitCommandIssuer issuer,
public void onRegenCommand(MVCommandIssuer issuer,
@Conditions("worldname:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.regen.world.description}")
String worldName,
LoadedMultiverseWorld world,
@Optional
@Syntax("--seed [seed] --keep-gamerules")
@Syntax("--seed [seed] --reset-gamerules")
@Description("{@@mv-core.regen.other.description}")
String[] flags
) {
@ -71,27 +78,26 @@ public class RegenCommand extends MultiverseCommand {
this.commandManager.getCommandQueueManager().addToQueue(new QueuedCommand(
issuer.getIssuer(),
() -> {
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING,
"{world}", worldName);
if (!this.worldManager.regenWorld(
worldName,
parsedFlags.hasFlag("--seed"),
!parsedFlags.hasFlagValue("--seed"),
parsedFlags.flagValue("--seed", String.class),
parsedFlags.hasFlag("--keep-gamerules")
)) {
issuer.sendError(MVCorei18n.REGEN_FAILED,
"{world}", worldName);
return;
}
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS,
"{world}", worldName);
issuer.sendInfo(MVCorei18n.REGEN_REGENERATING, "{world}", world.getName());
worldManager.regenWorld(RegenWorldOptions.world(world)
.randomSeed(parsedFlags.hasFlag("--seed"))
.seed(parsedFlags.flagValue("--seed", String.class))
.keepWorldConfig(!parsedFlags.hasFlag("--reset-world-config"))
.keepGameRule(!parsedFlags.hasFlag("--reset-gamerules"))
.keepWorldBorder(!parsedFlags.hasFlag("--reset-world-border"))
).onSuccess(newWorld -> {
Logging.fine("World regen success: " + newWorld);
issuer.sendInfo(MVCorei18n.REGEN_SUCCESS, "{world}", newWorld.getName());
}).onFailure(failure -> {
Logging.fine("World regen failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
},
this.commandManager.formatMessage(
issuer,
MessageType.INFO,
MVCorei18n.REGEN_PROMPT,
"{world}", worldName)
"{world}", world.getName())
));
}
}

View File

@ -1,34 +1,32 @@
package com.onarandombox.MultiverseCore.commands;
import java.util.ArrayList;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Subcommand;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.anchor.AnchorManager;
import com.onarandombox.MultiverseCore.api.MVCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.event.MVConfigReloadEvent;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.plugin.PluginManager;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.util.ArrayList;
import java.util.List;
@Service
@CommandAlias("mv")
public class ReloadCommand extends MultiverseCommand {
private final MVCoreConfig config;
private final AnchorManager anchorManager;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final PluginManager pluginManager;
@Inject
@ -36,7 +34,7 @@ public class ReloadCommand extends MultiverseCommand {
@NotNull MVCommandManager commandManager,
@NotNull MVCoreConfig config,
@NotNull AnchorManager anchorManager,
@NotNull MVWorldManager worldManager,
@NotNull WorldManager worldManager,
@NotNull PluginManager pluginManager
) {
super(commandManager);
@ -51,14 +49,18 @@ public class ReloadCommand extends MultiverseCommand {
@Description("{@@mv-core.reload.description}")
public void onReloadCommand(@NotNull BukkitCommandIssuer issuer) {
issuer.sendInfo(MVCorei18n.RELOAD_RELOADING);
this.config.load();
this.worldManager.loadWorldsConfig();
this.worldManager.loadWorlds(true);
this.anchorManager.loadAnchors();
try {
// TODO: Make this all Try<Void>
this.config.load().getOrElseThrow(e -> new RuntimeException("Failed to load config", e));
this.worldManager.initAllWorlds().getOrElseThrow(e -> new RuntimeException("Failed to init worlds", e));
this.anchorManager.loadAnchors();
} catch (Exception e) {
e.printStackTrace();
}
List<String> configsLoaded = new ArrayList<>();
configsLoaded.add("Multiverse-Core - config.yml");
configsLoaded.add("Multiverse-Core - worlds.yml");
configsLoaded.add("Multiverse-Core - worlds2.yml");
configsLoaded.add("Multiverse-Core - anchors.yml");
MVConfigReloadEvent configReload = new MVConfigReloadEvent(configsLoaded);

View File

@ -1,6 +1,5 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
@ -9,10 +8,12 @@ import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Single;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -21,10 +22,10 @@ import org.jvnet.hk2.annotations.Service;
@CommandAlias("mv")
public class RemoveCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public RemoveCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
public RemoveCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@ -34,7 +35,7 @@ public class RemoveCommand extends MultiverseCommand {
@CommandCompletion("@mvworlds:scope=both")
@Syntax("<world>")
@Description("{@@mv-core.remove.description}")
public void onRemoveCommand(BukkitCommandIssuer issuer,
public void onRemoveCommand(MVCommandIssuer issuer,
@Single
@Conditions("mvworlds:scope=both")
@ -42,11 +43,13 @@ public class RemoveCommand extends MultiverseCommand {
@Description("{@@mv-core.remove.world.description}")
String worldName
) {
if (!this.worldManager.removeWorldFromConfig(worldName)) {
issuer.sendError(MVCorei18n.REMOVE_FAILED);
return;
}
issuer.sendInfo(MVCorei18n.REMOVE_SUCCESS,
"{world}", worldName);
worldManager.removeWorld(worldName)
.onSuccess(removedWorldName -> {
Logging.fine("World remove success: " + removedWorldName);
issuer.sendInfo(MVCorei18n.REMOVEWORLD_REMOVED, "{world}", removedWorldName);
}).onFailure(failure -> {
Logging.fine("World remove failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}

View File

@ -1,17 +1,22 @@
package com.onarandombox.MultiverseCore.commands;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.annotation.CommandAlias;
import co.aikar.commands.annotation.CommandCompletion;
import co.aikar.commands.annotation.CommandPermission;
import co.aikar.commands.annotation.Description;
import co.aikar.commands.annotation.Optional;
import co.aikar.commands.annotation.Subcommand;
import co.aikar.commands.annotation.Syntax;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.commandtools.MVCommandIssuer;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlag;
import com.onarandombox.MultiverseCore.commandtools.flags.ParsedCommandFlags;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.options.UnloadWorldOptions;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@ -20,35 +25,51 @@ import org.jvnet.hk2.annotations.Service;
@CommandAlias("mv")
public class UnloadCommand extends MultiverseCommand {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final CommandFlag REMOVE_PLAYERS_FLAG = flag(CommandFlag.builder("--remove-players")
.addAlias("-r")
.build());
private final CommandFlag NO_SAVE_FLAG = flag(CommandFlag.builder("--no-save")
.addAlias("-n")
.build());
@Inject
public UnloadCommand(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
public UnloadCommand(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
super(commandManager);
this.worldManager = worldManager;
}
@Subcommand("unload")
@CommandPermission("multiverse.core.unload")
@CommandCompletion("@mvworlds")
@CommandCompletion("@mvworlds @flags:groupName=mvunloadcommand")
@Syntax("<world>")
@Description("{@@mv-core.unload.description}")
public void onUnloadCommand(BukkitCommandIssuer issuer,
public void onUnloadCommand(
MVCommandIssuer issuer,
@Syntax("<world>")
@Description("{@@mv-core.unload.world.description}")
MVWorld world
) {
issuer.sendInfo(MVCorei18n.UNLOAD_UNLOADING,
"{world}", world.getColoredWorldString());
@Syntax("<world>")
@Description("{@@mv-core.unload.world.description}")
LoadedMultiverseWorld world,
// TODO: Should be able to use MVWorld object directly for unloadWorld
if (!this.worldManager.unloadWorld(world.getName())) {
issuer.sendError(MVCorei18n.UNLOAD_FAILURE,
"{world}", world.getColoredWorldString());
return;
}
issuer.sendInfo(MVCorei18n.UNLOAD_SUCCESS,
"{world}", world.getColoredWorldString());
@Optional
@Syntax("[--remove-players] [--no-save]")
@Description("{@@mv-core.gamerules.description.page}")
String[] flags) {
ParsedCommandFlags parsedFlags = parseFlags(flags);
issuer.sendInfo(MVCorei18n.UNLOAD_UNLOADING, "{world}", world.getAlias());
UnloadWorldOptions unloadWorldOptions = UnloadWorldOptions.world(world)
.removePlayers(parsedFlags.hasFlag(REMOVE_PLAYERS_FLAG))
.saveBukkitWorld(!parsedFlags.hasFlag(NO_SAVE_FLAG));
worldManager.unloadWorld(unloadWorldOptions)
.onSuccess(loadedWorld -> {
Logging.fine("World unload success: " + loadedWorld);
issuer.sendInfo(MVCorei18n.UNLOAD_SUCCESS, "{world}", loadedWorld.getName());
}).onFailure(failure -> {
Logging.fine("World unload failure: " + failure);
issuer.sendError(failure.getFailureMessage());
});
}
}

View File

@ -1,6 +1,25 @@
package com.onarandombox.MultiverseCore.commandtools;
import java.util.ArrayList;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.PaperCommandCompletions;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.RootCommand;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.collect.Sets;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.MultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -9,35 +28,17 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.BukkitCommandIssuer;
import co.aikar.commands.CommandIssuer;
import co.aikar.commands.PaperCommandCompletions;
import co.aikar.commands.RegisteredCommand;
import co.aikar.commands.RootCommand;
import com.google.common.collect.Sets;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
public class MVCommandCompletions extends PaperCommandCompletions {
protected final MVCommandManager commandManager;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final DestinationsProvider destinationsProvider;
@Inject
public MVCommandCompletions(
@NotNull MVCommandManager mvCommandManager,
@NotNull MVWorldManager worldManager,
@NotNull WorldManager worldManager,
@NotNull DestinationsProvider destinationsProvider,
@NotNull MVCoreConfig config
) {
@ -56,7 +57,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
setDefaultCompletion("destinations", ParsedDestination.class);
setDefaultCompletion("flags", String[].class);
setDefaultCompletion("gamerules", GameRule.class);
setDefaultCompletion("mvworlds", MVWorld.class);
setDefaultCompletion("mvworlds", LoadedMultiverseWorld.class);
}
private Collection<String> suggestCommands(BukkitCommandCompletionContext context) {
@ -128,23 +129,26 @@ public class MVCommandCompletions extends PaperCommandCompletions {
private List<String> getMVWorldNames(BukkitCommandCompletionContext context) {
String scope = context.getConfig("scope", "loaded");
List<String> worlds = new ArrayList<>();
switch (scope) {
case "both":
worlds.addAll(worldManager.getUnloadedWorlds());
case "loaded":
worldManager.getMVWorlds()
case "both" -> {
return worldManager.getWorlds().stream().map(MultiverseWorld::getName).toList();
}
case "loaded" -> {
return worldManager.getLoadedWorlds()
.stream()
.map(MVWorld::getName)
.forEach(worlds::add);
break;
case "unloaded":
worlds.addAll(worldManager.getUnloadedWorlds());
break;
case "potential":
worlds.addAll(worldManager.getPotentialWorlds());
break;
.map(LoadedMultiverseWorld::getName)
.toList();
}
case "unloaded" -> {
return worldManager.getUnloadedWorlds().stream()
.map(MultiverseWorld::getName)
.toList();
}
case "potential" -> {
return worldManager.getPotentialWorlds();
}
}
return worlds;
Logging.severe("Invalid MVWorld scope: " + scope);
return Collections.emptyList();
}
}

View File

@ -6,19 +6,19 @@ import co.aikar.commands.BukkitConditionContext;
import co.aikar.commands.CommandConditions;
import co.aikar.commands.ConditionContext;
import co.aikar.commands.ConditionFailedException;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.world.WorldNameChecker;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import org.jetbrains.annotations.NotNull;
public class MVCommandConditions {
static void load(MVCommandManager commandManager, MVWorldManager worldManager) {
static void load(MVCommandManager commandManager, WorldManager worldManager) {
new MVCommandConditions(commandManager, worldManager);
}
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final MVCommandManager commandManager;
private MVCommandConditions(@NotNull MVCommandManager commandManager, @NotNull MVWorldManager worldManager) {
private MVCommandConditions(@NotNull MVCommandManager commandManager, @NotNull WorldManager worldManager) {
this.worldManager = worldManager;
this.commandManager = commandManager;
registerConditions();
@ -40,14 +40,14 @@ public class MVCommandConditions {
switch (scope) {
// Worlds that are loaded
case "loaded":
if (!this.worldManager.isMVWorld(worldName)) {
if (!this.worldManager.isLoadedWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' does not exist or is not loaded!");
}
break;
// Worlds that are unloaded
case "unloaded":
if (!this.worldManager.hasUnloadedWorld(worldName, false)) {
if (this.worldManager.isMVWorld(worldName)) {
if (!this.worldManager.isUnloadedWorld(worldName)) {
if (this.worldManager.isLoadedWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' is loaded already!");
}
throw new ConditionFailedException("World with name '" + worldName + "' does not exist!");
@ -55,20 +55,20 @@ public class MVCommandConditions {
break;
// World that are loaded or unloaded
case "both":
if (!this.worldManager.hasUnloadedWorld(worldName, true)) {
if (!this.worldManager.isWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' does not exist!");
}
break;
// World that are does not exist
case "new":
if (this.worldManager.hasUnloadedWorld(worldName, true)) {
if (this.worldManager.isWorld(worldName)) {
throw new ConditionFailedException("World with name '" + worldName + "' already exists!");
}
switch (WorldNameChecker.checkName(worldName)) {
case INVALID_CHARS:
throw new ConditionFailedException("World name '" + worldName + "' contains invalid characters!");
case BLACKLISTED:
throw new ConditionFailedException("World name '" + worldName + "' is used for critical server operations and is blacklisted!");
case INVALID_CHARS ->
throw new ConditionFailedException("World name '" + worldName + "' contains invalid characters!");
case BLACKLISTED ->
throw new ConditionFailedException("World name '" + worldName + "' is used for critical server operations and is blacklisted!");
}
break;
// Probably a typo happened here

View File

@ -1,7 +1,6 @@
package com.onarandombox.MultiverseCore.commandtools;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import co.aikar.commands.BukkitCommandExecutionContext;
@ -10,8 +9,6 @@ import co.aikar.commands.InvalidCommandArgument;
import co.aikar.commands.PaperCommandContexts;
import co.aikar.commands.contexts.ContextResolver;
import com.google.common.base.Strings;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.context.GameRuleValue;
import com.onarandombox.MultiverseCore.commandtools.context.MVConfigValue;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
@ -23,6 +20,8 @@ import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.utils.PlayerFinder;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import org.bukkit.GameRule;
@ -34,14 +33,14 @@ public class MVCommandContexts extends PaperCommandContexts {
private final MVCommandManager mvCommandManager;
private final DestinationsProvider destinationsProvider;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final MVCoreConfig config;
@Inject
public MVCommandContexts(
MVCommandManager mvCommandManager,
DestinationsProvider destinationsProvider,
MVWorldManager worldManager,
WorldManager worldManager,
MVCoreConfig config
) {
super(mvCommandManager);
@ -57,8 +56,8 @@ public class MVCommandContexts extends PaperCommandContexts {
registerContext(GameRule.class, this::parseGameRule);
registerContext(GameRuleValue.class, this::parseGameRuleValue);
registerContext(MVConfigValue.class, this::parseMVConfigValue);
registerIssuerAwareContext(MVWorld.class, this::parseMVWorld);
registerIssuerAwareContext(MVWorld[].class, this::parseMVWorldArray);
registerIssuerAwareContext(LoadedMultiverseWorld.class, this::parseMVWorld);
registerIssuerAwareContext(LoadedMultiverseWorld[].class, this::parseMVWorldArray);
registerIssuerAwareContext(Player.class, this::parsePlayer);
registerIssuerAwareContext(Player[].class, this::parsePlayerArray);
}
@ -162,13 +161,13 @@ public class MVCommandContexts extends PaperCommandContexts {
return new MVConfigValue(resolvedValue);
}
private MVWorld parseMVWorld(BukkitCommandExecutionContext context) {
private LoadedMultiverseWorld parseMVWorld(BukkitCommandExecutionContext context) {
String resolve = context.getFlagValue("resolve", "");
// Get world based on sender only
if (resolve.equals("issuerOnly")) {
if (context.getIssuer().isPlayer()) {
return worldManager.getMVWorld(context.getIssuer().getPlayer().getWorld());
return worldManager.getLoadedWorld(context.getIssuer().getPlayer().getWorld()).getOrNull();
}
if (context.isOptional()) {
return null;
@ -177,7 +176,7 @@ public class MVCommandContexts extends PaperCommandContexts {
}
String worldName = context.getFirstArg();
MVWorld world = worldManager.getMVWorld(worldName);
LoadedMultiverseWorld world = worldManager.getLoadedWorld(worldName).getOrNull();
// Get world based on input, fallback to sender if input is not a world
if (resolve.equals("issuerAware")) {
@ -186,7 +185,7 @@ public class MVCommandContexts extends PaperCommandContexts {
return world;
}
if (context.getIssuer().isPlayer()) {
return worldManager.getMVWorld(context.getIssuer().getPlayer().getWorld());
return worldManager.getLoadedWorld(context.getIssuer().getPlayer().getWorld()).getOrNull();
}
if (context.isOptional()) {
return null;
@ -205,18 +204,18 @@ public class MVCommandContexts extends PaperCommandContexts {
throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world.");
}
private MVWorld[] parseMVWorldArray(BukkitCommandExecutionContext context) {
private LoadedMultiverseWorld[] parseMVWorldArray(BukkitCommandExecutionContext context) {
String resolve = context.getFlagValue("resolve", "");
MVWorld playerWorld = null;
LoadedMultiverseWorld playerWorld = null;
if (context.getIssuer().isPlayer()) {
playerWorld = worldManager.getMVWorld(context.getIssuer().getPlayer().getWorld());
playerWorld = worldManager.getLoadedWorld(context.getIssuer().getPlayer().getWorld()).getOrNull();
}
// Get world based on sender only
if (resolve.equals("issuerOnly")) {
if (playerWorld != null) {
return new MVWorld[]{playerWorld};
return new LoadedMultiverseWorld[]{playerWorld};
}
if (context.isOptional()) {
return null;
@ -226,13 +225,13 @@ public class MVCommandContexts extends PaperCommandContexts {
String worldStrings = context.getFirstArg();
String[] worldNames = worldStrings == null ? new String[0] : worldStrings.split(",");
Set<MVWorld> worlds = new HashSet<>(worldNames.length);
Set<LoadedMultiverseWorld> worlds = new HashSet<>(worldNames.length);
for (String worldName : worldNames) {
if ("*".equals(worldName)) {
worlds.addAll(worldManager.getMVWorlds());
worlds.addAll(worldManager.getLoadedWorlds());
break;
}
MVWorld world = worldManager.getMVWorld(worldName);
LoadedMultiverseWorld world = worldManager.getLoadedWorld(worldName).getOrNull();
if (world == null) {
throw new InvalidCommandArgument("World " + worldName + " is not a loaded multiverse world.");
}
@ -243,10 +242,10 @@ public class MVCommandContexts extends PaperCommandContexts {
if (resolve.equals("issuerAware")) {
if (!worlds.isEmpty()) {
context.popFirstArg();
return worlds.toArray(new MVWorld[0]);
return worlds.toArray(new LoadedMultiverseWorld[0]);
}
if (playerWorld != null) {
return new MVWorld[]{playerWorld};
return new LoadedMultiverseWorld[]{playerWorld};
}
if (context.isOptional()) {
return null;
@ -257,7 +256,7 @@ public class MVCommandContexts extends PaperCommandContexts {
// Get world based on input only
if (!worlds.isEmpty()) {
context.popFirstArg();
return worlds.toArray(new MVWorld[0]);
return worlds.toArray(new LoadedMultiverseWorld[0]);
}
if (context.isOptional()) {
return null;

View File

@ -1,7 +1,5 @@
package com.onarandombox.MultiverseCore.commandtools;
import java.util.List;
import co.aikar.commands.BukkitCommandCompletionContext;
import co.aikar.commands.BukkitCommandExecutionContext;
import co.aikar.commands.BukkitLocales;
@ -11,9 +9,9 @@ import co.aikar.commands.CommandHelp;
import co.aikar.commands.HelpEntry;
import co.aikar.commands.PaperCommandManager;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.commandtools.flags.CommandFlagsManager;
import com.onarandombox.MultiverseCore.commandtools.queue.CommandQueueManager;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.Bukkit;
@ -21,6 +19,8 @@ import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.util.List;
/**
* Main class to manage permissions.
*/
@ -39,7 +39,7 @@ public class MVCommandManager extends PaperCommandManager {
@NotNull CommandQueueManager commandQueueManager,
@NotNull Provider<MVCommandContexts> commandContextsProvider,
@NotNull Provider<MVCommandCompletions> commandCompletionsProvider,
@NotNull MVWorldManager worldManager
@NotNull WorldManager worldManager
) {
super(plugin);
this.flagsManager = flagsManager;

View File

@ -60,10 +60,9 @@ public class CommandFlagsManager {
Collection<String> suggestions = new ArrayList<>();
CommandFlag currentFlag = (flags.length <= 1) ? null : flagGroup.getFlagByKey(flags[flags.length - 2]);
if (currentFlag instanceof CommandValueFlag) {
CommandValueFlag<?> valueFlag = (CommandValueFlag<?>) currentFlag;
if (currentFlag instanceof CommandValueFlag<?> valueFlag) {
if (valueFlag.getCompletion() != null) {
suggestions.addAll(valueFlag.getCompletion().get());
suggestions.addAll(valueFlag.getCompletion().apply(flags[flags.length - 1]));
}
if (valueFlag.isOptional()) {
suggestions.addAll(flagGroup.getRemainingKeys(flags));

View File

@ -8,7 +8,6 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
@ -43,7 +42,7 @@ public class CommandValueFlag<T> extends CommandFlag {
private final boolean optional;
private final T defaultValue;
private final Function<String, T> context;
private final Supplier<Collection<String>> completion;
private final Function<String, Collection<String>> completion;
/**
* Creates a new flag.
@ -63,7 +62,7 @@ public class CommandValueFlag<T> extends CommandFlag {
boolean optional,
@Nullable T defaultValue,
@Nullable Function<String, T> context,
@Nullable Supplier<Collection<String>> completion
@Nullable Function<String, Collection<String>> completion
) {
super(key, aliases);
this.type = type;
@ -114,7 +113,7 @@ public class CommandValueFlag<T> extends CommandFlag {
*
* @return The completion.
*/
public @Nullable Supplier<Collection<String>> getCompletion() {
public @Nullable Function<String, Collection<String>> getCompletion() {
return completion;
}
@ -129,7 +128,7 @@ public class CommandValueFlag<T> extends CommandFlag {
protected boolean optional = false;
protected T defaultValue = null;
protected Function<String, T> context = null;
protected Supplier<Collection<String>> completion = null;
protected Function<String, Collection<String>> completion = null;
/**
* Create a new builder.
@ -177,10 +176,10 @@ public class CommandValueFlag<T> extends CommandFlag {
/**
* Set the completion callback for autocomplete.
*
* @param completion The completion.
* @param completion The completion. Input is the current input string, and output is the list of suggestions.
* @return The builder.
*/
public @NotNull S completion(@NotNull Supplier<Collection<String>> completion) {
public @NotNull S completion(@NotNull Function<String, Collection<String>> completion) {
this.completion = completion;
return (S) this;
}
@ -210,7 +209,7 @@ public class CommandValueFlag<T> extends CommandFlag {
protected boolean optional = false;
protected T defaultValue = null;
protected Function<String, T> context = null;
protected Supplier<Collection<String>> completion = null;
protected Function<String, Collection<String>> completion = null;
public EnumBuilder(@NotNull String key, @NotNull Class<T> type) {
super(key);
@ -234,7 +233,7 @@ public class CommandValueFlag<T> extends CommandFlag {
.map(type -> type.name().toLowerCase())
.collect(Collectors.toList());
this.completion = () -> types;
this.completion = (input) -> types;
}
/**

View File

@ -89,7 +89,7 @@ public class MVCoreConfig implements MVConfig {
}
@Override
public boolean load() {
public Try<Void> load() {
migrateFromOldConfigFile();
return configHandle.load();
}
@ -100,9 +100,8 @@ public class MVCoreConfig implements MVConfig {
}
@Override
public boolean save() {
configHandle.save();
return true;
public Try<Void> save() {
return configHandle.save();
}
@Override

View File

@ -1,5 +1,6 @@
package com.onarandombox.MultiverseCore.configuration.handle;
import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
@ -9,6 +10,7 @@ import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.configuration.node.CommentedNode;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import io.github.townyadvanced.commentedconfiguration.CommentedConfiguration;
import io.vavr.control.Try;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -35,9 +37,12 @@ public class CommentedYamlConfigHandle extends FileConfigHandle<CommentedConfigu
* {@inheritDoc}
*/
@Override
protected boolean loadConfigObject() {
protected void loadConfigObject() throws IOException {
config = new CommentedConfiguration(configPath, logger);
return config.load();
if (!config.load()) {
throw new IOException("Failed to load commented config file " + configPath
+ ". See console for details.");
}
}
/**
@ -71,9 +76,9 @@ public class CommentedYamlConfigHandle extends FileConfigHandle<CommentedConfigu
* {@inheritDoc}
*/
@Override
public boolean save() {
config.save();
return true;
public Try<Void> save() {
// TODO: There is no way to check if the save was successful.
return Try.run(() -> config.save());
}
public static class Builder extends FileConfigHandle.Builder<CommentedConfiguration, Builder> {

View File

@ -2,6 +2,7 @@ package com.onarandombox.MultiverseCore.configuration.handle;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import io.vavr.control.Try;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -16,7 +17,7 @@ public class ConfigurationSectionHandle extends GenericConfigHandle<Configuratio
return new Builder<>(configurationSection);
}
public ConfigurationSectionHandle(@NotNull ConfigurationSection configurationSection,
protected ConfigurationSectionHandle(@NotNull ConfigurationSection configurationSection,
@Nullable Logger logger,
@Nullable NodeGroup nodes,
@Nullable ConfigMigrator migrator) {
@ -24,6 +25,17 @@ public class ConfigurationSectionHandle extends GenericConfigHandle<Configuratio
this.config = configurationSection;
}
/**
* Loads the configuration with a new configuration section.
*
* @param section The configuration section.
* @return Whether the configuration was loaded or its given error.
*/
public Try<Void> load(@NotNull ConfigurationSection section) {
this.config = section;
return load();
}
/**
* Builder for {@link ConfigurationSectionHandle}.
*

View File

@ -5,12 +5,11 @@ import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Logger;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import io.vavr.control.Try;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -33,49 +32,43 @@ abstract class FileConfigHandle<C extends FileConfiguration> extends GenericConf
* {@inheritDoc}
*/
@Override
public boolean load() {
boolean newFileCreated;
try {
newFileCreated = createConfigFile();
} catch (IOException e) {
Logging.severe("Failed to create config file: %s", configFile.getName());
Logging.severe(e.getMessage());
return false;
}
if (!loadConfigObject()) {
Logging.severe("Failed to load config file: %s", configFile.getName());
return false;
}
if (!newFileCreated) {
migrateConfig();
}
setUpNodes();
return true;
public Try<Void> load() {
boolean isNewFile = !configFile.exists();
return createConfigFile()
.andThenTry(this::loadConfigObject)
.andThenTry(() -> {
if (!isNewFile) {
migrateConfig();
}
setUpNodes();
});
}
/**
* Create a new config file if file does not exist
* Create a new config file if file does not exist.
*
* @return True if file exist or created successfully, otherwise false.
* @return Whether the file was created or its given error.
*/
protected boolean createConfigFile() throws IOException {
if (configFile.exists()) {
return false;
}
return configFile.createNewFile();
protected Try<Void> createConfigFile() {
return Try.run(() -> {
if (configFile.exists()) {
return;
}
if (!configFile.createNewFile()) {
throw new IOException("Failed to create config file: " + configFile.getName());
}
});
}
/**
* Loads the configuration object.
*
* @return True if the configuration was loaded successfully, false otherwise.
*/
protected abstract boolean loadConfigObject();
protected abstract void loadConfigObject() throws IOException, InvalidConfigurationException;
/**
* Saves the configuration.
*/
public abstract boolean save();
public abstract Try<Void> save();
/**
* Checks if the configuration is loaded.

View File

@ -3,9 +3,7 @@ package com.onarandombox.MultiverseCore.configuration.handle;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.ConfigNodeNotFoundException;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.configuration.node.NodeSerializer;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import io.vavr.control.Option;
import io.vavr.control.Try;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.Plugin;
@ -24,7 +22,7 @@ public abstract class GenericConfigHandle<C extends ConfigurationSection> {
protected C config;
public GenericConfigHandle(@Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
protected GenericConfigHandle(@Nullable Logger logger, @Nullable NodeGroup nodes, @Nullable ConfigMigrator migrator) {
this.logger = logger;
this.nodes = nodes;
this.migrator = migrator;
@ -33,12 +31,15 @@ public abstract class GenericConfigHandle<C extends ConfigurationSection> {
/**
* Loads the configuration.
*
* @return True if the configuration was loaded successfully, false otherwise.
* @return Whether the configuration was loaded or its given error.
*/
public boolean load() {
migrateConfig();
setUpNodes();
return true;
public Try<Void> load() {
return Try.run(() -> {
if (!config.getKeys(false).isEmpty()) {
migrateConfig();
}
setUpNodes();
});
}
/**

View File

@ -6,7 +6,7 @@ import java.util.logging.Logger;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.configuration.node.ValueNode;
import io.vavr.control.Try;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
@ -35,27 +35,17 @@ public class YamlConfigHandle extends FileConfigHandle<YamlConfiguration> {
* {@inheritDoc}
*/
@Override
protected boolean loadConfigObject() {
protected void loadConfigObject() throws IOException, InvalidConfigurationException {
config = new YamlConfiguration();
try {
config.load(configFile);
} catch (IOException | InvalidConfigurationException e) {
return false;
}
return true;
config.load(configFile);
}
/**
* {@inheritDoc}
*/
@Override
public boolean save() {
try {
config.save(configFile);
} catch (IOException e) {
return false;
}
return true;
public Try<Void> save() {
return Try.run(() -> config.save(configFile));
}
/**

View File

@ -0,0 +1,30 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import co.aikar.commands.ACFUtil;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.configuration.ConfigurationSection;
/**
* Single migrator action that converts a string value to a long.
*/
public class LongMigratorAction implements MigratorAction {
public static LongMigratorAction of(String path) {
return new LongMigratorAction(path);
}
private final String path;
LongMigratorAction(String path) {
this.path = path;
}
/**
* {@inheritDoc}
*/
@Override
public void migrate(ConfigurationSection config) {
config.set(path, ACFUtil.parseLong(config.getString(path)));
Logging.info("Converted %s to long %s", path, config.getLong(path));
}
}

View File

@ -37,8 +37,8 @@ public class MoveMigratorAction implements MigratorAction {
public void migrate(ConfigurationSection config) {
Optional.ofNullable(config.get(fromPath))
.ifPresent(value -> {
config.set(toPath, value);
config.set(fromPath, null);
config.set(toPath, value);
Logging.config("Moved path %s to %s", fromPath, toPath);
});
}

View File

@ -0,0 +1,29 @@
package com.onarandombox.MultiverseCore.configuration.migration;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.configuration.ConfigurationSection;
/**
* Single migrator action changes a string value of "null" to an empty string.
*/
public class NullStringMigratorAction implements MigratorAction {
public static NullStringMigratorAction of(String path) {
return new NullStringMigratorAction(path);
}
private final String path;
protected NullStringMigratorAction(String path) {
this.path = path;
}
/**
* {@inheritDoc}
*/
@Override
public void migrate(ConfigurationSection config) {
config.set(path, "null".equals(config.getString(path)) ? "" : config.getString(path));
Logging.info("Converted %s to %s", path, config.getString(path));
}
}

View File

@ -5,11 +5,12 @@ import java.util.Collections;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.Teleporter;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@ -17,10 +18,10 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class CannonDestination implements Destination<CannonDestinationInstance> {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public CannonDestination(MVWorldManager worldManager) {
public CannonDestination(WorldManager worldManager) {
this.worldManager = worldManager;
}
@ -53,7 +54,7 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
return null;
}
MVWorld world = this.worldManager.getMVWorld(worldName);
World world = this.worldManager.getLoadedWorld(worldName).map(LoadedMultiverseWorld::getBukkitWorld).getOrNull().getOrNull();
if (world == null) {
return null;
}
@ -62,7 +63,7 @@ public class CannonDestination implements Destination<CannonDestinationInstance>
double dSpeed;
try {
location = new Location(
world.getCBWorld(),
world,
Double.parseDouble(coordinatesParams[0]),
Double.parseDouble(coordinatesParams[1]),
Double.parseDouble(coordinatesParams[2]),

View File

@ -5,11 +5,12 @@ import java.util.Collections;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.Teleporter;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.Location;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
@ -17,10 +18,10 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class ExactDestination implements Destination<ExactDestinationInstance> {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public ExactDestination(MVWorldManager worldManager) {
public ExactDestination(WorldManager worldManager) {
this.worldManager = worldManager;
}
@ -49,7 +50,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
return null;
}
MVWorld world = this.worldManager.getMVWorld(worldName);
World world = this.worldManager.getLoadedWorld(worldName).map(LoadedMultiverseWorld::getBukkitWorld).getOrNull().getOrNull();
if (world == null) {
return null;
}
@ -57,7 +58,7 @@ public class ExactDestination implements Destination<ExactDestinationInstance> {
Location location;
try {
location = new Location(
world.getCBWorld(),
world,
Double.parseDouble(coordinatesParams[0]),
Double.parseDouble(coordinatesParams[1]),
Double.parseDouble(coordinatesParams[2])

View File

@ -6,9 +6,9 @@ import java.util.Collections;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.api.Destination;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.Teleporter;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -17,11 +17,11 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class WorldDestination implements Destination<WorldDestinationInstance> {
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final LocationManipulation locationManipulation;
@Inject
public WorldDestination(MVWorldManager worldManager, LocationManipulation locationManipulation) {
public WorldDestination(WorldManager worldManager, LocationManipulation locationManipulation) {
this.worldManager = worldManager;
this.locationManipulation = locationManipulation;
}
@ -45,7 +45,7 @@ public class WorldDestination implements Destination<WorldDestinationInstance> {
}
String worldName = items[0];
MVWorld world = this.worldManager.getMVWorld(worldName);
LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(worldName).getOrNull();
if (world == null) {
return null;
}

View File

@ -1,7 +1,7 @@
package com.onarandombox.MultiverseCore.destination.core;
import com.onarandombox.MultiverseCore.api.DestinationInstance;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class WorldDestinationInstance implements DestinationInstance {
private final MVWorld world;
private final LoadedMultiverseWorld world;
private final String direction;
private final float yaw;
@ -18,7 +18,7 @@ public class WorldDestinationInstance implements DestinationInstance {
*
* @param world The world to teleport to.
*/
public WorldDestinationInstance(@NotNull MVWorld world, @Nullable String direction, float yaw) {
public WorldDestinationInstance(@NotNull LoadedMultiverseWorld world, @Nullable String direction, float yaw) {
this.world = world;
this.direction = direction;
this.yaw = yaw;
@ -59,8 +59,8 @@ public class WorldDestinationInstance implements DestinationInstance {
@Override
public @NotNull String serialise() {
if (this.direction != null) {
return this.world.getCBWorld().getName() + ":" + this.direction;
return this.world.getName() + ":" + this.direction;
}
return this.world.getCBWorld().getName();
return this.world.getName();
}
}

View File

@ -1,6 +1,6 @@
package com.onarandombox.MultiverseCore.economy;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import jakarta.inject.Inject;
import org.bukkit.Material;
import org.bukkit.World;
@ -98,7 +98,7 @@ public class MVEconomist {
* @param player the player to deposit currency into.
* @param world the world to take entry fee from.
*/
public void payEntryFee(Player player, MVWorld world) {
public void payEntryFee(Player player, LoadedMultiverseWorld world) {
payEntryFee(player, world.getPrice(), world.getCurrency());
}

View File

@ -1,10 +1,8 @@
package com.onarandombox.MultiverseCore.listeners;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler;
@ -17,13 +15,13 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class MVChatListener implements InjectableListener {
private final MVCoreConfig config;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final MVPlayerListener playerListener;
@Inject
public MVChatListener(
MVCoreConfig config,
MVWorldManager worldManager,
WorldManager worldManager,
MVPlayerListener playerListener
) {
this.config = config;
@ -40,31 +38,28 @@ public class MVChatListener implements InjectableListener {
if (event.isCancelled()) {
return;
}
// Check whether the Server is set to prefix the chat with the World name.
// If not we do nothing, if so we need to check if the World has an Alias.
if (config.isEnablePrefixChat()) {
String world = playerListener.getPlayerWorld().get(event.getPlayer().getName());
if (world == null) {
world = event.getPlayer().getWorld().getName();
playerListener.getPlayerWorld().put(event.getPlayer().getName(), world);
}
String prefix = "";
// If we're not a MV world, don't do anything
if (!this.worldManager.isMVWorld(world)) {
return;
}
MVWorld mvworld = this.worldManager.getMVWorld(world);
if (mvworld.isHidden()) {
return;
}
prefix = mvworld.getColoredWorldString();
String chat = event.getFormat();
String prefixChatFormat = config.getPrefixChatFormat();
prefixChatFormat = prefixChatFormat.replace("%world%", prefix).replace("%chat%", chat);
prefixChatFormat = ChatColor.translateAlternateColorCodes('&', prefixChatFormat);
event.setFormat(prefixChatFormat);
if (!config.isEnablePrefixChat()) {
return;
}
String world = playerListener.getPlayerWorld().get(event.getPlayer().getName());
if (world == null) {
world = event.getPlayer().getWorld().getName();
playerListener.getPlayerWorld().put(event.getPlayer().getName(), world);
}
String prefix = this.worldManager.getLoadedWorld(world)
.map((mvworld) -> mvworld.isHidden() ? "" : mvworld.getAlias())
.getOrElse("");
String chat = event.getFormat();
String prefixChatFormat = config.getPrefixChatFormat();
prefixChatFormat = prefixChatFormat.replace("%world%", prefix).replace("%chat%", chat);
prefixChatFormat = ChatColor.translateAlternateColorCodes('&', prefixChatFormat);
event.setFormat(prefixChatFormat);
}
}

View File

@ -8,20 +8,15 @@
package com.onarandombox.MultiverseCore.listeners;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.WorldPurger;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.WorldPurger;
import jakarta.inject.Inject;
import org.bukkit.World;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason;
import org.bukkit.event.entity.FoodLevelChangeEvent;
@ -33,17 +28,14 @@ import org.jvnet.hk2.annotations.Service;
*/
@Service
public class MVEntityListener implements InjectableListener {
private final MVCoreConfig config;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final WorldPurger worldPurger;
@Inject
public MVEntityListener(
@NotNull MVCoreConfig config,
@NotNull MVWorldManager worldManager,
@NotNull WorldManager worldManager,
@NotNull WorldPurger worldPurger
) {
this.config = config;
this.worldManager = worldManager;
this.worldPurger = worldPurger;
}
@ -57,16 +49,15 @@ public class MVEntityListener implements InjectableListener {
if (event.isCancelled()) {
return;
}
if (event.getEntity() instanceof Player) {
Player p = (Player) event.getEntity();
MVWorld w = this.worldManager.getMVWorld(p.getWorld().getName());
if (w != null && !w.getHunger()) {
// If the world has hunger set to false, do not let the level go down
if (event.getFoodLevel() < ((Player) event.getEntity()).getFoodLevel()) {
event.setCancelled(true);
}
}
if (!(event.getEntity() instanceof Player player)) {
return;
}
worldManager.getLoadedWorld(player.getWorld())
.peek((world) -> {
if (!world.getHunger() && event.getFoodLevel() < player.getFoodLevel()) {
event.setCancelled(true);
}
});
}
/**
@ -75,14 +66,15 @@ public class MVEntityListener implements InjectableListener {
*/
@EventHandler
public void entityRegainHealth(EntityRegainHealthEvent event) {
if (event.isCancelled()) {
if (event.isCancelled() || event.getRegainReason() != RegainReason.REGEN) {
return;
}
RegainReason reason = event.getRegainReason();
MVWorld world = this.worldManager.getMVWorld(event.getEntity().getLocation().getWorld());
if (world != null && reason == RegainReason.REGEN && !world.getAutoHeal()) {
event.setCancelled(true);
}
worldManager.getLoadedWorld(event.getEntity().getWorld())
.peek((world) -> {
if (!world.getAutoHeal()) {
event.setCancelled(true);
}
});
}
/**
@ -91,45 +83,24 @@ public class MVEntityListener implements InjectableListener {
*/
@EventHandler
public void creatureSpawn(CreatureSpawnEvent event) {
if (event.isCancelled()) {
return;
}
// Check to see if the Creature is spawned by a plugin, we don't want to prevent this behaviour.
// TODO: Allow the egg thing to be a config param. Doubt this will be per world; seems silly.
if (event.getSpawnReason() == SpawnReason.CUSTOM || event.getSpawnReason() == SpawnReason.SPAWNER_EGG
if (event.getSpawnReason() == SpawnReason.CUSTOM
|| event.getSpawnReason() == SpawnReason.SPAWNER_EGG
|| event.getSpawnReason() == SpawnReason.BREEDING) {
return;
}
World world = event.getEntity().getWorld();
if (event.isCancelled())
return;
// Check if it's a world which we are meant to be managing.
if (!(this.worldManager.isMVWorld(world.getName())))
return;
EntityType type = event.getEntityType();
/**
* Handle people with non-standard animals: ie a patched craftbukkit.
*/
if (type == null || type.getName() == null) {
Logging.finer("Found a null typed creature.");
return;
}
MVWorld mvworld = this.worldManager.getMVWorld(world.getName());
event.setCancelled(this.worldPurger.shouldWeKillThisCreature(mvworld, event.getEntity()));
}
/**
* Handles portal search radius adjustment.
* @param event The Event that was fired.
*/
@EventHandler
public void entityPortal(EntityPortalEvent event) {
if (event.isCancelled() || event.getTo() == null) {
return;
}
if (!config.isUsingCustomPortalSearch()) {
event.setSearchRadius(config.getCustomPortalSearchRadius());
}
worldManager.getLoadedWorld(event.getEntity().getWorld())
.peek((world) -> {
if (this.worldPurger.shouldWeKillThisCreature(world, event.getEntity())) {
Logging.finer("Cancelling Creature Spawn Event for: " + event.getEntity());
event.setCancelled(true);
}
});
}
}

View File

@ -7,25 +7,24 @@
package com.onarandombox.MultiverseCore.listeners;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.event.MVRespawnEvent;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.permissions.CorePermissionsChecker;
import com.onarandombox.MultiverseCore.teleportation.TeleportQueue;
import com.onarandombox.MultiverseCore.utils.result.ResultChain;
import com.onarandombox.MultiverseCore.world.entrycheck.EntryFeeResult;
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.entrycheck.EntryFeeResult;
import com.onarandombox.MultiverseCore.worldnew.entrycheck.WorldEntryCheckerProvider;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.GameMode;
@ -45,6 +44,10 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
import org.jvnet.hk2.annotations.Service;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* Multiverse's Listener for players.
*/
@ -52,7 +55,7 @@ import org.jvnet.hk2.annotations.Service;
public class MVPlayerListener implements InjectableListener {
private final Plugin plugin;
private final MVCoreConfig config;
private final Provider<MVWorldManager> worldManagerProvider;
private final Provider<WorldManager> worldManagerProvider;
private final SafeTTeleporter safeTTeleporter;
private final Server server;
private final TeleportQueue teleportQueue;
@ -60,6 +63,7 @@ public class MVPlayerListener implements InjectableListener {
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
private final Provider<MVCommandManager> commandManagerProvider;
private final CorePermissionsChecker permissionsChecker;
private final DestinationsProvider destinationsProvider;
private final Map<String, String> playerWorld = new ConcurrentHashMap<String, String>();
@ -67,14 +71,15 @@ public class MVPlayerListener implements InjectableListener {
public MVPlayerListener(
MultiverseCore plugin,
MVCoreConfig config,
Provider<MVWorldManager> worldManagerProvider,
Provider<WorldManager> worldManagerProvider,
SafeTTeleporter safeTTeleporter,
Server server,
TeleportQueue teleportQueue,
MVEconomist economist,
WorldEntryCheckerProvider worldEntryCheckerProvider,
Provider<MVCommandManager> commandManagerProvider,
CorePermissionsChecker permissionsChecker
CorePermissionsChecker permissionsChecker,
DestinationsProvider destinationsProvider
) {
this.plugin = plugin;
this.config = config;
@ -86,9 +91,10 @@ public class MVPlayerListener implements InjectableListener {
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
this.commandManagerProvider = commandManagerProvider;
this.permissionsChecker = permissionsChecker;
this.destinationsProvider = destinationsProvider;
}
private MVWorldManager getWorldManager() {
private WorldManager getWorldManager() {
return worldManagerProvider.get();
}
@ -110,7 +116,7 @@ public class MVPlayerListener implements InjectableListener {
@EventHandler(priority = EventPriority.LOW)
public void playerRespawn(PlayerRespawnEvent event) {
World world = event.getPlayer().getWorld();
MVWorld mvWorld = getWorldManager().getMVWorld(world.getName());
LoadedMultiverseWorld mvWorld = getWorldManager().getLoadedWorld(world.getName()).getOrNull();
// If it's not a World MV manages we stop.
if (mvWorld == null) {
return;
@ -122,15 +128,15 @@ public class MVPlayerListener implements InjectableListener {
}
// Get the instance of the World the player should respawn at.
MVWorld respawnWorld = null;
if (getWorldManager().isMVWorld(mvWorld.getRespawnToWorld())) {
respawnWorld = getWorldManager().getMVWorld(mvWorld.getRespawnToWorld());
LoadedMultiverseWorld respawnWorld = null;
if (getWorldManager().isLoadedWorld(mvWorld.getRespawnWorld())) {
respawnWorld = getWorldManager().getLoadedWorld(mvWorld.getRespawnWorld()).getOrNull();
}
// If it's null then it either means the World doesn't exist or the value is blank, so we don't handle it.
// NOW: We'll always handle it to get more accurate spawns
if (respawnWorld != null) {
world = respawnWorld.getCBWorld();
world = respawnWorld.getBukkitWorld().getOrNull();
}
// World has been set to the appropriate world
Location respawnLocation = getMostAccurateRespawnLocation(world);
@ -141,7 +147,7 @@ public class MVPlayerListener implements InjectableListener {
}
private Location getMostAccurateRespawnLocation(World w) {
MVWorld mvw = getWorldManager().getMVWorld(w.getName());
LoadedMultiverseWorld mvw = getWorldManager().getLoadedWorld(w.getName()).getOrNull();
if (mvw != null) {
return mvw.getSpawnLocation();
}
@ -154,26 +160,33 @@ public class MVPlayerListener implements InjectableListener {
*/
@EventHandler
public void playerJoin(PlayerJoinEvent event) {
Player p = event.getPlayer();
if (!p.hasPlayedBefore()) {
Logging.finer("Player joined for the FIRST time!");
if (config.getFirstSpawnOverride()) {
Logging.fine("Moving NEW player to(firstspawnoverride): "
+ getWorldManager().getFirstSpawnWorld().getSpawnLocation());
this.sendPlayerToDefaultWorld(p);
}
Player player = event.getPlayer();
LoadedMultiverseWorld world = getWorldManager().getLoadedWorld(player.getWorld()).getOrNull();
if (world == null) {
Logging.finer("Player joined in a world that is not managed by Multiverse.");
return;
} else {
Logging.finer("Player joined AGAIN!");
if (config.getEnforceAccess() // check this only if we're enforcing access!
&& !permissionsChecker.hasWorldAccessPermission(p, getWorldManager().getFirstSpawnWorld())) {
p.sendMessage("[MV] - Sorry you can't be in this world anymore!");
this.sendPlayerToDefaultWorld(p);
}
}
Option.of(destinationsProvider.parseDestination(config.getFirstSpawnLocation()))
.peek(parsedDestination -> {
if (!player.hasPlayedBefore()) {
Logging.finer("Player joined for the FIRST time!");
if (config.getFirstSpawnOverride()) {
Logging.fine("Moving NEW player to(firstspawnoverride): %s", config.getFirstSpawnLocation());
this.sendPlayerToDefaultWorld(player, parsedDestination);
}
} else {
Logging.finer("Player joined AGAIN!");
if (worldEntryCheckerProvider.forSender(player).canAccessWorld(world).isFailure()) {
player.sendMessage("[MV] - Sorry you can't be in this world anymore!");
this.sendPlayerToDefaultWorld(player, parsedDestination);
}
}
});
// Handle the Players GameMode setting for the new world.
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
playerWorld.put(p.getName(), p.getWorld().getName());
playerWorld.put(player.getName(), player.getWorld().getName());
}
/**
@ -213,8 +226,8 @@ public class MVPlayerListener implements InjectableListener {
}
Logging.finer("Inferred sender '" + teleporter + "' from name '"
+ teleporterName + "', fetched from name '" + teleportee.getName() + "'");
MVWorld fromWorld = getWorldManager().getMVWorld(event.getFrom().getWorld().getName());
MVWorld toWorld = getWorldManager().getMVWorld(event.getTo().getWorld().getName());
LoadedMultiverseWorld fromWorld = getWorldManager().getLoadedWorld(event.getFrom().getWorld().getName()).getOrNull();
LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(event.getTo().getWorld().getName()).getOrNull();
if (toWorld == null) {
Logging.fine("Player '" + teleportee.getName() + "' is teleporting to world '"
+ event.getTo().getWorld().getName() + "' which is not managed by Multiverse-Core. No further "
@ -291,8 +304,8 @@ public class MVPlayerListener implements InjectableListener {
event.setSearchRadius(config.getCustomPortalSearchRadius());
}
MVWorld fromWorld = getWorldManager().getMVWorld(event.getFrom().getWorld().getName());
MVWorld toWorld = getWorldManager().getMVWorld(event.getTo().getWorld().getName());
LoadedMultiverseWorld fromWorld = getWorldManager().getLoadedWorld(event.getFrom().getWorld().getName()).getOrNull();
LoadedMultiverseWorld toWorld = getWorldManager().getLoadedWorld(event.getTo().getWorld().getName()).getOrNull();
if (event.getFrom().getWorld().equals(event.getTo().getWorld())) {
// The player is Portaling to the same world.
Logging.finer("Player '" + event.getPlayer().getName() + "' is portaling to the same world.");
@ -308,13 +321,13 @@ public class MVPlayerListener implements InjectableListener {
Logging.fine("Teleport result: %s", entryResult);
}
private void sendPlayerToDefaultWorld(final Player player) {
private void sendPlayerToDefaultWorld(final Player player, ParsedDestination parsedDestination) {
// Remove the player 1 tick after the login. I'm sure there's GOT to be a better way to do this...
this.server.getScheduler().scheduleSyncDelayedTask(this.plugin,
new Runnable() {
@Override
public void run() {
player.teleport(getWorldManager().getFirstSpawnWorld().getSpawnLocation());
safeTTeleporter.safelyTeleportAsync(getCommandManager().getCommandIssuer(player), player, parsedDestination);
}
}, 1L);
}
@ -322,7 +335,7 @@ public class MVPlayerListener implements InjectableListener {
// FOLLOWING 2 Methods and Private class handle Per Player GameModes.
private void handleGameModeAndFlight(Player player, World world) {
MVWorld mvWorld = getWorldManager().getMVWorld(world.getName());
LoadedMultiverseWorld mvWorld = getWorldManager().getLoadedWorld(world.getName()).getOrNull();
if (mvWorld != null) {
this.handleGameModeAndFlight(player, mvWorld);
} else {
@ -336,7 +349,7 @@ public class MVPlayerListener implements InjectableListener {
* @param player The {@link Player}.
* @param world The world the player is in.
*/
public void handleGameModeAndFlight(final Player player, final MVWorld world) {
public void handleGameModeAndFlight(final Player player, final LoadedMultiverseWorld world) {
// We perform this task one tick later to MAKE SURE that the player actually reaches the
// destination world, otherwise we'd be changing the player mode if they havent moved anywhere.
this.server.getScheduler().scheduleSyncDelayedTask(this.plugin,
@ -345,7 +358,7 @@ public class MVPlayerListener implements InjectableListener {
public void run() {
if (!permissionsChecker.hasGameModeBypassPermission(player, world)) {
// Check that the player is in the new world and they haven't been teleported elsewhere or the event cancelled.
if (player.getWorld() == world.getCBWorld()) {
if (player.getWorld() == world.getBukkitWorld().getOrNull()) {
Logging.fine("Handling gamemode for player: %s, Changing to %s", player.getName(), world.getGameMode().toString());
Logging.finest("From World: %s", player.getWorld());
Logging.finest("To World: %s", world);

View File

@ -8,17 +8,20 @@
package com.onarandombox.MultiverseCore.listeners;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.Material;
import org.bukkit.PortalType;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.world.PortalCreateEvent;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
/**
@ -27,10 +30,12 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class MVPortalListener implements InjectableListener {
private MVWorldManager worldManager;
private final MVCoreConfig config;
private final WorldManager worldManager;
@Inject
public MVPortalListener(MVWorldManager worldManager) {
public MVPortalListener(@NotNull MVCoreConfig config, @NotNull WorldManager worldManager) {
this.config = config;
this.worldManager = worldManager;
}
@ -42,7 +47,7 @@ public class MVPortalListener implements InjectableListener {
public void portalForm(PortalCreateEvent event) {
Logging.fine("Attempting to create portal at '%s' with reason: %s", event.getWorld().getName(), event.getReason());
MVWorld world = this.worldManager.getMVWorld(event.getWorld());
LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(event.getWorld()).getOrNull();
if (world == null) {
Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PortalCreateEvent.", event.getWorld().getName());
return;
@ -75,7 +80,7 @@ public class MVPortalListener implements InjectableListener {
return;
}
if (!world.getAllowedPortals().isPortalAllowed(targetType)) {
if (!world.getPortalForm().isPortalAllowed(targetType)) {
Logging.fine("Cancelling creation of %s portal because portalForm disallows.", targetType);
event.setCancelled(true);
}
@ -98,16 +103,30 @@ public class MVPortalListener implements InjectableListener {
return;
}
MVWorld world = this.worldManager.getMVWorld(event.getPlayer().getWorld());
LoadedMultiverseWorld world = this.worldManager.getLoadedWorld(event.getPlayer().getWorld()).getOrNull();
if (world == null) {
Logging.fine("World '%s' is not managed by Multiverse! Ignoring at PlayerInteractEvent.",
event.getPlayer().getWorld().getName());
return;
}
if (!world.getAllowedPortals().isPortalAllowed(PortalType.ENDER)) {
if (!world.getPortalForm().isPortalAllowed(PortalType.ENDER)) {
Logging.fine("Cancelling creation of ENDER portal because portalForm disallows.");
event.setCancelled(true);
}
}
/**
* Handles portal search radius adjustment.
* @param event The Event that was fired.
*/
@EventHandler
public void entityPortal(EntityPortalEvent event) {
if (event.isCancelled() || event.getTo() == null) {
return;
}
if (!config.isUsingCustomPortalSearch()) {
event.setSearchRadius(config.getCustomPortalSearchRadius());
}
}
}

View File

@ -7,9 +7,9 @@
package com.onarandombox.MultiverseCore.listeners;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.inject.Inject;
import org.bukkit.event.EventHandler;
import org.bukkit.event.weather.ThunderChangeEvent;
@ -22,10 +22,10 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class MVWeatherListener implements InjectableListener {
private MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public MVWeatherListener(MVWorldManager worldManager) {
public MVWeatherListener(WorldManager worldManager) {
this.worldManager = worldManager;
}
@ -35,14 +35,16 @@ public class MVWeatherListener implements InjectableListener {
*/
@EventHandler
public void weatherChange(WeatherChangeEvent event) {
if (event.isCancelled()) {
if (event.isCancelled() || !event.toWeatherState()) {
return;
}
MVWorld world = this.worldManager.getMVWorld(event.getWorld().getName());
if (world != null) {
// If it's going to start raining and we have weather disabled
event.setCancelled((event.toWeatherState() && !world.isWeatherEnabled()));
}
worldManager.getLoadedWorld(event.getWorld())
.peek((world) -> {
if (!world.getAllowWeather()) {
Logging.fine("Cancelling weather for %s as getAllowWeather is false", world.getName());
event.setCancelled(true);
}
});
}
/**
@ -51,13 +53,15 @@ public class MVWeatherListener implements InjectableListener {
*/
@EventHandler
public void thunderChange(ThunderChangeEvent event) {
if (event.isCancelled()) {
if (event.isCancelled() || !event.toThunderState()) {
return;
}
MVWorld world = this.worldManager.getMVWorld(event.getWorld().getName());
if (world != null) {
// If it's going to start raining and we have weather disabled
event.setCancelled((event.toThunderState() && !world.isWeatherEnabled()));
}
worldManager.getLoadedWorld(event.getWorld())
.peek((world) -> {
if (!world.getAllowWeather()) {
Logging.fine("Cancelling thunder for %s as getAllowWeather is false", world.getName());
event.setCancelled(true);
}
});
}
}

View File

@ -1,33 +0,0 @@
/******************************************************************************
* Multiverse 2 Copyright (c) the Multiverse Team 2011. *
* Multiverse 2 is licensed under the BSD License. *
* For more information please check the README.md file included *
* with this project. *
******************************************************************************/
package com.onarandombox.MultiverseCore.listeners;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import jakarta.inject.Inject;
import org.bukkit.event.EventHandler;
import org.bukkit.event.world.WorldInitEvent;
import org.jvnet.hk2.annotations.Service;
@Service
public class MVWorldInitListener implements InjectableListener {
private final MVWorldManager worldManager;
@Inject
public MVWorldInitListener(MVWorldManager worldManager) {
this.worldManager = worldManager;
}
@EventHandler
public void initWorld(WorldInitEvent event) {
if (!worldManager.isKeepingSpawnInMemory(event.getWorld())) {
event.getWorld().setKeepSpawnInMemory(false);
}
}
}

View File

@ -7,12 +7,15 @@
package com.onarandombox.MultiverseCore.listeners;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import com.onarandombox.MultiverseCore.worldnew.options.UnloadWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.reasons.LoadFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.UnloadFailureReason;
import jakarta.inject.Inject;
import org.bukkit.World;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.world.WorldLoadEvent;
import org.bukkit.event.world.WorldUnloadEvent;
import org.jvnet.hk2.annotations.Service;
@ -23,47 +26,46 @@ import org.jvnet.hk2.annotations.Service;
@Service
public class MVWorldListener implements InjectableListener {
private MVWorldManager worldManager;
private final WorldManager worldManager;
@Inject
public MVWorldListener(MVWorldManager worldManager) {
public MVWorldListener(WorldManager worldManager) {
this.worldManager = worldManager;
}
/**
* This method is called when Bukkit fires off a WorldUnloadEvent.
*
* @param event The Event that was fired.
*/
@EventHandler
@EventHandler(priority = EventPriority.MONITOR)
public void unloadWorld(WorldUnloadEvent event) {
if (event.isCancelled()) {
return;
}
if (event.getWorld() instanceof World) {
World world = (World) event.getWorld();
if (world != null) {
this.worldManager.unloadWorld(world.getName(), false);
}
}
worldManager.getLoadedWorld(event.getWorld().getName())
.peek(world -> worldManager.unloadWorld(UnloadWorldOptions.world(world)).onFailure(failure -> {
if (failure.getFailureReason() != UnloadFailureReason.WORLD_ALREADY_UNLOADING) {
Logging.severe("Failed to unload world: " + failure);
}
}));
}
/**
* This method is called when Bukkit fires off a WorldLoadEvent.
*
* @param event The Event that was fired.
*/
@EventHandler
@EventHandler(priority = EventPriority.MONITOR)
public void loadWorld(WorldLoadEvent event) {
World world = event.getWorld();
if (world != null) {
if (this.worldManager.getUnloadedWorlds().contains(world.getName())) {
this.worldManager.loadWorld(world.getName());
}
MVWorld mvWorld = worldManager.getMVWorld(world);
if (mvWorld != null) {
// This is where we can temporarily fix those pesky property issues!
world.setPVP(mvWorld.isPVPEnabled());
world.setDifficulty(mvWorld.getDifficulty());
}
}
worldManager.getUnloadedWorld(event.getWorld().getName())
.peek(world -> {
Logging.fine("Loading world: " + world.getName());
worldManager.loadWorld(world).onFailure(failure -> {
if (failure.getFailureReason() != LoadFailureReason.WORLD_ALREADY_LOADING) {
Logging.severe("Failed to load world: " + failure);
}
});
});
}
}

View File

@ -1,26 +1,37 @@
package com.onarandombox.MultiverseCore.permissions;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.MultiverseWorld;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
@Service
public class CorePermissionsChecker {
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MultiverseWorld world) {
return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName()));
}
public boolean hasWorldExemptPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
@Deprecated // TODO: Remove old MVWorld
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull com.onarandombox.MultiverseCore.api.MVWorld world) {
return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName()));
}
public boolean hasWorldExemptPermission(@NotNull CommandSender sender, @NotNull LoadedMultiverseWorld world) {
return hasPermission(sender, concatPermission(CorePermissions.WORLD_EXEMPT, world.getName()));
}
public boolean hasPlayerLimitBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
public boolean hasPlayerLimitBypassPermission(@NotNull CommandSender sender, @NotNull LoadedMultiverseWorld world) {
return hasPermission(sender, concatPermission(CorePermissions.PLAYERLIMIT_BYPASS, world.getName()));
}
public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull LoadedMultiverseWorld world) {
return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName()));
}
@Deprecated // TODO: Remove old MVWorld
public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull com.onarandombox.MultiverseCore.api.MVWorld world) {
return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName()));
}

View File

@ -1,15 +1,15 @@
package com.onarandombox.MultiverseCore.placeholders;
import java.util.Optional;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import io.vavr.control.Option;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -19,11 +19,11 @@ import org.jvnet.hk2.annotations.Service;
public class MultiverseCorePlaceholders extends PlaceholderExpansion {
private final MultiverseCore plugin;
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final MVEconomist economist;
@Inject
public MultiverseCorePlaceholders(MultiverseCore plugin, MVWorldManager worldManager, MVEconomist economist) {
public MultiverseCorePlaceholders(MultiverseCore plugin, WorldManager worldManager, MVEconomist economist) {
this.plugin = plugin;
this.worldManager = worldManager;
this.economist = economist;
@ -74,31 +74,30 @@ public class MultiverseCorePlaceholders extends PlaceholderExpansion {
}
final var placeholder = paramsArray[0];
Optional<MVWorld> targetWorld;
Option<LoadedMultiverseWorld> targetWorld;
// If no world is defined, use the player's world
if (paramsArray.length == 1) {
if (!offlinePlayer.isOnline()) {
return null;
}
targetWorld = Optional.ofNullable(worldManager.getMVWorld(((Player)offlinePlayer).getWorld()));
targetWorld = worldManager.getLoadedWorld(((Player)offlinePlayer).getWorld());
} else {
targetWorld = Optional.ofNullable(worldManager.getMVWorld(paramsArray[1]));
targetWorld = worldManager.getLoadedWorld(paramsArray[1]);
}
// Fail if world is null
return targetWorld.map(world -> getWorldPlaceHolderValue(placeholder, world))
.orElse(null);
return targetWorld.map(world -> getWorldPlaceHolderValue(placeholder, world)).getOrNull();
}
private @Nullable String getWorldPlaceHolderValue(@NotNull String placeholder, @NotNull MVWorld world) {
private @Nullable String getWorldPlaceHolderValue(@NotNull String placeholder, @NotNull LoadedMultiverseWorld world) {
// Switch to find what specific placeholder we want
switch (placeholder.toLowerCase()) {
case "alias" -> {
return world.getColoredWorldString();
return world.getAlias();
}
case "animalspawn" -> {
return String.valueOf(world.canAnimalsSpawn());
return String.valueOf(world.getSpawningAnimals());
}
case "autoheal" -> {
return String.valueOf(world.getAutoHeal());
@ -131,7 +130,7 @@ public class MultiverseCorePlaceholders extends PlaceholderExpansion {
return String.valueOf(world.getHunger());
}
case "monstersspawn" -> {
return String.valueOf(world.canMonstersSpawn());
return String.valueOf(world.getSpawningMonsters());
}
case "name" -> {
return world.getName();
@ -143,19 +142,20 @@ public class MultiverseCorePlaceholders extends PlaceholderExpansion {
return String.valueOf(world.getPrice());
}
case "pvp" -> {
return String.valueOf(world.isPVPEnabled());
return String.valueOf(world.getPvp());
}
case "seed" -> {
return String.valueOf(world.getSeed());
}
case "time" -> {
return world.getTime();
}
// TODO: Time is removed, not sure if it's worth adding back
// case "time" -> {
// return world.getTime();
// }
case "type" -> {
return world.getWorldType().toString().toLowerCase();
return world.getBukkitWorld().map(World::getWorldType).map(Enum::name).getOrElse("null");
}
case "weather" -> {
return String.valueOf(world.isWeatherEnabled());
return String.valueOf(world.getAllowWeather());
}
default -> {
warning("Unknown placeholder: " + placeholder);

View File

@ -7,6 +7,8 @@ import com.onarandombox.MultiverseCore.utils.message.MessageReplacement;
import org.jetbrains.annotations.NotNull;
public enum MVCorei18n implements MessageKeyProvider {
// BEGIN CHECKSTYLE-SUPPRESSION: Javadoc
// config status
CONFIG_SAVE_FAILED,
CONFIG_NODE_NOTFOUND,
@ -16,7 +18,6 @@ public enum MVCorei18n implements MessageKeyProvider {
// clone command
CLONE_CLONING,
CLONE_FAILED,
CLONE_SUCCESS,
// Coordinates command
@ -36,13 +37,10 @@ public enum MVCorei18n implements MessageKeyProvider {
CREATE_PROPERTIES_GENERATOR,
CREATE_PROPERTIES_STRUCTURES,
CREATE_LOADING,
CREATE_FAILED,
CREATE_SUCCESS,
// delete command
DELETE_DELETING,
DELETE_FAILED,
DELETE_SUCCESS,
DELETE_PROMPT,
// Dumps command
@ -62,28 +60,21 @@ public enum MVCorei18n implements MessageKeyProvider {
// import command
IMPORT_IMPORTING,
IMPORT_FAILED,
IMPORT_SUCCESS,
// load command
LOAD_LOADING,
LOAD_FAILED,
LOAD_SUCCESS,
// regen command
REGEN_REGENERATING,
REGEN_FAILED,
REGEN_SUCCESS,
REGEN_PROMPT,
REGEN_SUCCESS,
// reload command
RELOAD_RELOADING,
RELOAD_SUCCESS,
// remove command
REMOVE_FAILED,
REMOVE_SUCCESS,
// root MV command
ROOT_TITLE,
ROOT_HELP,
@ -93,7 +84,6 @@ public enum MVCorei18n implements MessageKeyProvider {
// unload command
UNLOAD_UNLOADING,
UNLOAD_FAILURE,
UNLOAD_SUCCESS,
// debug command
@ -111,11 +101,50 @@ public enum MVCorei18n implements MessageKeyProvider {
ENTRYCHECK_EXCEEDPLAYERLIMIT,
ENTRYCHECK_NOWORLDACCESS,
// world manager result
CLONEWORLD_INVALIDWORLDNAME,
CLONEWORLD_WORLDEXISTFOLDER,
CLONEWORLD_WORLDEXISTUNLOADED,
CLONEWORLD_WORLDEXISTLOADED,
CLONEWORLD_COPYFAILED,
CREATEWORLD_INVALIDWORLDNAME,
CREATEWORLD_WORLDEXISTFOLDER,
CREATEWORLD_WORLDEXISTUNLOADED,
CREATEWORLD_WORLDEXISTLOADED,
CREATEWORLD_BUKKITCREATIONFAILED,
DELETE_SUCCESS,
DELETEWORLD_WORLDNONEXISTENT,
DELETEWORLD_LOADFAILED,
DELETEWORLD_WORLDFOLDERNOTFOUND,
DELETEWORLD_FAILEDTODELETEFOLDER,
IMPORTWORLD_INVALIDWORLDNAME,
IMPORTWORLD_WORLDFOLDERINVALID,
IMPORTWORLD_WORLDEXISTUNLOADED,
IMPORTWORLD_WORLDEXISTLOADED,
IMPORTWORLD_BUKKITCREATIONFAILED,
LOADWORLD_WORLDALREADYLOADING,
LOADWORLD_WORLDNONEXISTENT,
LOADWORLD_WORLDEXISTFOLDER,
LOADWORLD_WORLDEXISTLOADED,
LOADWORLD_BUKKITCREATIONFAILED,
REMOVEWORLD_REMOVED,
REMOVEWORLD_WORLDNONEXISTENT,
UNLOADWORLD_WORLDALREADYUNLOADING,
UNLOADWORLD_WORLDNONEXISTENT,
UNLOADWORLD_WORLDUNLOADED,
UNLOADWORLD_BUKKITUNLOADFAILED,
// generic
GENERIC_SUCCESS,
GENERIC_FAILURE
GENERIC_FAILURE;
;
// END CHECKSTYLE-SUPPRESSION: Javadoc
private final MessageKey key = MessageKey.of("mv-core." + this.name().replace('_', '.').toLowerCase());

View File

@ -1,13 +1,9 @@
package com.onarandombox.MultiverseCore.utils.metrics;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.WorldManager;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import org.apache.commons.lang.WordUtils;
@ -15,29 +11,24 @@ import org.bstats.bukkit.Metrics;
import org.bukkit.World;
import org.jvnet.hk2.annotations.Service;
import java.util.Map;
import java.util.function.Consumer;
@Service
public class MetricsConfigurator {
private static final int PLUGIN_ID = 7765;
private static final String NO_GENERATOR_NAME = "N/A";
private final MVWorldManager worldManager;
private final WorldManager worldManager;
private final Metrics metrics;
@Inject
private MetricsConfigurator(MultiverseCore plugin, MVWorldManager worldManager) {
private MetricsConfigurator(MultiverseCore plugin, WorldManager worldManager) {
this.worldManager = worldManager;
this.metrics = new Metrics(plugin, PLUGIN_ID);
}
private MVWorldManager getWorldManager() {
return worldManager;
}
private Collection<MVWorld> getMVWorlds() {
return getWorldManager().getMVWorlds();
}
@PostConstruct
private void initMetrics() {
try {
@ -54,20 +45,20 @@ public class MetricsConfigurator {
private void addCustomGeneratorsMetric() {
addAdvancedPieMetric("custom_generators", map -> {
for (MVWorld w : getMVWorlds()) {
for (LoadedMultiverseWorld w : worldManager.getLoadedWorlds()) {
MetricsHelper.incrementCount(map, getGeneratorName(w));
}
});
}
private String getGeneratorName(MVWorld world) {
private String getGeneratorName(LoadedMultiverseWorld world) {
String gen = world.getGenerator();
return (gen != null && !gen.equalsIgnoreCase("null")) ? gen.split(":")[0] : NO_GENERATOR_NAME;
}
private void addEnvironmentsMetric() {
addAdvancedPieMetric("environments", map -> {
for (MVWorld w : getMVWorlds()) {
for (LoadedMultiverseWorld w : worldManager.getLoadedWorlds()) {
MetricsHelper.incrementCount(map, titleCaseEnv(w.getEnvironment()));
}
});
@ -80,9 +71,8 @@ public class MetricsConfigurator {
private void addWorldCountMetric() {
addMultiLineMetric("world_count", map -> {
int loadedWorldsCount = getMVWorlds().size();
map.put("Loaded worlds", loadedWorldsCount);
map.put("Total number of worlds", loadedWorldsCount + getWorldManager().getUnloadedWorlds().size());
map.put("Loaded worlds", worldManager.getLoadedWorlds().size());
map.put("Total number of worlds", worldManager.getWorlds().size());
});
}

View File

@ -0,0 +1,338 @@
package com.onarandombox.MultiverseCore.utils.result;
import com.onarandombox.MultiverseCore.utils.message.Message;
import com.onarandombox.MultiverseCore.utils.message.MessageReplacement;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Represents an attempt to process a value that can fail with a reason that has a localized message.
*
* @param <T> The type of the value.
* @param <F> The type of failure reason.
*/
public sealed interface Attempt<T, F extends FailureReason> permits Attempt.Success, Attempt.Failure {
/**
* Creates a new success attempt.
*
* @param value The value.
* @param <T> The type of the value.
* @param <F> The type of failure reason.
* @return The new success attempt.
*/
static <T, F extends FailureReason> Attempt<T, F> success(T value) {
return new Success<>(value);
}
/**
* Creates a new failure attempt.
*
* @param failureReason The reason for failure.
* @param messageReplacements The replacements for the failure message.
* @param <T> The type of the value.
* @param <F> The type of failure reason.
* @return The new failure attempt.
*/
static <T, F extends FailureReason> Attempt<T, F> failure(
F failureReason, MessageReplacement... messageReplacements) {
return new Failure<>(failureReason, Message.of(failureReason, "Failed!", messageReplacements));
}
/**
* Creates a new failure attempt with a custom message.
*
* @param failureReason The reason for failure.
* @param message The custom message for failure. This will override the default message.
* @param <T> The type of the value.
* @param <F> The type of failure reason.
* @return The new failure attempt.
*/
static <T, F extends FailureReason> Attempt<T, F> failure(F failureReason, Message message) {
return new Failure<>(failureReason, message);
}
/**
* Gets the value of this attempt. Exceptions will be thrown if this is a failure attempt.
*
* @return The value.
*/
T get();
/**
* Gets the reason for failure. Exceptions will be thrown if this is a success attempt.
*
* @return The reason for failure.
*/
F getFailureReason();
/**
* Gets the message for failure. Exceptions will be thrown if this is a success attempt.
*
* @return The message for failure.
*/
Message getFailureMessage();
/**
* Returns whether this attempt is a success.
*
* @return Whether this attempt is a success.
*/
default boolean isSuccess() {
return this instanceof Success;
}
/**
* Returns whether this attempt is a failure.
*
* @return Whether this attempt is a failure.
*/
default boolean isFailure() {
return this instanceof Failure;
}
/**
* Peeks at the value if this is a success attempt.
*
* @param consumer The consumer with the value.
* @return This attempt.
*/
default Attempt<T, F> peek(Consumer<T> consumer) {
if (this instanceof Success) {
consumer.accept(get());
}
return this;
}
/**
* Maps the value to another value if this is a success attempt.
*
* @param mapper The mapper.
* @param <U> The type of the new value.
* @return The new attempt.
*/
default <U> Attempt<U, F> map(Function<? super T, ? extends U> mapper) {
if (this instanceof Success) {
return new Success<>(mapper.apply(get()));
} else {
return new Failure<>(getFailureReason(), getFailureMessage());
}
}
/**
* Maps the value to another attempt if this is a success attempt.
*
* @param mapper The mapper.
* @param <U> The type of the new value.
* @return The new attempt.
*/
default <U> Attempt<U, F> map(Supplier<? extends U> mapper) {
if (this instanceof Success) {
return new Success<>(mapper.get());
} else {
return new Failure<>(getFailureReason(), getFailureMessage());
}
}
/**
* Maps the value to another attempt with the same fail reason if this is a success attempt.
*
* @param mapper The mapper.
* @param <U> The type of the new value.
* @return The new attempt.
*/
default <U> Attempt<U, F> mapAttempt(Function<? super T, Attempt<U, F>> mapper) {
if (this instanceof Success) {
return mapper.apply(get());
} else {
return new Failure<>(getFailureReason(), getFailureMessage());
}
}
/**
* Maps the value to another attempt with the same fail reason if this is a success attempt.
*
* @param mapper The mapper.
* @param <U> The type of the new value.
* @return The new attempt.
*/
default <U> Attempt<U, F> mapAttempt(Supplier<Attempt<U, F>> mapper) {
if (this instanceof Success) {
return mapper.get();
} else {
return new Failure<>(getFailureReason(), getFailureMessage());
}
}
/**
* Maps to another attempt with a different fail reason.
*
* @param failureReason The new fail reason.
* @param <UF> The type of the new fail reason.
* @return The new attempt.
*/
default <UF extends FailureReason> Attempt<T, UF> transform(UF failureReason) {
if (this instanceof Success) {
return new Success<>(get());
} else {
return new Failure<>(failureReason, getFailureMessage());
}
}
/**
* Calls either the failure or success function depending on the result type.
*
* @param failureMapper The failure function.
* @param successMapper The success function.
* @param <N> The type of the new value.
* @return The result of the function.
*/
default <N> N fold(Function<Failure<T, F>, N> failureMapper, Function<T, N> successMapper) {
if (this instanceof Success) {
return successMapper.apply(get());
} else {
return failureMapper.apply((Failure<T, F>) this);
}
}
/**
* Calls the given runnable if this is a success attempt.
*
* @param runnable The runnable.
* @return This attempt.
*/
default Attempt<T, F> onSuccess(Runnable runnable) {
if (this instanceof Success) {
runnable.run();
}
return this;
}
/**
* Calls the given consumer if this is a success attempt.
*
* @param consumer The consumer with the value.
* @return This attempt.
*/
default Attempt<T, F> onSuccess(Consumer<T> consumer) {
if (this instanceof Success) {
consumer.accept(get());
}
return this;
}
/**
* Calls the given consumer if this is a failure attempt.
*
* @param runnable The runnable.
* @return This attempt.
*/
default Attempt<T, F> onFailure(Runnable runnable) {
if (this instanceof Failure) {
runnable.run();
}
return this;
}
/**
* Calls the given consumer if this is a failure attempt.
*
* @param consumer The consumer with the failure instance.
* @return This attempt.
*/
default Attempt<T, F> onFailure(Consumer<Failure<T, F>> consumer) {
if (this instanceof Failure) {
consumer.accept((Failure<T, F>) this);
}
return this;
}
/**
* Calls the given runnable if this is a failure attempt.
*
* @param consumer The consumer with the failure reason.
* @return This attempt.
*/
default Attempt<T, F> onFailureReason(Consumer<F> consumer) {
if (this instanceof Failure) {
consumer.accept(getFailureReason());
}
return this;
}
/**
* Represents a successful attempt with a value.
*
* @param <T> The type of the value.
* @param <F> The type of failure reason.
*/
final class Success<T, F extends FailureReason> implements Attempt<T, F> {
private final T value;
Success(T value) {
this.value = value;
}
@Override
public T get() {
return value;
}
@Override
public F getFailureReason() {
throw new UnsupportedOperationException("No failure reason as attempt is a success");
}
@Override
public Message getFailureMessage() {
throw new UnsupportedOperationException("No failure message as attempt is a success");
}
@Override
public String toString() {
return "Success{"
+ "value=" + value
+ '}';
}
}
/**
* Represents a failed attempt with a reason.
*
* @param <T> The type of the value.
* @param <F> The type of failure reason.
*/
final class Failure<T, F extends FailureReason> implements Attempt<T, F> {
private final F failureReason;
private final Message message;
Failure(F failureReason, Message message) {
this.failureReason = failureReason;
this.message = message;
}
@Override
public T get() {
throw new UnsupportedOperationException("No value as attempt is a failure");
}
@Override
public F getFailureReason() {
return failureReason;
}
@Override
public Message getFailureMessage() {
return message;
}
@Override
public String toString() {
return "Failure{"
+ "reason=" + failureReason
+ '}';
}
}
}

View File

@ -6,40 +6,139 @@ import org.jetbrains.annotations.NotNull;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Function;
public sealed interface Result<S extends SuccessReason, F extends FailureReason> permits Result.Success, Result.Failure {
static <F extends FailureReason, S extends SuccessReason> Result<S, F> success(S successReason, MessageReplacement...replacements) {
/**
* Represents result of an operation with a reason for success or failure that has localized messages.
*
* @param <S> The type of success reason.
* @param <F> The type of failure reason.
*/
public sealed interface Result<S extends SuccessReason, F extends FailureReason>
permits Result.Success, Result.Failure {
/**
* Creates a new success result.
*
* @param successReason The reason for success.
* @param replacements The replacements for the success message.
* @param <F> The type of failure reason.
* @param <S> The type of success reason.
* @return The new success result.
*/
static <F extends FailureReason, S extends SuccessReason> Result<S, F> success(
S successReason, MessageReplacement... replacements) {
return new Success<>(successReason, replacements);
}
static <F extends FailureReason, S extends SuccessReason> Result<S, F> failure(F failureReason, MessageReplacement...replacements) {
/**
* Creates a new success result.
*
* @param successReason The reason for success.
* @param message The custom message for success. This will override the default message.
* @param <F> The type of failure reason.
* @param <S> The type of success reason.
* @return The new success result.
*/
static <F extends FailureReason, S extends SuccessReason> Result<S, F> success(S successReason, Message message) {
return new Success<>(successReason, message);
}
/**
* Creates a new failure result.
*
* @param failureReason The reason for failure.
* @param replacements The replacements for the failure message.
* @param <F> The type of failure reason.
* @param <S> The type of success reason.
* @return The new failure result.
*/
static <F extends FailureReason, S extends SuccessReason> Result<S, F> failure(
F failureReason, MessageReplacement... replacements) {
return new Failure<>(failureReason, replacements);
}
/**
* Creates a new failure result.
*
* @param failureReason The reason for failure.
* @param message The custom message for failure. This will override the default message.
* @param <F> The type of failure reason.
* @param <S> The type of success reason.
* @return The new failure result.
*/
static <F extends FailureReason, S extends SuccessReason> Result<S, F> failure(F failureReason, Message message) {
return new Failure<>(failureReason, message);
}
/**
* Returns whether this result is a success.
*
* @return Whether this result is a success.
*/
boolean isSuccess();
/**
* Returns whether this result is a failure.
*
* @return Whether this result is a failure.
*/
boolean isFailure();
/**
* Returns the reason for success.
*
* @return The reason for success.
*/
S getSuccessReason();
/**
* Returns the reason for failure.
*
* @return The reason for failure.
*/
F getFailureReason();
/**
* Returns the message for the reason of this result.
*
* @return The message for the reason.
*/
@NotNull Message getReasonMessage();
default Result<S, F> onSuccess(Consumer<S> consumer) {
if (this.isSuccess()) {
consumer.accept(this.getSuccessReason());
/**
* Executes the given consumer if this result is a success.
*
* @param consumer The consumer with success object.
* @return This result.
*/
default Result<S, F> onSuccess(Consumer<Success<S, F>> consumer) {
if (this instanceof Success) {
consumer.accept((Success<S, F>) this);
}
return this;
}
default Result<S, F> onFailure(Consumer<F> consumer) {
if (this.isFailure()) {
consumer.accept(this.getFailureReason());
/**
* Executes the given consumer if this result is a failure.
*
* @param consumer The consumer with failure object.
* @return This result.
*/
default Result<S, F> onFailure(Consumer<Failure<S, F>> consumer) {
if (this instanceof Failure) {
consumer.accept((Failure<S, F>) this);
}
return this;
}
/**
* Executes the given consumer if this result is a success and the success reason matches the given reason.
*
* @param successReason The success reason to match.
* @param consumer The consumer with success reason.
* @return This result.
*/
default Result<S, F> onSuccessReason(S successReason, Consumer<S> consumer) {
if (this.isSuccess() && this.getSuccessReason() == successReason) {
consumer.accept(this.getSuccessReason());
@ -47,6 +146,13 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
return this;
}
/**
* Executes the given consumer if this result is a failure and the failure reason matches the given reason.
*
* @param failureReason The failure reason to match.
* @param consumer The consumer with failure reason.
* @return This result.
*/
default Result<S, F> onFailureReason(F failureReason, Consumer<F> consumer) {
if (this.isFailure() && this.getFailureReason() == failureReason) {
consumer.accept(this.getFailureReason());
@ -54,13 +160,67 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
return this;
}
final class Success<F extends FailureReason, S extends SuccessReason> implements Result<S, F> {
private final S successReason;
private final MessageReplacement[] replacements;
/**
* Executes the given function if this result is a success and returns the result of the function.
*
* @param function The function with success object.
* @return The result of the function.
*/
default Result<S, F> onSuccessThen(Function<Success<S, F>, Result<S, F>> function) {
if (this instanceof Success) {
return function.apply((Success<S, F>) this);
}
return this;
}
public Success(S successReason, MessageReplacement[] replacements) {
/**
* Executes the given function if this result is a failure and returns the result of the function.
*
* @param function The function with failure object.
* @return The result of the function.
*/
default Result<S, F> onFailureThen(Function<Failure<S, F>, Result<S, F>> function) {
if (this instanceof Failure) {
return function.apply((Failure<S, F>) this);
}
return this;
}
/**
* Executes either the failure or success function depending on the result type.
*
* @param failureFunc The function with success reason.
* @param successFunc The function with success reason.
* @param <R> The type of the result.
* @return The result of the function.
*/
default <R> R fold(Function<Failure<S, F>, R> failureFunc, Function<Success<S, F>, R> successFunc) {
if (this instanceof Failure) {
return failureFunc.apply((Failure<S, F>) this);
} else if (this instanceof Success) {
return successFunc.apply((Success<S, F>) this);
}
throw new IllegalStateException("Unknown result type: " + this.getClass().getName());
}
/**
* The class for a successful result.
*
* @param <F> The type of failure reason.
* @param <S> The type of success reason.
*/
final class Success<S extends SuccessReason, F extends FailureReason> implements Result<S, F> {
private final S successReason;
private final Message message;
Success(S successReason, Message message) {
this.successReason = successReason;
this.replacements = replacements;
this.message = message;
}
Success(S successReason, MessageReplacement[] replacements) {
this.successReason = successReason;
this.message = Message.of(successReason, "Success!", replacements);
}
@Override
@ -80,29 +240,40 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
@Override
public F getFailureReason() {
throw new NoSuchElementException("No reason for failure");
throw new UnsupportedOperationException("No reason for success");
}
@Override
public @NotNull Message getReasonMessage() {
return Message.of(successReason, "Success!", replacements);
return message;
}
@Override
public String toString() {
return "Success{" +
"reason=" + successReason +
'}';
return "Success{"
+ "reason=" + successReason
+ '}';
}
}
/**
* The class for a failed result.
*
* @param <S> The type of success reason.
* @param <F> The type of failure reason.
*/
final class Failure<S extends SuccessReason, F extends FailureReason> implements Result<S, F> {
private final F failureReason;
private final MessageReplacement[] replacements;
private final Message message;
public Failure(F failureReason, MessageReplacement[] replacements) {
Failure(F failureReason, Message message) {
this.failureReason = failureReason;
this.replacements = replacements;
this.message = message;
}
Failure(F failureReason, MessageReplacement[] replacements) {
this.failureReason = failureReason;
this.message = Message.of(failureReason, "Failed!", replacements);
}
@Override
@ -117,7 +288,7 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
@Override
public S getSuccessReason() {
throw new NoSuchElementException("No reason for success");
throw new UnsupportedOperationException("No reason for failure");
}
@Override
@ -127,14 +298,14 @@ public sealed interface Result<S extends SuccessReason, F extends FailureReason>
@Override
public @NotNull Message getReasonMessage() {
return Message.of(failureReason, "Success!", replacements);
return message;
}
@Override
public String toString() {
return "Failure{" +
"reason=" + failureReason +
'}';
return "Failure{"
+ "reason=" + failureReason
+ '}';
}
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.utils.result;

View File

@ -24,7 +24,7 @@ import com.onarandombox.MultiverseCore.exceptions.PropertyDoesNotExistException;
import com.onarandombox.MultiverseCore.listeners.MVPlayerListener;
import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType;
import com.onarandombox.MultiverseCore.world.configuration.EnglishChatColor;
import com.onarandombox.MultiverseCore.world.configuration.SpawnLocation;
import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation;
import com.onarandombox.MultiverseCore.world.configuration.SpawnSettings;
import com.onarandombox.MultiverseCore.world.configuration.WorldPropertyValidator;
import me.main__.util.SerializationConfig.ChangeDeniedException;
@ -342,7 +342,7 @@ public class SimpleMVWorld implements MVWorld {
for (Player p : server.getWorld(getName()).getPlayers()) {
Logging.finer(String.format("Setting %s's GameMode to %s",
p.getName(), newValue.toString()));
playerListener.handleGameModeAndFlight(p, SimpleMVWorld.this);
//playerListener.handleGameModeAndFlight(p, SimpleMVWorld.this);
}
return super.validateChange(property, newValue, oldValue, object);
}

View File

@ -9,7 +9,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.onarandombox.MultiverseCore.world.configuration.EntryFee;
import com.onarandombox.MultiverseCore.world.configuration.SpawnLocation;
import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation;
import com.onarandombox.MultiverseCore.world.configuration.SpawnSettings;
import com.onarandombox.MultiverseCore.world.configuration.WorldPropertyValidator;
import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType;

View File

@ -0,0 +1,167 @@
package com.onarandombox.MultiverseCore.worldnew;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.api.BlockSafety;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
import com.onarandombox.MultiverseCore.worldnew.config.NullLocation;
import com.onarandombox.MultiverseCore.worldnew.config.SpawnLocation;
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig;
import io.vavr.control.Option;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.UUID;
/**
* Extension of {@link MultiverseWorld} that represents a world that is currently loaded with bukkit world object.
*/
public class LoadedMultiverseWorld extends MultiverseWorld {
private static final int SPAWN_LOCATION_SEARCH_TOLERANCE = 16;
private static final int SPAWN_LOCATION_SEARCH_RADIUS = 16;
private final UUID worldUid;
private final BlockSafety blockSafety;
private final SafeTTeleporter safetyTeleporter;
private final LocationManipulation locationManipulation;
LoadedMultiverseWorld(
@NotNull World world,
@NotNull WorldConfig worldConfig,
@NotNull BlockSafety blockSafety,
@NotNull SafeTTeleporter safetyTeleporter,
@NotNull LocationManipulation locationManipulation) {
super(world.getName(), worldConfig);
this.worldUid = world.getUID();
this.blockSafety = blockSafety;
this.safetyTeleporter = safetyTeleporter;
this.locationManipulation = locationManipulation;
setupWorldConfig(world);
setupSpawnLocation(world);
}
private void setupWorldConfig(World world) {
worldConfig.setMVWorld(this);
worldConfig.load();
worldConfig.setEnvironment(world.getEnvironment());
worldConfig.setSeed(world.getSeed());
}
private void setupSpawnLocation(World world) {
Location spawnLocation = worldConfig.getSpawnLocation();
if (spawnLocation == null || spawnLocation instanceof NullLocation) {
SpawnLocation newLocation = new SpawnLocation(readSpawnFromWorld(world));
worldConfig.setSpawnLocation(newLocation);
}
}
private Location readSpawnFromWorld(World world) {
Location location = world.getSpawnLocation();
// Verify that location was safe
if (blockSafety.playerCanSpawnHereSafely(location)) {
return location;
}
if (!this.getAdjustSpawn()) {
Logging.fine("Spawn location from world.dat file was unsafe!!");
Logging.fine("NOT adjusting spawn for '" + this.getAlias() + "' because you told me not to.");
Logging.fine("To turn on spawn adjustment for this world simply type:");
Logging.fine("/mvm set adjustspawn true " + this.getAlias());
return location;
}
// The location is not safe, so we need to find a better one.
Logging.warning("Spawn location from world.dat file was unsafe. Adjusting...");
Logging.warning("Original Location: " + locationManipulation.strCoordsRaw(location));
Location newSpawn = safetyTeleporter.getSafeLocation(location,
SPAWN_LOCATION_SEARCH_TOLERANCE, SPAWN_LOCATION_SEARCH_RADIUS);
// I think we could also do this, as I think this is what Notch does.
// Not sure how it will work in the nether...
//Location newSpawn = this.spawnLocation.getWorld().getHighestBlockAt(this.spawnLocation).getLocation();
if (newSpawn != null) {
Logging.info("New Spawn for '%s' is located at: %s",
this.getName(), locationManipulation.locationToString(newSpawn));
return newSpawn;
}
// If it's a standard end world, let's check in a better place:
Logging.fine("Checking for a safe location using top block...");
Location newerSpawn;
newerSpawn = blockSafety.getTopBlock(new Location(world, 0, 0, 0));
if (newerSpawn != null) {
Logging.info("New Spawn for '%s' is located at: %s",
this.getName(), locationManipulation.locationToString(newerSpawn));
return newerSpawn;
}
Logging.severe("Safe spawn NOT found!!!");
return location;
}
/**
* Gets the Bukkit world object that this world describes.
*
* @return Bukkit world object.
*/
public Option<World> getBukkitWorld() {
return Option.of(Bukkit.getWorld(worldUid));
}
/**
* Gets the type of this world.
*
* @return Type of this world.
*/
public Option<WorldType> getWorldType() {
//noinspection deprecation
return getBukkitWorld().map(World::getWorldType);
}
/**
* Gets whether or not structures are being generated.
*
* @return True if structures are being generated.
*/
public Option<Boolean> canGenerateStructures() {
return getBukkitWorld().map(World::canGenerateStructures);
}
/**
* Get a list of all players in this World.
*
* @return A list of all Players currently residing in this world
*/
public Option<List<Player>> getPlayers() {
return getBukkitWorld().map(World::getPlayers);
}
/**
* {@inheritDoc}
*/
@Override
void setWorldConfig(WorldConfig worldConfig) {
super.setWorldConfig(worldConfig);
setupWorldConfig(getBukkitWorld().get());
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "LoadedMultiverseWorld{"
+ "name='" + worldName + "', "
+ "env='" + getEnvironment() + "', "
+ "type='" + getWorldType().getOrNull() + "', "
+ "gen='" + getGenerator() + "'"
+ '}';
}
}

View File

@ -0,0 +1,672 @@
package com.onarandombox.MultiverseCore.worldnew;
import com.google.common.base.Strings;
import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType;
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig;
import io.vavr.control.Try;
import org.bukkit.Bukkit;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* Represents a world handled by Multiverse which has all the custom properties provided by Multiverse.
*/
public class MultiverseWorld {
protected final String worldName;
protected WorldConfig worldConfig;
MultiverseWorld(String worldName, WorldConfig worldConfig) {
this.worldName = worldName;
this.worldConfig = worldConfig;
}
/**
* Gets the name of this world. The name cannot be changed.
* <br/>
* Note for plugin developers: Usually {@link #getAlias()}
* is what you want to use instead of this method.
*
* @return The name of the world as a String.
*/
public String getName() {
return worldName;
}
/**
* Gets whether this world is loaded.
*
* @return True if the world is loaded, else false.
*/
public boolean isLoaded() {
return worldConfig.hasMVWorld();
}
public Collection<String> getConfigurablePropertyNames() {
return worldConfig.getConfigurablePropertyNames();
}
public Try<Object> getProperty(String name) {
return worldConfig.getProperty(name);
}
public Try<Void> setProperty(String name, Object value) {
return worldConfig.setProperty(name, value);
}
/**
* Gets whether or not Multiverse should auto-adjust the spawn for this world.
*
* @return True if Multiverse should adjust the spawn, false if not.
*/
public boolean getAdjustSpawn() {
return worldConfig.getAdjustSpawn();
}
/**
* Sets whether or not Multiverse should auto-adjust the spawn for this world.
*
* @param adjustSpawn True if multiverse should adjust the spawn, false if not.
*/
public Try<Void> setAdjustSpawn(boolean adjustSpawn) {
return worldConfig.setAdjustSpawn(adjustSpawn);
}
/**
* Gets the alias of this world.
* <br/>
* This alias allows users to have a world named "world" but show up in the list as "FernIsland"
*
* @return The alias of the world as a String.
*/
public String getAlias() {
return Strings.isNullOrEmpty(worldConfig.getAlias()) ? worldName : worldConfig.getAlias();
}
/**
* Sets the alias of the world.
*
* @param alias A string that is the new alias.
*/
public Try<Void> setAlias(String alias) {
return worldConfig.setAlias(alias);
}
/**
* Whether or not players are allowed to fly in this world.
*
* @return True if players allowed to fly in this world.
*/
public boolean getAllowFlight() {
return worldConfig.getAllowFlight();
}
/**
* Sets whether or not players are allowed to fly in this world.
*
* @param allowFlight True to allow flight in this world.
*/
public Try<Void> setAllowFlight(boolean allowFlight) {
return worldConfig.setAllowFlight(allowFlight);
}
/**
* Gets whether weather is enabled in this world.
*
* @return True if weather events will occur, false if not.
*/
public boolean getAllowWeather() {
return worldConfig.getAllowWeather();
}
/**
* Sets whether or not there will be weather events in a given world.
* If set to false, Multiverse will disable the weather in the world immediately.
*
* @param allowWeather True if weather events should occur in a world, false if not.
*/
public Try<Void> setAllowWeather(boolean allowWeather) {
return worldConfig.setAllowWeather(allowWeather);
}
/**
* Gets whether or not a world will auto-heal players if the difficulty is on peaceful.
*
* @return True if the world should heal (default), false if not.
*/
public boolean getAutoHeal() {
return worldConfig.getAutoHeal();
}
/**
* Sets whether or not a world will auto-heal players if the difficulty is on peaceful.
*
* @param autoHeal True if the world will heal.
*/
public Try<Void> setAutoHeal(boolean autoHeal) {
return worldConfig.setAutoHeal(autoHeal);
}
/**
* Gets whether or not Multiverse should auto-load this world.
*
* @return True if Multiverse should auto-load this world.
*/
public boolean getAutoLoad() {
return worldConfig.getAutoLoad();
}
/**
* Sets whether or not Multiverse should auto-load this world.
* <br/>
* True is default.
*
* @param autoLoad True if multiverse should autoload this world the spawn, false if not.
*/
public Try<Void> setAutoLoad(boolean autoLoad) {
return worldConfig.setAutoLoad(autoLoad);
}
/**
* Gets whether or not a player who dies in this world will respawn in their
* bed or follow the normal respawn pattern.
*
* @return True if players dying in this world should respawn at their bed.
*/
public boolean getBedRespawn() {
return worldConfig.getBedRespawn();
}
/**
* Sets whether or not a player who dies in this world will respawn in their
* bed or follow the normal respawn pattern.
* <br/>
* True is default.
*
* @param bedRespawn True if players dying in this world respawn at their bed.
*/
public Try<Void> setBedRespawn(boolean bedRespawn) {
return worldConfig.setBedRespawn(bedRespawn);
}
/**
* Gets the type of currency that will be used when users enter this world. A value of null indicates a non-item
* based currency is used.
*
* @return The type of currency that will be used when users enter this world.
*/
public Material getCurrency() {
return worldConfig.getEntryFeeCurrency();
}
/**
* Sets the type of item that will be required given the price is not 0.
* Use a value of null to specify a non-item based currency.
*
* @param currency The Type of currency that will be used when users enter this world.
*/
public Try<Void> setCurrency(Material currency) {
return worldConfig.setEntryFeeCurrency(currency);
}
/**
* Gets the difficulty of this world.
*
* @return The difficulty of this world.
*/
public Difficulty getDifficulty() {
return worldConfig.getDifficulty();
}
/**
* Sets the difficulty of this world and returns {@code true} on success.
* Valid string values are either an integer of difficulty(0-3) or
* the name that resides in the Bukkit enum, ex. PEACEFUL
*
* @param difficulty The new difficulty.
*/
public Try<Void> setDifficulty(Difficulty difficulty) {
return worldConfig.setDifficulty(difficulty);
}
/**
* Gets the environment of this world. You cannot change this after world creation.
*
* @return A {@link World.Environment}.
*/
public World.Environment getEnvironment() {
return worldConfig.getEnvironment();
}
/**
* Gets the GameMode of this world.
*
* @return The GameMode of this world.
*/
public GameMode getGameMode() {
return worldConfig.getGameMode();
}
/**
* Sets the game mode of this world.
*
* @param gameMode The new {@link GameMode}.
*/
public Try<Void> setGameMode(GameMode gameMode) {
return worldConfig.setGameMode(gameMode);
}
/**
* Gets the generator string of this world. You cannot change this after world creation.
*
* @return The name of the generator.
*/
public String getGenerator() {
return worldConfig.getGenerator();
}
/**
* Gets whether or not this world will display in chat, mvw and mvl regardless if a user has the
* access permissions to go to this world.
*
* @return True if the world will be hidden, false if not.
*/
public boolean isHidden() {
return worldConfig.isHidden();
}
/**
* Sets whether or not this world will display in chat, mvw and mvl regardless if a user has the
* access permissions to go to this world.
*
* @param hidden True if the world should be hidden, false if not.
*/
public Try<Void> setHidden(boolean hidden) {
return worldConfig.setHidden(hidden);
}
/**
* Gets whether or not the hunger level of players will go down in a world.
*
* @return True if it will go down, false if it will remain steady.
*/
public boolean getHunger() {
return worldConfig.getHunger();
}
/**
* Sets whether or not the hunger level of players will go down in a world.
*
* @param hunger True if hunger will go down, false to keep it at the level they entered a world with.
*/
public Try<Void> setHunger(boolean hunger) {
return worldConfig.setHunger(hunger);
}
/**
* Gets whether or not CraftBukkit is keeping the chunks for this world in memory.
*
* @return True if CraftBukkit is keeping spawn chunks in memory.
*/
public boolean getKeepSpawnInMemory() {
return worldConfig.getKeepSpawnInMemory();
}
/**
* If true, tells Craftbukkit to keep a worlds spawn chunks loaded in memory (default: true)
* If not, CraftBukkit will attempt to free memory when players have not used that world.
* This will not happen immediately.
*
* @param keepSpawnInMemory If true, CraftBukkit will keep the spawn chunks loaded in memory.
*/
public Try<Void> setKeepSpawnInMemory(boolean keepSpawnInMemory) {
return worldConfig.setKeepSpawnInMemory(keepSpawnInMemory);
}
/**
* Gets the player limit for this world after which players without an override
* permission node will not be allowed in. A value of -1 or less signifies no limit
*
* @return The player limit
*/
public int getPlayerLimit() {
return worldConfig.getPlayerLimit();
}
/**
* Sets the player limit for this world after which players without an override
* permission node will not be allowed in. A value of -1 or less signifies no limit
*
* @param playerLimit The new limit
*/
public Try<Void> setPlayerLimit(int playerLimit) {
return worldConfig.setPlayerLimit(playerLimit);
}
/**
* Gets which type(s) of portals are allowed to be constructed in this world.
*
* @return The type of portals that are allowed.
*/
public AllowedPortalType getPortalForm() {
return worldConfig.getPortalForm();
}
/**
* Sets The types of portals that are allowed in this world.
*
* @param portalForm The type of portals allowed in this world.
*/
public Try<Void> setPortalForm(AllowedPortalType portalForm) {
return worldConfig.setPortalForm(portalForm);
}
/**
* Gets the amount of currency it requires to enter this world.
*
* @return The amount it costs to enter this world.
*/
public double getPrice() {
return worldConfig.getEntryFeeAmount();
}
/**
* Sets the price for entry to this world.
* You can think of this like an amount.
* The type can be set with {@link #setCurrency(Material)}
*
* @param price The Amount of money/item to enter the world.
*/
public Try<Void> setPrice(double price) {
return worldConfig.setEntryFeeAmount(price);
}
/**
* Gets whether or not PVP is enabled in this world in some form (fake or not).
*
* @return True if players can take damage from other players.
*/
public boolean getPvp() {
return worldConfig.getPvp();
}
/**
* Turn pvp on or off. This setting is used to set the world's PVP mode.
*
* @param pvp True to enable PVP damage, false to disable it.
*/
public Try<Void> setPvp(boolean pvp) {
return worldConfig.setPvp(pvp);
}
/**
* Gets the world name players will respawn in if they die in this one.
*
* @return A world name that exists on the server.
*/
public String getRespawnWorldName() {
return worldConfig.getRespawnWorld();
}
/**
* Gets the world players will respawn in if they die in this one.
*
* @return A world that exists on the server.
*/
public @Nullable World getRespawnWorld() {
return Bukkit.getWorld(worldConfig.getRespawnWorld());
}
/**
* Sets the world players will respawn in if they die in this one.
* Returns true upon success, false upon failure.
*
* @param respawnWorld The name of a world that exists on the server.
*/
public Try<Void> setRespawnWorld(World respawnWorld) {
return worldConfig.setRespawnWorld(respawnWorld.getName());
}
/**
* Sets the world players will respawn in if they die in this one.
* Returns true upon success, false upon failure.
*
* @param respawnWorld The name of a world that exists on the server.
*/
public Try<Void> setRespawnWorld(MultiverseWorld respawnWorld) {
return worldConfig.setRespawnWorld(respawnWorld.getName());
}
/**
* Sets the world players will respawn in if they die in this one.
* Returns true upon success, false upon failure.
*
* @param respawnWorld The name of a world that exists on the server.
*/
public Try<Void> setRespawnWorld(String respawnWorld) {
return worldConfig.setRespawnWorld(respawnWorld);
}
/**
* Gets the scaling value of this world.Really only has an effect if you use
* Multiverse-NetherPortals.
*
* @return This world's non-negative, non-zero scale.
*/
public double getScale() {
return worldConfig.getScale();
}
/**
* Sets the scale of this world. Really only has an effect if you use
* Multiverse-NetherPortals. TODO: we are removing mvnp.
*
* @param scale A scaling value, cannot be negative or 0.
*/
public Try<Void> setScale(double scale) {
return worldConfig.setScale(scale);
}
/**
* Gets the world seed of this world. This cannot be changed after world creation.
*
* @return The Long version of the seed.
*/
public long getSeed() {
return worldConfig.getSeed();
}
/**
* Gets the spawn location of this world.
*
* @return The spawn location of this world.
*/
public Location getSpawnLocation() {
return worldConfig.getSpawnLocation();
}
/**
* Sets the spawn location for a world.
*
* @param spawnLocation The spawn location for a world.
*/
public Try<Void> setSpawnLocation(Location spawnLocation) {
return worldConfig.setSpawnLocation(spawnLocation);
}
/**
* Gets whether or not animals are allowed to spawn in this world.
*
* @return True if ANY animal can, false if no animals can spawn.
*/
public boolean getSpawningAnimals() {
return worldConfig.getSpawningAnimals();
}
/**
* Sets whether or not animals can spawn.
* <br/>
* If there are values in {@link #getSpawningAnimalsExceptions()} and this is false,
* those animals become the exceptions, and will spawn
*
* @param spawningAnimals True to allow spawning of monsters, false to prevent.
*/
public Try<Void> setSpawningAnimals(boolean spawningAnimals) {
return worldConfig.setSpawningAnimals(spawningAnimals);
}
/**
* Gets the amount of ticks between animal spawns.
*
* @return The amount of ticks between animal spawns.
*/
public int getSpawningAnimalsTicks() {
return worldConfig.getSpawningAnimalsTicks();
}
/**
* Sets the amount of ticks between animal spawns. Set to -1 to use bukkit default.
*
* @param spawningAnimalsAmount The amount of ticks between animal spawns.
* @return Result of setting property.
*/
public Try<Void> setSpawningAnimalsTicks(int spawningAnimalsAmount) {
return worldConfig.setSpawningAnimalsTicks(spawningAnimalsAmount);
}
/**
* Returns a list of animals. This list always negates the {@link #getSpawningAnimals()} result.
*
* @return A list of animals that will spawn if {@link #getSpawningAnimals()} is false.
*/
public List<String> getSpawningAnimalsExceptions() {
return worldConfig.getSpawningAnimalsExceptions();
}
/**
* Sets the list of animals that will spawn if {@link #getSpawningAnimals()} is false.
*
* @param spawningAnimalsExceptions The list of animals that will spawn if {@link #getSpawningAnimals()} is false.
* @return Result of setting property.
*/
public Try<Void> setSpawningAnimalsExceptions(List<String> spawningAnimalsExceptions) {
return worldConfig.setSpawningAnimalsExceptions(spawningAnimalsExceptions);
}
/**
* Gets whether or not monsters are allowed to spawn in this world.
*
* @return True if ANY monster can, false if no monsters can spawn.
*/
public boolean getSpawningMonsters() {
return worldConfig.getSpawningMonsters();
}
/**
* Sets whether or not monsters can spawn.
* If there are values in {@link #getSpawningMonstersExceptions()} and this is false,
* those monsters become the exceptions, and will spawn
*
* @param spawningMonsters True to allow spawning of monsters, false to prevent.
* @return Result of setting property.
*/
public Try<Void> setSpawningMonsters(boolean spawningMonsters) {
return worldConfig.setSpawningMonsters(spawningMonsters);
}
/**
* Gets the amount of ticks between monster spawns.
*
* @return The amount of ticks between monster spawns.
*/
public int getSpawningMonstersTicks() {
return worldConfig.getSpawningMonstersTicks();
}
/**
* Sets the amount of ticks between monster spawns. Set to -1 to use bukkit default.
*
* @param spawningMonstersAmount The amount of ticks between monster spawns.
* @return Result of setting property.
*/
public Try<Void> setSpawningMonstersTicks(int spawningMonstersAmount) {
return worldConfig.setSpawningMonstersTicks(spawningMonstersAmount);
}
/**
* Returns a list of monsters. This list always negates the {@link #getSpawningMonsters()} result.
*
* @return A list of monsters that will spawn if {@link #getSpawningMonsters()} is false.
*/
public List<String> getSpawningMonstersExceptions() {
return worldConfig.getSpawningMonstersExceptions();
}
/**
* Sets the list of monsters that will spawn if {@link #getSpawningMonsters()} is false.
*
* @param spawningMonstersExceptions The list of monsters that will spawn if {@link #getSpawningMonsters()}
* is false.
* @return Result of setting property.
*/
public Try<Void> setSpawningMonstersExceptions(List<String> spawningMonstersExceptions) {
return worldConfig.setSpawningMonstersExceptions(spawningMonstersExceptions);
}
/**
* Gets a list of all the worlds that players CANNOT travel to from this world,regardless of their access
* permissions.
*
* @return A List of world names.
*/
public List<String> getWorldBlacklist() {
return worldConfig.getWorldBlacklist();
}
/**
* Sets the list of worlds that players CANNOT travel to from this world, regardless of their access permissions.
*
* @param worldBlacklist A List of world names.
* @return Result of setting property.
*/
public Try<Void> setWorldBlacklist(List<String> worldBlacklist) {
return worldConfig.setWorldBlacklist(worldBlacklist);
}
/**
* Gets the world config. Only for internal use.
*
* @return The world config.
*/
WorldConfig getWorldConfig() {
return worldConfig;
}
/**
* Sets the world config. Only for internal use.
*
* @param worldConfig The world config.
*/
void setWorldConfig(WorldConfig worldConfig) {
this.worldConfig = worldConfig;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "MultiverseWorld{"
+ "name='" + worldName + "', "
+ "env='" + getEnvironment() + "', "
+ "gen='" + getGenerator() + "'"
+ '}';
}
}

View File

@ -1,69 +1,872 @@
package com.onarandombox.MultiverseCore.worldnew;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.base.Strings;
import com.onarandombox.MultiverseCore.api.BlockSafety;
import com.onarandombox.MultiverseCore.api.LocationManipulation;
import com.onarandombox.MultiverseCore.api.SafeTTeleporter;
import com.onarandombox.MultiverseCore.utils.message.MessageReplacement;
import com.onarandombox.MultiverseCore.utils.result.Attempt;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
import com.onarandombox.MultiverseCore.worldnew.config.WorldConfig;
import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigFile;
import com.onarandombox.MultiverseCore.worldnew.config.WorldsConfigManager;
import com.onarandombox.MultiverseCore.worldnew.generators.GeneratorProvider;
import com.onarandombox.MultiverseCore.worldnew.helpers.DataStore.GameRulesStore;
import com.onarandombox.MultiverseCore.worldnew.helpers.DataTransfer;
import com.onarandombox.MultiverseCore.worldnew.helpers.FilesManipulator;
import com.onarandombox.MultiverseCore.worldnew.options.CloneWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.options.CreateWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.options.ImportWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.options.KeepWorldSettingsOptions;
import com.onarandombox.MultiverseCore.worldnew.options.RegenWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.options.UnloadWorldOptions;
import com.onarandombox.MultiverseCore.worldnew.reasons.CloneFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.CreateFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.DeleteFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.ImportFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.LoadFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.RegenFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.RemoveFailureReason;
import com.onarandombox.MultiverseCore.worldnew.reasons.UnloadFailureReason;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@Service
import static com.onarandombox.MultiverseCore.utils.message.MessageReplacement.replace;
import static com.onarandombox.MultiverseCore.worldnew.helpers.DataStore.WorldBorderStore;
import static com.onarandombox.MultiverseCore.worldnew.helpers.DataStore.WorldConfigStore;
/**
* This manager contains all the world managing functions that your heart desires.
*/
@Service // SUPPRESS CHECKSTYLE: ClassFanOutComplexity This is the world manager, it's going to be complex.
public class WorldManager {
private final WorldsConfigFile worldsConfigFile;
private static final List<String> CLONE_IGNORE_FILES = Arrays.asList("uid.dat", "session.lock");
private final Map<String, MultiverseWorld> worldsMap;
private final Map<String, LoadedMultiverseWorld> loadedWorldsMap;
private final List<String> unloadTracker;
private final List<String> loadTracker;
private final WorldsConfigManager worldsConfigManager;
private final WorldNameChecker worldNameChecker;
private final GeneratorProvider generatorProvider;
private final FilesManipulator filesManipulator;
private final BlockSafety blockSafety;
private final SafeTTeleporter safetyTeleporter;
private final LocationManipulation locationManipulation;
@Inject
WorldManager(@NotNull WorldsConfigFile worldsConfigFile) {
this.worldsConfigFile = worldsConfigFile;
this.worldsConfigFile.load();
WorldManager(
@NotNull WorldsConfigManager worldsConfigManager,
@NotNull WorldNameChecker worldNameChecker,
@NotNull GeneratorProvider generatorProvider,
@NotNull FilesManipulator filesManipulator,
@NotNull BlockSafety blockSafety,
@NotNull SafeTTeleporter safetyTeleporter,
@NotNull LocationManipulation locationManipulation) {
this.worldsMap = new HashMap<>();
this.loadedWorldsMap = new HashMap<>();
this.unloadTracker = new ArrayList<>();
this.loadTracker = new ArrayList<>();
this.worldsConfigManager = worldsConfigManager;
this.worldNameChecker = worldNameChecker;
this.generatorProvider = generatorProvider;
this.filesManipulator = filesManipulator;
this.blockSafety = blockSafety;
this.safetyTeleporter = safetyTeleporter;
this.locationManipulation = locationManipulation;
}
public void loadAllWorlds() {
for (String worldName : worldsConfigFile.getAllWorldsInConfig()) {
Logging.fine("Loading world: " + worldName);
loadWorld(worldName);
/**
* Loads all worlds from the worlds config.
*
* @return The result of the load.
*/
public Try<Void> initAllWorlds() {
return updateWorldsFromConfig().andThenTry(() -> {
loadDefaultWorlds();
autoLoadWorlds();
saveWorldsConfig();
});
}
/**
* Updates the current set of worlds to match the worlds config.
*
* @return A successful Try if the worlds.yml config was loaded successfully.
*/
private Try<Void> updateWorldsFromConfig() {
return worldsConfigManager.load().mapTry(result -> {
loadNewWorldConfigs(result._1());
removeWorldsNotInConfigs(result._2());
return null;
});
}
private void loadNewWorldConfigs(Collection<WorldConfig> newWorldConfigs) {
newWorldConfigs.forEach(worldConfig -> getWorld(worldConfig.getWorldName())
.peek(unloadedWorld -> unloadedWorld.setWorldConfig(worldConfig))
.onEmpty(() -> {
MultiverseWorld mvWorld = new MultiverseWorld(worldConfig.getWorldName(), worldConfig);
worldsMap.put(mvWorld.getName(), mvWorld);
}));
}
private void removeWorldsNotInConfigs(Collection<String> removedWorlds) {
removedWorlds.forEach(worldName -> removeWorld(worldName)
.onFailure(failure -> Logging.severe("Failed to unload world %s: %s", worldName, failure))
.onSuccess(success -> Logging.fine("Unloaded world %s as it was removed from config", worldName)));
}
/**
* Load worlds that are already loaded by bukkit before Multiverse-Core is loaded.
*/
private void loadDefaultWorlds() {
Bukkit.getWorlds().forEach(bukkitWorld -> {
if (isWorld(bukkitWorld.getName())) {
return;
}
importWorld(ImportWorldOptions.worldName(bukkitWorld.getName())
.environment(bukkitWorld.getEnvironment())
.generator(generatorProvider.getDefaultGeneratorForWorld(bukkitWorld.getName())));
});
}
/**
* Loads all worlds that are set to autoload.
*/
private void autoLoadWorlds() {
getWorlds().forEach(world -> {
if (isLoadedWorld(world) || !world.getAutoLoad()) {
return;
}
loadWorld(world).onFailure(failure -> Logging.severe("Failed to load world %s: %s",
world.getName(), failure));
});
}
/**
* Creates a new world.
*
* @param options The options for customizing the creation of a new world.
* @return The result of the creation.
*/
public Attempt<LoadedMultiverseWorld, CreateFailureReason> createWorld(CreateWorldOptions options) {
return validateCreateWorldOptions(options).mapAttempt(this::createValidatedWorld);
}
private Attempt<CreateWorldOptions, CreateFailureReason> validateCreateWorldOptions(
CreateWorldOptions options) {
if (!worldNameChecker.isValidWorldName(options.worldName())) {
return worldActionResult(CreateFailureReason.INVALID_WORLDNAME, options.worldName());
} else if (getLoadedWorld(options.worldName()).isDefined()) {
return worldActionResult(CreateFailureReason.WORLD_EXIST_LOADED, options.worldName());
} else if (getWorld(options.worldName()).isDefined()) {
return worldActionResult(CreateFailureReason.WORLD_EXIST_UNLOADED, options.worldName());
} else if (hasWorldFolder(options.worldName())) {
return worldActionResult(CreateFailureReason.WORLD_EXIST_FOLDER, options.worldName());
}
return worldActionResult(options);
}
private boolean hasWorldFolder(String worldName) {
File worldFolder = new File(Bukkit.getWorldContainer(), worldName);
return worldFolder.exists();
}
private Attempt<LoadedMultiverseWorld, CreateFailureReason> createValidatedWorld(
CreateWorldOptions options) {
String parsedGenerator = parseGenerator(options.worldName(), options.generator());
WorldCreator worldCreator = WorldCreator.name(options.worldName())
.environment(options.environment())
.generateStructures(options.generateStructures())
.generator(parsedGenerator)
.seed(options.seed())
.type(options.worldType());
return createBukkitWorld(worldCreator).fold(
exception -> worldActionResult(CreateFailureReason.BUKKIT_CREATION_FAILED,
options.worldName(), exception),
world -> {
LoadedMultiverseWorld loadedWorld = newLoadedMultiverseWorld(
world,
parsedGenerator,
options.useSpawnAdjust());
return worldActionResult(loadedWorld);
});
}
/**
* Imports an existing world folder.
*
* @param options The options for customizing the import of an existing world folder.
* @return The result of the import.
*/
public Attempt<LoadedMultiverseWorld, ImportFailureReason> importWorld(
ImportWorldOptions options) {
return validateImportWorldOptions(options).mapAttempt(this::doImportWorld);
}
private Attempt<ImportWorldOptions, ImportFailureReason> validateImportWorldOptions(
ImportWorldOptions options) {
String worldName = options.worldName();
if (!worldNameChecker.isValidWorldName(worldName)) {
return worldActionResult(ImportFailureReason.INVALID_WORLDNAME, worldName);
} else if (!worldNameChecker.isValidWorldFolder(worldName)) {
return worldActionResult(ImportFailureReason.WORLD_FOLDER_INVALID, worldName);
} else if (isLoadedWorld(worldName)) {
return worldActionResult(ImportFailureReason.WORLD_EXIST_LOADED, worldName);
} else if (isWorld(worldName)) {
return worldActionResult(ImportFailureReason.WORLD_EXIST_UNLOADED, worldName);
}
return worldActionResult(options);
}
private Attempt<LoadedMultiverseWorld, ImportFailureReason> doImportWorld(
ImportWorldOptions options) {
String parsedGenerator = parseGenerator(options.worldName(), options.generator());
WorldCreator worldCreator = WorldCreator.name(options.worldName())
.environment(options.environment())
.generator(parsedGenerator);
return createBukkitWorld(worldCreator).fold(
exception -> worldActionResult(ImportFailureReason.BUKKIT_CREATION_FAILED,
options.worldName(), exception),
world -> {
LoadedMultiverseWorld loadedWorld = newLoadedMultiverseWorld(world,
parsedGenerator,
options.useSpawnAdjust());
return worldActionResult(loadedWorld);
});
}
/**
* Parses generator string and defaults to generator in bukkit.yml if not specified.
*
* @param worldName The name of the world.
* @param generator The input generator string.
* @return The parsed generator string.
*/
private @Nullable String parseGenerator(@NotNull String worldName, @Nullable String generator) {
return Strings.isNullOrEmpty(generator)
? generatorProvider.getDefaultGeneratorForWorld(worldName)
: generator;
}
/**
* Creates a new loaded multiverseWorld from a bukkit world.
*
* @param world The bukkit world to create a multiverse world from.
* @param generator The generator string.
* @param adjustSpawn Whether to adjust spawn.
*/
private LoadedMultiverseWorld newLoadedMultiverseWorld(
@NotNull World world, @Nullable String generator, boolean adjustSpawn) {
WorldConfig worldConfig = worldsConfigManager.addWorldConfig(world.getName());
worldConfig.setAdjustSpawn(adjustSpawn);
worldConfig.setGenerator(generator == null ? "" : generator);
MultiverseWorld mvWorld = new MultiverseWorld(world.getName(), worldConfig);
worldsMap.put(mvWorld.getName(), mvWorld);
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
world,
worldConfig,
blockSafety,
safetyTeleporter,
locationManipulation);
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
saveWorldsConfig();
return loadedWorld;
}
public void addWorld(String worldName) {
WorldConfig worldConfig = worldsConfigFile.getWorldConfig(worldName);
// TODO: Implement logic
/**
* Loads an existing world in config.
*
* @param worldName The name of the world to load.
* @return The result of the load.
*/
public Attempt<LoadedMultiverseWorld, LoadFailureReason> loadWorld(@NotNull String worldName) {
return getWorld(worldName)
.map(this::loadWorld)
.getOrElse(() -> worldNameChecker.isValidWorldFolder(worldName)
? worldActionResult(LoadFailureReason.WORLD_EXIST_FOLDER, worldName)
: worldActionResult(LoadFailureReason.WORLD_NON_EXISTENT, worldName));
}
/**
* Loads an existing world in config.
*
* @param world The world to load.
* @return The result of the load.
*/
public Attempt<LoadedMultiverseWorld, LoadFailureReason> loadWorld(@NotNull MultiverseWorld world) {
return validateWorldToLoad(world).mapAttempt(this::doLoadWorld);
}
private Attempt<MultiverseWorld, LoadFailureReason> validateWorldToLoad(
@NotNull MultiverseWorld mvWorld) {
if (loadTracker.contains(mvWorld.getName())) {
// This is to prevent recursive calls by WorldLoadEvent
Logging.fine("World already loading: " + mvWorld.getName());
return worldActionResult(LoadFailureReason.WORLD_ALREADY_LOADING, mvWorld.getName());
} else if (isLoadedWorld(mvWorld)) {
Logging.severe("World already loaded: " + mvWorld.getName());
return worldActionResult(LoadFailureReason.WORLD_EXIST_LOADED, mvWorld.getName());
}
return worldActionResult(mvWorld);
}
private Attempt<LoadedMultiverseWorld, LoadFailureReason> doLoadWorld(@NotNull MultiverseWorld mvWorld) {
return createBukkitWorld(WorldCreator.name(mvWorld.getName())
.environment(mvWorld.getEnvironment())
.generator(Strings.isNullOrEmpty(mvWorld.getGenerator()) ? null : mvWorld.getGenerator())
.seed(mvWorld.getSeed())).fold(
exception -> worldActionResult(LoadFailureReason.BUKKIT_CREATION_FAILED,
mvWorld.getName(), exception),
world -> {
WorldConfig worldConfig = worldsConfigManager.getWorldConfig(mvWorld.getName()).get();
LoadedMultiverseWorld loadedWorld = new LoadedMultiverseWorld(
world,
worldConfig,
blockSafety,
safetyTeleporter,
locationManipulation);
loadedWorldsMap.put(loadedWorld.getName(), loadedWorld);
saveWorldsConfig();
return worldActionResult(loadedWorld);
});
}
/**
* Unloads an existing multiverse world. It will still remain as an unloaded world.
*
* @param options The options for customizing the unloading of a world.
* @return The result of the unload action.
*/
public Attempt<MultiverseWorld, UnloadFailureReason> unloadWorld(@NotNull UnloadWorldOptions options) {
LoadedMultiverseWorld world = options.world();
if (unloadTracker.contains(world.getName())) {
// This is to prevent recursive calls by WorldUnloadEvent
Logging.fine("World already unloading: " + world.getName());
return worldActionResult(UnloadFailureReason.WORLD_ALREADY_UNLOADING, world.getName());
}
if (options.removePlayers()) {
removePlayersFromWorld(world);
}
return unloadBukkitWorld(world.getBukkitWorld().getOrNull(), options.saveBukkitWorld()).fold(
exception -> worldActionResult(UnloadFailureReason.BUKKIT_UNLOAD_FAILED,
world.getName(), exception),
success -> Option.of(loadedWorldsMap.remove(world.getName())).fold(
() -> {
Logging.severe("Failed to remove world from map: " + world.getName());
return worldActionResult(UnloadFailureReason.WORLD_NON_EXISTENT, world.getName());
},
mvWorld -> {
Logging.fine("Removed MultiverseWorld from map: " + world.getName());
mvWorld.getWorldConfig().deferenceMVWorld();
return worldActionResult(getWorld(mvWorld.getName()).get());
}));
}
private void removePlayersFromWorld(@NotNull LoadedMultiverseWorld world) {
world.getPlayers().peek(players -> players.forEach(player -> {
Location spawnLocation = Bukkit.getWorlds().get(0).getSpawnLocation();
if (player.isOnline()) {
Logging.fine("Teleporting player '%s' to world spawn: %s", player.getName(), spawnLocation);
safetyTeleporter.safelyTeleport(null, player, spawnLocation, true);
}
}));
}
/**
* Removes an existing multiverse world. It will be deleted from the worlds config and will no longer be an
* unloaded world. World files will not be deleted.
*
* @param worldName The name of the world to remove.
* @return The result of the remove.
*/
public Attempt<String, RemoveFailureReason> removeWorld(
@NotNull String worldName) {
return getWorld(worldName)
.map(this::removeWorld)
.getOrElse(() -> worldActionResult(RemoveFailureReason.WORLD_NON_EXISTENT, worldName));
}
/**
* Removes an existing multiverse world. It will be deleted from the worlds config and will no longer be an
* unloaded world. World files will not be deleted.
*
* @param world The multiverse world to remove.
* @return The result of the remove.
*/
public Attempt<String, RemoveFailureReason> removeWorld(@NotNull MultiverseWorld world) {
return getLoadedWorld(world).fold(
() -> removeWorldFromConfig(world),
this::removeWorld);
}
/**
* Removes an existing multiverse world. It will be deleted from the worlds config and will no longer be an
* unloaded world. World files will not be deleted.
*
* @param loadedWorld The multiverse world to remove.
* @return The result of the remove.
*/
public Attempt<String, RemoveFailureReason> removeWorld(@NotNull LoadedMultiverseWorld loadedWorld) {
// TODO: Config option on removePlayers
return unloadWorld(UnloadWorldOptions.world(loadedWorld).removePlayers(true))
.transform(RemoveFailureReason.UNLOAD_FAILED)
.mapAttempt(this::removeWorldFromConfig);
}
/**
* Removes an existing multiverse world from the world's config. It will no longer be a world known to Multiverse.
*
* @param world The multiverse world to remove.
* @return The result of the remove.
*/
private Attempt<String, RemoveFailureReason> removeWorldFromConfig(@NotNull MultiverseWorld world) {
// Remove world from config
worldsMap.remove(world.getName());
worldsConfigManager.deleteWorldConfig(world.getName());
saveWorldsConfig();
return worldActionResult(world.getName());
}
public void loadWorld(String worldName) {
WorldConfig worldConfig = worldsConfigFile.getWorldConfig(worldName);
// TODO: Implement logic
/**
* Deletes an existing multiverse world entirely. World will be loaded if it is not already loaded.
* Warning: This will delete all world files.
*
* @param worldName The name of the world to delete.
* @return The result of the delete action.
*/
public Attempt<String, DeleteFailureReason> deleteWorld(@NotNull String worldName) {
return getWorld(worldName)
.map(this::deleteWorld)
.getOrElse(() -> worldActionResult(DeleteFailureReason.WORLD_NON_EXISTENT, worldName));
}
public void unloadWorld() {
// TODO: Implement logic
/**
* Deletes an existing multiverse world entirely. World will be loaded if it is not already loaded.
* Warning: This will delete all world files.
*
* @param world The world to delete.
* @return The result of the delete action.
*/
public Attempt<String, DeleteFailureReason> deleteWorld(@NotNull MultiverseWorld world) {
return getLoadedWorld(world).fold(
() -> loadWorld(world)
.transform(DeleteFailureReason.LOAD_FAILED)
.mapAttempt(this::deleteWorld),
this::deleteWorld);
}
public void removeWorld(String worldName) {
// TODO: Implement logic
worldsConfigFile.deleteWorldConfigSection(worldName);
saveWorldsConfig();
/**
* Deletes an existing multiverse world entirely. Warning: This will delete all world files.
*
* @param world The multiverse world to delete.
* @return The result of the delete action.
*/
public Attempt<String, DeleteFailureReason> deleteWorld(@NotNull LoadedMultiverseWorld world) {
// TODO: Possible config options to keep certain files
AtomicReference<File> worldFolder = new AtomicReference<>();
return validateWorldToDelete(world)
.peek(worldFolder::set)
.mapAttempt(() -> removeWorld(world).transform(DeleteFailureReason.REMOVE_FAILED))
.mapAttempt(() -> filesManipulator.deleteFolder(worldFolder.get()).fold(
exception -> worldActionResult(DeleteFailureReason.FAILED_TO_DELETE_FOLDER,
world.getName(), exception),
success -> worldActionResult(world.getName())));
}
public void deleteWorld(String worldName) {
// TODO: Implement logic
worldsConfigFile.deleteWorldConfigSection(worldName);
saveWorldsConfig();
private Attempt<File, DeleteFailureReason> validateWorldToDelete(
@NotNull LoadedMultiverseWorld world) {
File worldFolder = world.getBukkitWorld().map(World::getWorldFolder).getOrNull();
if (worldFolder == null || !worldNameChecker.isValidWorldFolder(worldFolder)) {
Logging.severe("Failed to get world folder for world: " + world.getName());
return worldActionResult(DeleteFailureReason.WORLD_FOLDER_NOT_FOUND, world.getName());
}
return worldActionResult(worldFolder);
}
public void getMVWorld(String worldName) {
// TODO: Implement logic
/**
* Clones an existing multiverse world.
*
* @param options The options for customizing the cloning of a world.
* @return The result of the clone.
*/
public Attempt<LoadedMultiverseWorld, CloneFailureReason> cloneWorld(@NotNull CloneWorldOptions options) {
return cloneWorldValidateWorld(options)
.mapAttempt(this::cloneWorldCopyFolder)
.mapAttempt(validatedOptions -> {
ImportWorldOptions importWorldOptions = ImportWorldOptions
.worldName(validatedOptions.newWorldName())
.environment(validatedOptions.world().getEnvironment())
.generator(validatedOptions.world().getGenerator());
return importWorld(importWorldOptions).transform(CloneFailureReason.IMPORT_FAILED);
})
.onSuccess(newWorld -> {
cloneWorldTransferData(options, newWorld);
if (options.keepWorldConfig()) {
newWorld.setSpawnLocation(options.world().getSpawnLocation());
}
saveWorldsConfig();
});
}
public void getUnloadedWorld(String worldName) {
// TODO: Implement logic
private Attempt<CloneWorldOptions, CloneFailureReason> cloneWorldValidateWorld(
@NotNull CloneWorldOptions options) {
String newWorldName = options.newWorldName();
if (!worldNameChecker.isValidWorldName(newWorldName)) {
Logging.severe("Invalid world name: " + newWorldName);
return worldActionResult(CloneFailureReason.INVALID_WORLDNAME, newWorldName);
}
if (worldNameChecker.isValidWorldFolder(newWorldName)) {
return worldActionResult(CloneFailureReason.WORLD_EXIST_FOLDER, newWorldName);
}
if (isLoadedWorld(newWorldName)) {
Logging.severe("World already loaded when attempting to clone: " + newWorldName);
return worldActionResult(CloneFailureReason.WORLD_EXIST_LOADED, newWorldName);
}
if (isWorld(newWorldName)) {
Logging.severe("World already exist unloaded: " + newWorldName);
return worldActionResult(CloneFailureReason.WORLD_EXIST_UNLOADED, newWorldName);
}
return worldActionResult(options);
}
public void saveWorldsConfig() {
worldsConfigFile.save();
private Attempt<CloneWorldOptions, CloneFailureReason> cloneWorldCopyFolder(
@NotNull CloneWorldOptions options) {
File worldFolder = options.world().getBukkitWorld().map(World::getWorldFolder).get();
File newWorldFolder = new File(Bukkit.getWorldContainer(), options.newWorldName());
return filesManipulator.copyFolder(worldFolder, newWorldFolder, CLONE_IGNORE_FILES).fold(
exception -> worldActionResult(CloneFailureReason.COPY_FAILED,
options.world().getName(), exception),
success -> worldActionResult(options));
}
private void cloneWorldTransferData(@NotNull CloneWorldOptions options, @NotNull LoadedMultiverseWorld newWorld) {
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, options.world());
dataTransfer.pasteAllTo(newWorld);
}
private DataTransfer<LoadedMultiverseWorld> transferData(
@NotNull KeepWorldSettingsOptions options, @NotNull LoadedMultiverseWorld world) {
DataTransfer<LoadedMultiverseWorld> dataTransfer = new DataTransfer<>();
if (options.keepWorldConfig()) {
dataTransfer.addDataStore(new WorldConfigStore(), world);
}
if (options.keepGameRule()) {
dataTransfer.addDataStore(new GameRulesStore(), world);
}
if (options.keepWorldBorder()) {
dataTransfer.addDataStore(new WorldBorderStore(), world);
}
return dataTransfer;
}
/**
* Regenerates a world.
*
* @param options The options for customizing the regeneration of a world.
* @return The result of the regeneration.
*/
public Attempt<LoadedMultiverseWorld, RegenFailureReason> regenWorld(@NotNull RegenWorldOptions options) {
LoadedMultiverseWorld world = options.world();
List<Player> playersInWorld = world.getPlayers().getOrElse(Collections.emptyList());
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, world);
boolean shouldKeepSpawnLocation = options.keepWorldConfig() && options.seed() == world.getSeed();
Location spawnLocation = world.getSpawnLocation();
CreateWorldOptions createWorldOptions = CreateWorldOptions.worldName(world.getName())
.environment(world.getEnvironment())
.generateStructures(world.canGenerateStructures().getOrElse(true))
.generator(world.getGenerator())
.seed(options.seed())
.useSpawnAdjust(!shouldKeepSpawnLocation && world.getAdjustSpawn())
.worldType(world.getWorldType().getOrElse(WorldType.NORMAL));
return deleteWorld(world)
.transform(RegenFailureReason.DELETE_FAILED)
.mapAttempt(() -> createWorld(createWorldOptions).transform(RegenFailureReason.CREATE_FAILED))
.onSuccess(newWorld -> {
dataTransfer.pasteAllTo(newWorld);
if (shouldKeepSpawnLocation) {
// Special case for spawn location to prevent unsafe location if world was regen using a
// different seed.
newWorld.setSpawnLocation(spawnLocation);
}
teleportPlayersToWorld(playersInWorld, newWorld);
saveWorldsConfig();
});
}
private void teleportPlayersToWorld(@NotNull List<Player> players, @NotNull LoadedMultiverseWorld world) {
players.forEach(player -> {
Location spawnLocation = world.getSpawnLocation();
if (player.isOnline()) {
safetyTeleporter.safelyTeleport(null, player, spawnLocation, true);
}
});
}
private <T, F extends FailureReason> Attempt<T, F> worldActionResult(@NotNull T value) {
return Attempt.success(value);
}
private <T, F extends FailureReason> Attempt<T, F> worldActionResult(
@NotNull F failureReason, @NotNull String worldName) {
return Attempt.failure(failureReason, replaceWorldName(worldName));
}
private <T, F extends FailureReason> Attempt<T, F> worldActionResult(
@NotNull F failureReason, @NotNull String worldName, @NotNull Throwable error) {
// TODO: Localize error message if its a MultiverseException
return Attempt.failure(failureReason, replaceWorldName(worldName), replaceError(error.getMessage()));
}
private MessageReplacement replaceWorldName(@NotNull String worldName) {
return replace("{world}").with(worldName);
}
private MessageReplacement replaceError(@NotNull String errorMessage) {
return replace("{error}").with(errorMessage);
}
/**
* Creates a bukkit world.
*
* @param worldCreator The world parameters.
* @return The created world.
*/
private Try<World> createBukkitWorld(WorldCreator worldCreator) {
return Try.of(() -> {
this.loadTracker.add(worldCreator.name());
World world = worldCreator.createWorld();
if (world == null) {
// TODO: Localize this
throw new Exception("World created returned null!");
}
Logging.fine("Bukkit created world: " + world.getName());
return world;
}).onFailure(exception -> {
Logging.severe("Failed to create bukkit world: " + worldCreator.name());
exception.printStackTrace();
}).andFinally(() -> this.loadTracker.remove(worldCreator.name()));
}
/**
* Unloads a bukkit world.
*
* @param world The bukkit world to unload.
* @return The unloaded world.
*/
private Try<Void> unloadBukkitWorld(World world, boolean save) {
return Try.run(() -> {
if (world == null) {
return;
}
unloadTracker.add(world.getName());
if (!Bukkit.unloadWorld(world, save)) {
// TODO: Localize this, maybe with MultiverseException
throw new Exception("Is this the default world? You can't unload the default world!");
}
Logging.fine("Bukkit unloaded world: " + world.getName());
}).onFailure(exception -> {
Logging.severe("Failed to unload bukkit world: " + world.getName());
exception.printStackTrace();
}).andFinally(() -> unloadTracker.remove(world.getName()));
}
/**
* Gets a list of all potential worlds that can be loaded from the server folders.
* Checks based on folder contents and name.
*
* @return A list of all potential worlds.
*/
public List<String> getPotentialWorlds() {
File[] files = Bukkit.getWorldContainer().listFiles();
if (files == null) {
return Collections.emptyList();
}
return Arrays.stream(files)
.filter(file -> !isWorld(file.getName()))
.filter(worldNameChecker::isValidWorldFolder)
.map(File::getName)
.toList();
}
/**
* Get a world that is not loaded.
*
* @param worldName The name of the world to get.
* @return The world if it exists.
*/
public Option<MultiverseWorld> getUnloadedWorld(@Nullable String worldName) {
return isLoadedWorld(worldName) ? Option.none() : Option.of(worldsMap.get(worldName));
}
/**
* Get a list of all worlds that are not loaded.
*
* @return A list of all worlds that are not loaded.
*/
public Collection<MultiverseWorld> getUnloadedWorlds() {
return worldsMap.values().stream().filter(world -> !world.isLoaded()).toList();
}
/**
* Check if a world is a world that is not loaded.
*
* @param worldName The name of the world to check.
* @return True if the world is a world that is not loaded.
*/
public boolean isUnloadedWorld(@Nullable String worldName) {
return !isLoadedWorld(worldName) && isWorld(worldName);
}
/**
* Get a world that may or may not be loaded. It will an {@link LoadedMultiverseWorld} if the world is loaded,
* otherwise returns an {@link MultiverseWorld} instance.
*
* @param worldName The name of the world to get.
* @return The world if it exists.
*/
public Option<MultiverseWorld> getWorld(@Nullable String worldName) {
return getLoadedWorld(worldName).fold(() -> getUnloadedWorld(worldName), Option::of);
}
/**
* <p>Get a list of all worlds that may or may not be loaded. It will an {@link LoadedMultiverseWorld} if the world
* is loaded, otherwise you will get an {@link MultiverseWorld} instance.</p>
*
* <p>If you want only unloaded worlds, use {@link #getUnloadedWorlds()}. If you want only loaded worlds, use
* {@link #getLoadedWorlds()}.</p>
*
* @return A list of all worlds that may or may not be loaded.
*/
public Collection<MultiverseWorld> getWorlds() {
return worldsMap.values().stream()
.map(world -> getLoadedWorld(world).fold(() -> world, loadedWorld -> loadedWorld))
.toList();
}
/**
* Check if a world is a world is known to multiverse, but may or may not be loaded.
*
* @param worldName The name of the world to check.
* @return True if the world is a world is known to multiverse, but may or may not be loaded.
*/
public boolean isWorld(@Nullable String worldName) {
return worldsMap.containsKey(worldName);
}
/**
* Get a multiverse world that is loaded.
*
* @param world The bukkit world that should be loaded.
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable World world) {
return world == null ? Option.none() : Option.of(loadedWorldsMap.get(world.getName()));
}
/**
* Get a multiverse world that is loaded.
*
* @param world The world that should be loaded.
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable MultiverseWorld world) {
return world == null ? Option.none() : Option.of(loadedWorldsMap.get(world.getName()));
}
/**
* Get a multiverse world that is loaded.
*
* @param worldName The name of the world to get.
* @return The multiverse world if it exists.
*/
public Option<LoadedMultiverseWorld> getLoadedWorld(@Nullable String worldName) {
return Option.of(loadedWorldsMap.get(worldName));
}
/**
* Get a list of all multiverse worlds that are loaded.
*
* @return A list of all multiverse worlds that are loaded.
*/
public Collection<LoadedMultiverseWorld> getLoadedWorlds() {
return loadedWorldsMap.values();
}
/**
* Check if a world is a multiverse world that is loaded.
*
* @param world The bukkit world to check.
* @return True if the world is a multiverse world that is loaded.
*/
public boolean isLoadedWorld(@Nullable World world) {
return world != null && isLoadedWorld(world.getName());
}
/**
* Check if a world is a multiverse world that is loaded.
*
* @param world The world to check.
* @return True if the world is a multiverse world that is loaded.
*/
public boolean isLoadedWorld(@Nullable MultiverseWorld world) {
return world != null && isLoadedWorld(world.getName());
}
/**
* Check if a world is a multiverse world that is loaded.
*
* @param worldName The name of the world to check.
* @return True if the world is a multiverse world that is loaded.
*/
public boolean isLoadedWorld(@Nullable String worldName) {
return loadedWorldsMap.containsKey(worldName);
}
/**
* Saves the worlds.yml config.
*
* @return true if it had successfully saved the file.
*/
public boolean saveWorldsConfig() {
return worldsConfigManager.save()
.onFailure(failure -> {
Logging.severe("Failed to save worlds config: %s", failure);
failure.printStackTrace();
})
.isSuccess();
}
}

View File

@ -0,0 +1,160 @@
package com.onarandombox.MultiverseCore.worldnew;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.util.Set;
import java.util.regex.Pattern;
/**
* <p>Utility class in helping to check the status of a world name and it's associated world folder.</p>
*
* <p>Note this is for preliminary checks and better command output. A valid result will suggest but not
* 100% determine that a world name can be created, loaded or imported.</p>
*/
@Service
public class WorldNameChecker {
private static final Pattern WORLD_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9/._-]+");
private static final Set<String> BLACKLIST_NAMES = Set.of(
"cache",
"config",
"crash-reports",
"logs",
"plugins",
"versions");
/**
* Checks if a world name is valid.
*
* @param worldName The world name to check on.
* @return True if check result is valid, else false.
*/
public boolean isValidWorldName(@Nullable String worldName) {
return checkName(worldName) == NameStatus.VALID;
}
/**
* Checks the current validity status of a world name.
*
* @param worldName The world name to check on.
* @return The resulting name status.
*/
@NotNull
public NameStatus checkName(@Nullable String worldName) {
if (BLACKLIST_NAMES.contains(worldName)) {
return NameStatus.BLACKLISTED;
}
if (worldName == null || !WORLD_NAME_PATTERN.matcher(worldName).matches()) {
return NameStatus.INVALID_CHARS;
}
return NameStatus.VALID;
}
/**
* Checks if a world name has a valid world folder.
*
* @param worldName The world name to check on.
* @return True if check result is valid, else false.
*/
public boolean isValidWorldFolder(@Nullable String worldName) {
return checkFolder(worldName) == FolderStatus.VALID;
}
/**
* Checks if a world folder is valid.
*
* @param worldFolder The world folder to check on.
* @return True if check result is valid, else false.
*/
public boolean isValidWorldFolder(@Nullable File worldFolder) {
return checkFolder(worldFolder) == FolderStatus.VALID;
}
/**
* Checks the current folder status for a world name.
*
* @param worldName The world name to check on.
* @return The resulting folder status.
*/
@NotNull
public FolderStatus checkFolder(@Nullable String worldName) {
if (worldName == null) {
return FolderStatus.DOES_NOT_EXIST;
}
File worldFolder = new File(Bukkit.getWorldContainer(), worldName);
return checkFolder(worldFolder);
}
/**
* Checks the current folder status.
*
* @param worldFolder The world folder to check on.
* @return The resulting folder status.
*/
@NotNull
public FolderStatus checkFolder(@Nullable File worldFolder) {
if (worldFolder == null || !worldFolder.exists() || !worldFolder.isDirectory()) {
return FolderStatus.DOES_NOT_EXIST;
}
if (!folderHasDat(worldFolder)) {
return FolderStatus.NOT_A_WORLD;
}
return FolderStatus.VALID;
}
/**
* A very basic check to see if a folder has a level.dat file. If it does, we can safely assume
* it's a world folder.
*
* @param worldFolder The File that may be a world.
* @return True if it looks like a world, else false.
*/
private boolean folderHasDat(@NotNull File worldFolder) {
File[] files = worldFolder.listFiles((file, name) -> name.toLowerCase().endsWith(".dat"));
return files != null && files.length > 0;
}
/**
* Result after checking validity of world name.
*/
public enum NameStatus {
/**
* Name is valid.
*/
VALID,
/**
* Name not valid as it contains invalid characters.
*/
INVALID_CHARS,
/**
* Name not valid as it is deemed blacklisted.
*/
BLACKLISTED
}
/**
* Result after checking validity of world folder.
*/
public enum FolderStatus {
/**
* Folder is valid.
*/
VALID,
/**
* Folder exist, but contents in it doesnt look like a world.
*/
NOT_A_WORLD,
/**
* Folder does not exist.
*/
DOES_NOT_EXIST
}
}

View File

@ -0,0 +1,237 @@
package com.onarandombox.MultiverseCore.worldnew;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Ambient;
import org.bukkit.entity.Animals;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Ghast;
import org.bukkit.entity.Golem;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Monster;
import org.bukkit.entity.Phantom;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.Slime;
import org.bukkit.entity.Squid;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
// TODO: This entire class is a mess.
/**
* Used to remove animals from worlds that don't belong there.
*/
@Service
public class WorldPurger {
/**
* Synchronizes the given worlds with their settings.
*
* @param worlds A list of {@link LoadedMultiverseWorld}
*/
public void purgeWorlds(@Nullable List<LoadedMultiverseWorld> worlds) {
if (worlds == null || worlds.isEmpty()) {
return;
}
for (LoadedMultiverseWorld world : worlds) {
this.purgeWorld(world);
}
}
/**
* Convenience method for {@link #purgeWorld(LoadedMultiverseWorld, java.util.List, boolean, boolean)} that takes
* the settings from the world-config.
*
* @param world The {@link LoadedMultiverseWorld}.
*/
public void purgeWorld(@Nullable LoadedMultiverseWorld world) {
if (world == null) {
return;
}
ArrayList<String> allMobs = new ArrayList<>(world.getSpawningAnimalsExceptions());
allMobs.addAll(world.getSpawningMonstersExceptions());
purgeWorld(world, allMobs, !world.getSpawningAnimals(), !world.getSpawningMonsters());
}
/**
* Clear all animals/monsters that do not belong to a world according to the config.
*
* @param world The {@link LoadedMultiverseWorld}.
* @param thingsToKill A {@link List} of animals/monsters to be killed.
* @param negateAnimals Whether the monsters in the list should be negated.
* @param negateMonsters Whether the animals in the list should be negated.
*/
public void purgeWorld(
LoadedMultiverseWorld world,
List<String> thingsToKill,
boolean negateAnimals,
boolean negateMonsters) {
purgeWorld(world, thingsToKill, negateAnimals, negateMonsters, null);
}
/**
* Clear all animals/monsters that do not belong to a world according to the config.
*
* @param world The {@link LoadedMultiverseWorld}.
* @param thingsToKill A {@link List} of animals/monsters to be killed.
* @param negateAnimals Whether the monsters in the list should be negated.
* @param negateMonsters Whether the animals in the list should be negated.
* @param sender The {@link CommandSender} that initiated the action. He will/should be notified.
*/
public void purgeWorld(
@Nullable LoadedMultiverseWorld world,
@NotNull List<String> thingsToKill,
boolean negateAnimals,
boolean negateMonsters,
CommandSender sender) {
if (world == null) {
return;
}
World bukkitWorld = world.getBukkitWorld().getOrNull();
if (bukkitWorld == null) {
return;
}
int projectilesKilled = 0;
int entitiesKilled = 0;
boolean specifiedAll = thingsToKill.contains("ALL");
boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll;
boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll;
List<Entity> worldEntities = bukkitWorld.getEntities();
List<LivingEntity> livingEntities = new ArrayList<LivingEntity>(worldEntities.size());
List<Projectile> projectiles = new ArrayList<Projectile>(worldEntities.size());
for (final Entity e : worldEntities) {
if (e instanceof Projectile p) {
if (p.getShooter() != null) {
projectiles.add((Projectile) e);
}
} else if (e instanceof LivingEntity) {
livingEntities.add((LivingEntity) e);
}
}
for (final LivingEntity e : livingEntities) {
if (killDecision(e, thingsToKill, negateAnimals, negateMonsters, specifiedAnimals, specifiedMonsters)) {
final Iterator<Projectile> it = projectiles.iterator();
while (it.hasNext()) {
final Projectile p = it.next();
if (Objects.equals(p.getShooter(), e)) {
p.remove();
it.remove();
projectilesKilled++;
}
}
e.remove();
entitiesKilled++;
}
}
if (sender != null) {
sender.sendMessage(entitiesKilled + " entities purged from the world '" + world.getName() + "' along with "
+ projectilesKilled + " projectiles that belonged to them.");
}
}
/**
* Determines whether the specified creature should be killed and automatically reads the params from a world object.
*
* @param world The world.
* @param entity The creature.
* @return {@code true} if the creature should be killed, otherwise {@code false}.
*/
public boolean shouldWeKillThisCreature(@NotNull LoadedMultiverseWorld world, @NotNull Entity entity) {
ArrayList<String> allMobs = new ArrayList<>(world.getSpawningAnimalsExceptions());
allMobs.addAll(world.getSpawningMonstersExceptions());
return this.shouldWeKillThisCreature(entity, allMobs, !world.getSpawningAnimals(), !world.getSpawningMonsters());
}
/**
* Determines whether the specified creature should be killed.
*
* @param entity The creature.
* @param thingsToKill A {@link List} of animals/monsters to be killed.
* @param negateAnimals Whether the monsters in the list should be negated.
* @param negateMonsters Whether the animals in the list should be negated.
* @return {@code true} if the creature should be killed, otherwise {@code false}.
*/
public boolean shouldWeKillThisCreature(
Entity entity,
List<String> thingsToKill,
boolean negateAnimals,
boolean negateMonsters) {
boolean specifiedAll = thingsToKill.contains("ALL");
boolean specifiedAnimals = thingsToKill.contains("ANIMALS") || specifiedAll;
boolean specifiedMonsters = thingsToKill.contains("MONSTERS") || specifiedAll;
return this.killDecision(
entity,
thingsToKill,
negateAnimals,
negateMonsters,
specifiedAnimals,
specifiedMonsters);
}
private boolean killDecision(
Entity entity,
List<String> thingsToKill,
boolean negateAnimals,
boolean negateMonsters,
boolean specifiedAnimals,
boolean specifiedMonsters) {
boolean negate = false;
boolean specified = false;
if (entity instanceof Golem
|| entity instanceof Squid
|| entity instanceof Animals
|| entity instanceof Ambient) {
// it's an animal
if (specifiedAnimals && !negateAnimals) {
Logging.finest("Removing an entity because I was told to remove all animals in world %s: %s",
entity.getWorld().getName(), entity);
return true;
}
if (specifiedAnimals) {
specified = true;
}
negate = negateAnimals;
} else if (entity instanceof Monster
|| entity instanceof Ghast
|| entity instanceof Slime
|| entity instanceof Phantom) {
// it's a monster
if (specifiedMonsters && !negateMonsters) {
Logging.finest("Removing an entity because I was told to remove all monsters in world %s: %s",
entity.getWorld().getName(), entity);
return true;
}
if (specifiedMonsters) {
specified = true;
}
negate = negateMonsters;
}
for (String s : thingsToKill) {
EntityType type = EntityType.fromName(s);
if (type != null && type.equals(entity.getType())) {
specified = true;
if (!negate) {
Logging.finest(
"Removing an entity because it WAS specified and we are NOT negating in world %s: %s",
entity.getWorld().getName(), entity);
return true;
}
break;
}
}
if (!specified && negate) {
Logging.finest("Removing an entity because it was NOT specified and we ARE negating in world %s: %s",
entity.getWorld().getName(), entity);
return true;
}
return false;
}
}

View File

@ -0,0 +1,88 @@
package com.onarandombox.MultiverseCore.worldnew.config;
import com.onarandombox.MultiverseCore.configuration.migration.MigratorAction;
import io.vavr.control.Try;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import java.util.concurrent.atomic.AtomicReference;
class LegacyAliasMigrator implements MigratorAction {
@Override
public void migrate(ConfigurationSection config) {
AtomicReference<String> alias = new AtomicReference<>(config.getString("alias", ""));
if (alias.get().isEmpty()) return;
String color = config.getString("color", "");
String style = config.getString("style", "");
Try.of(() -> Enum.valueOf(EnglishChatColor.class, color.toUpperCase()))
.map(c -> c.color)
.onSuccess(c -> {
if (c != ChatColor.WHITE) {
alias.set("&" + c.getChar() + alias.get());
}
});
Try.of(() -> Enum.valueOf(EnglishChatStyle.class, style.toUpperCase()))
.map(c -> c.color)
.onSuccess(s -> {
if (s != null) {
alias.set("&" + s.getChar() + alias.get());
}
});
config.set("alias", alias.get());
config.set("color", null);
config.set("style", null);
}
private enum EnglishChatColor {
// BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable
AQUA(ChatColor.AQUA),
BLACK(ChatColor.BLACK),
BLUE(ChatColor.BLUE),
DARKAQUA(ChatColor.DARK_AQUA),
DARKBLUE(ChatColor.DARK_BLUE),
DARKGRAY(ChatColor.DARK_GRAY),
DARKGREEN(ChatColor.DARK_GREEN),
DARKPURPLE(ChatColor.DARK_PURPLE),
DARKRED(ChatColor.DARK_RED),
GOLD(ChatColor.GOLD),
GRAY(ChatColor.GRAY),
GREEN(ChatColor.GREEN),
LIGHTPURPLE(ChatColor.LIGHT_PURPLE),
RED(ChatColor.RED),
YELLOW(ChatColor.YELLOW),
WHITE(ChatColor.WHITE);
// END CHECKSTYLE-SUPPRESSION: JavadocVariable
private final ChatColor color;
//private final String text;
EnglishChatColor(ChatColor color) {
this.color = color;
}
}
private enum EnglishChatStyle {
// BEGIN CHECKSTYLE-SUPPRESSION: JavadocVariable
/**
* No style.
*/
NORMAL(null),
MAGIC(ChatColor.MAGIC),
BOLD(ChatColor.BOLD),
STRIKETHROUGH(ChatColor.STRIKETHROUGH),
UNDERLINE(ChatColor.UNDERLINE),
ITALIC(ChatColor.ITALIC);
// END CHECKSTYLE-SUPPRESSION: JavadocVariable
private final ChatColor color;
EnglishChatStyle(ChatColor color) {
this.color = color;
}
}
}

View File

@ -0,0 +1,71 @@
package com.onarandombox.MultiverseCore.worldnew.config;
import com.onarandombox.MultiverseCore.world.SimpleMVWorld;
import org.bukkit.Location;
import org.bukkit.configuration.serialization.SerializableAs;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.Map;
/**
* Null-location.
*/
@SerializableAs("MVNullLocation (It's a bug if you see this in your config file)")
public final class NullLocation extends SpawnLocation {
private static final NullLocation INSTANCE = new NullLocation();
/**
* Get the default null location instance.
*
* @return The instance.
*/
public static NullLocation get() {
return INSTANCE;
}
private NullLocation() {
super(0, -1, 0);
}
@Override
public @NotNull Location clone() {
throw new UnsupportedOperationException();
}
@Override
public @NotNull Map<String, Object> serialize() {
return Collections.emptyMap();
}
/**
* Let Bukkit be able to deserialize this.
*
* @param args The map.
* @return The deserialized object.
*/
public static SimpleMVWorld.NullLocation deserialize(Map<String, Object> args) {
return new SimpleMVWorld.NullLocation();
}
@Override
public @NotNull Vector toVector() {
throw new UnsupportedOperationException();
}
@Override
public int hashCode() {
return -1;
}
@Override
public boolean equals(Object obj) {
return false;
}
@Override
public String toString() {
return "Location{null}";
}
}

View File

@ -1,9 +1,4 @@
package com.onarandombox.MultiverseCore.world.configuration;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
package com.onarandombox.MultiverseCore.worldnew.config;
import org.bukkit.Chunk;
import org.bukkit.Location;
@ -11,6 +6,12 @@ import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.configuration.serialization.SerializableAs;
import org.jetbrains.annotations.NotNull;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
/**
* Just like a regular {@link Location}, however {@code world} is usually {@code null}
@ -20,14 +21,35 @@ import org.bukkit.configuration.serialization.SerializableAs;
public class SpawnLocation extends Location implements ConfigurationSerializable {
private Reference<World> worldRef;
/**
* Constructs a new Location with the given coordinates.
*
* @param x The x-coordinate of this new location
* @param y The y-coordinate of this new location
* @param z The z-coordinate of this new location
*/
public SpawnLocation(double x, double y, double z) {
super(null, x, y, z);
}
/**
* Constructs a new Location with the given coordinates and direction.
*
* @param x The x-coordinate of this new location
* @param y The y-coordinate of this new location
* @param z The z-coordinate of this new location
* @param yaw The absolute rotation on the x-plane, in degrees
* @param pitch The absolute rotation on the y-plane, in degrees
*/
public SpawnLocation(double x, double y, double z, float yaw, float pitch) {
super(null, x, y, z, yaw, pitch);
}
/**
* Constructs a new Location from an existing Location.
*
* @param loc The location to clone.
*/
public SpawnLocation(Location loc) {
this(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
}
@ -52,38 +74,43 @@ public class SpawnLocation extends Location implements ConfigurationSerializable
* {@inheritDoc}
*/
@Override
public Chunk getChunk() {
if ((this.worldRef != null) && (this.worldRef.get() != null))
return this.worldRef.get().getChunkAt(this);
return null;
public @NotNull Chunk getChunk() {
World world = this.worldRef != null ? this.worldRef.get() : null;
if (world != null) {
return world.getChunkAt(this);
}
throw new IllegalStateException("World is null");
}
/**
* {@inheritDoc}
*/
@Override
public Block getBlock() {
if ((this.worldRef != null) && (this.worldRef.get() != null))
return this.worldRef.get().getBlockAt(this);
return null;
public @NotNull Block getBlock() {
World world = this.worldRef != null ? this.worldRef.get() : null;
if (world != null) {
return world.getBlockAt(this);
}
throw new IllegalStateException("World is null");
}
/**
* {@inheritDoc}
*/
@Override
public Map<String, Object> serialize() {
Map<String, Object> serialized = new HashMap<String, Object>(5); // SUPPRESS CHECKSTYLE: MagicNumberCheck
serialized.put("x", this.getX());
serialized.put("y", this.getY());
serialized.put("z", this.getZ());
serialized.put("pitch", this.getPitch());
serialized.put("yaw", this.getYaw());
return serialized;
public @NotNull Map<String, Object> serialize() {
return new HashMap<>() {{
put("x", getX());
put("y", getY());
put("z", getZ());
put("pitch", getPitch());
put("yaw", getYaw());
}};
}
/**
* Let Bukkit be able to deserialize this.
*
* @param args The map.
* @return The deserialized object.
*/

View File

@ -2,29 +2,116 @@ package com.onarandombox.MultiverseCore.worldnew.config;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.configuration.handle.ConfigurationSectionHandle;
import com.onarandombox.MultiverseCore.configuration.migration.BooleanMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.ConfigMigrator;
import com.onarandombox.MultiverseCore.configuration.migration.IntegerMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.LongMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.MoveMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.NullStringMigratorAction;
import com.onarandombox.MultiverseCore.configuration.migration.VersionMigrator;
import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import io.vavr.control.Try;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
public class WorldConfig {
/**
* Represents a world configuration.
*/
public final class WorldConfig {
private final String worldName;
private final WorldConfigNodes configNodes;
private final ConfigurationSectionHandle configHandle;
public WorldConfig(@NotNull final ConfigurationSection configSection) {
WorldConfig(@NotNull String worldName, @NotNull final ConfigurationSection configSection) {
this.worldName = worldName;
this.configNodes = new WorldConfigNodes();
// TODO: Config migration and version
this.configHandle = ConfigurationSectionHandle.builder(configSection)
.logger(Logging.getLogger())
.nodes(configNodes.getNodes())
.migrator(ConfigMigrator.builder(configNodes.VERSION)
.addVersionMigrator(initialVersionMigrator())
.build())
.build();
this.configHandle.load();
load();
}
private VersionMigrator initialVersionMigrator() {
return VersionMigrator.builder(1.0)
.addAction(MoveMigratorAction.of("adjustSpawn", "adjust-spawn"))
.addAction(BooleanMigratorAction.of("adjust-spawn"))
.addAction(MoveMigratorAction.of("allowFlight", "allow-flight"))
.addAction(BooleanMigratorAction.of("allow-flight"))
.addAction(MoveMigratorAction.of("allowWeather", "allow-weather"))
.addAction(BooleanMigratorAction.of("allow-weather"))
.addAction(MoveMigratorAction.of("autoHeal", "auto-heal"))
.addAction(BooleanMigratorAction.of("auto-heal"))
.addAction(MoveMigratorAction.of("autoLoad", "auto-load"))
.addAction(BooleanMigratorAction.of("auto-load"))
.addAction(MoveMigratorAction.of("bedRespawn", "bed-respawn"))
.addAction(BooleanMigratorAction.of("bed-respawn"))
//.addAction(MoveMigratorAction.of("difficulty", "difficulty"))
.addAction(MoveMigratorAction.of("entryfee.amount", "entry-fee.amount"))
.addAction(MoveMigratorAction.of("entryfee.currency", "entry-fee.currency"))
//.addAction(MoveMigratorAction.of("environment", "environment"))
.addAction(MoveMigratorAction.of("gameMode", "gamemode"))
//.addAction(MoveMigratorAction.of("generator", "generator"))
.addAction(NullStringMigratorAction.of("generator"))
//.addAction(MoveMigratorAction.of("hidden", "hidden"))
.addAction(BooleanMigratorAction.of("hidden"))
//.addAction(MoveMigratorAction.of("hunger", "hunger"))
.addAction(BooleanMigratorAction.of("hunger"))
.addAction(MoveMigratorAction.of("keepSpawnInMemory", "keep-spawn-in-memory"))
.addAction(BooleanMigratorAction.of("keep-spawn-in-memory"))
.addAction(MoveMigratorAction.of("playerLimit", "player-limit"))
.addAction(IntegerMigratorAction.of("player-limit"))
.addAction(MoveMigratorAction.of("portalForm", "portal-form"))
//.addAction(MoveMigratorAction.of("pvp", "pvp"))
.addAction(BooleanMigratorAction.of("pvp"))
.addAction(MoveMigratorAction.of("respawnWorld", "respawn-world"))
//.addAction(MoveMigratorAction.of("scale", "scale"))
//.addAction(MoveMigratorAction.of("seed", "seed"))
.addAction(LongMigratorAction.of("seed"))
.addAction(MoveMigratorAction.of("spawnLocation", "spawn-location"))
//.addAction(MoveMigratorAction.of("spawning.animals.spawn", "spawning.animals.spawn"))
.addAction(BooleanMigratorAction.of("spawning.animals.spawn"))
.addAction(MoveMigratorAction.of("spawning.animals.amount", "spawning.animals.tick-rate"))
.addAction(IntegerMigratorAction.of("spawning.animals.tick-rate"))
//.addAction(MoveMigratorAction.of("spawning.animals.exceptions", "spawning.animals.exceptions"))
//.addAction(MoveMigratorAction.of("spawning.monsters.spawn", "spawning.monsters.spawn"))
.addAction(BooleanMigratorAction.of("spawning.monsters.spawn"))
.addAction(MoveMigratorAction.of("spawning.monsters.amount", "spawning.monsters.tick-rate"))
.addAction(IntegerMigratorAction.of("spawning.monsters.tick-rate"))
//.addAction(MoveMigratorAction.of("spawning.monsters.exceptions", "spawning.monsters.exceptions"))
.addAction(MoveMigratorAction.of("worldBlacklist", "world-blacklist"))
.addAction(new LegacyAliasMigrator())
.build();
}
public Try<Void> load() {
return configHandle.load();
}
public Try<Void> load(ConfigurationSection section) {
return configHandle.load(section);
}
public String getWorldName() {
return worldName;
}
public Collection<String> getConfigurablePropertyNames() {
return configNodes.getNodes().getNames();
}
public Try<Object> getProperty(String name) {
@ -83,6 +170,14 @@ public class WorldConfig {
return configHandle.set(configNodes.AUTO_LOAD, autoLoad);
}
public boolean getBedRespawn() {
return configHandle.get(configNodes.BED_RESPAWN);
}
public Try<Void> setBedRespawn(boolean bedRespawn) {
return configHandle.set(configNodes.BED_RESPAWN, bedRespawn);
}
public Difficulty getDifficulty() {
return configHandle.get(configNodes.DIFFICULTY);
}
@ -91,6 +186,22 @@ public class WorldConfig {
return configHandle.set(configNodes.DIFFICULTY, difficulty);
}
public double getEntryFeeAmount() {
return configHandle.get(configNodes.ENTRY_FEE_AMOUNT);
}
public Try<Void> setEntryFeeAmount(double entryFeeAmount) {
return configHandle.set(configNodes.ENTRY_FEE_AMOUNT, entryFeeAmount);
}
public Material getEntryFeeCurrency() {
return configHandle.get(configNodes.ENTRY_FEE_CURRENCY);
}
public Try<Void> setEntryFeeCurrency(Material entryFeeCurrency) {
return configHandle.set(configNodes.ENTRY_FEE_CURRENCY, entryFeeCurrency);
}
public World.Environment getEnvironment() {
return configHandle.get(configNodes.ENVIRONMENT);
}
@ -99,11 +210,11 @@ public class WorldConfig {
return configHandle.set(configNodes.ENVIRONMENT, environment);
}
public GameMode getGamemode() {
public GameMode getGameMode() {
return configHandle.get(configNodes.GAMEMODE);
}
public Try<Void> setGamemode(GameMode gamemode) {
public Try<Void> setGameMode(GameMode gamemode) {
return configHandle.set(configNodes.GAMEMODE, gamemode);
}
@ -179,19 +290,87 @@ public class WorldConfig {
return configHandle.set(configNodes.SCALE, scale);
}
public @Nullable String getSeed() {
public long getSeed() {
return configHandle.get(configNodes.SEED);
}
public Try<Void> setSeed(String seed) {
public Try<Void> setSeed(long seed) {
return configHandle.set(configNodes.SEED, seed);
}
public List<String> getWorldBlacklist() {
return (List<String>) configHandle.get(configNodes.WORLD_BLACKLIST);
public Location getSpawnLocation() {
return configHandle.get(configNodes.SPAWN_LOCATION);
}
public void setWorldBlacklist(List<String> worldBlacklist) {
configHandle.set(configNodes.WORLD_BLACKLIST, worldBlacklist);
public Try<Void> setSpawnLocation(Location spawnLocation) {
return configHandle.set(configNodes.SPAWN_LOCATION, spawnLocation);
}
public boolean getSpawningAnimals() {
return configHandle.get(configNodes.SPAWNING_ANIMALS);
}
public Try<Void> setSpawningAnimals(boolean spawningAnimals) {
return configHandle.set(configNodes.SPAWNING_ANIMALS, spawningAnimals);
}
public int getSpawningAnimalsTicks() {
return configHandle.get(configNodes.SPAWNING_ANIMALS_TICKS);
}
public Try<Void> setSpawningAnimalsTicks(int spawningAnimalsAmount) {
return configHandle.set(configNodes.SPAWNING_ANIMALS_TICKS, spawningAnimalsAmount);
}
public List<String> getSpawningAnimalsExceptions() {
return configHandle.get(configNodes.SPAWNING_ANIMALS_EXCEPTIONS);
}
public Try<Void> setSpawningAnimalsExceptions(List<String> spawningAnimalsExceptions) {
return configHandle.set(configNodes.SPAWNING_ANIMALS_EXCEPTIONS, spawningAnimalsExceptions);
}
public boolean getSpawningMonsters() {
return configHandle.get(configNodes.SPAWNING_MONSTERS);
}
public Try<Void> setSpawningMonsters(boolean spawningMonsters) {
return configHandle.set(configNodes.SPAWNING_MONSTERS, spawningMonsters);
}
public int getSpawningMonstersTicks() {
return configHandle.get(configNodes.SPAWNING_MONSTERS_TICKS);
}
public Try<Void> setSpawningMonstersTicks(int spawningMonstersAmount) {
return configHandle.set(configNodes.SPAWNING_MONSTERS_TICKS, spawningMonstersAmount);
}
public List<String> getSpawningMonstersExceptions() {
return configHandle.get(configNodes.SPAWNING_MONSTERS_EXCEPTIONS);
}
public Try<Void> setSpawningMonstersExceptions(List<String> spawningMonstersExceptions) {
return configHandle.set(configNodes.SPAWNING_MONSTERS_EXCEPTIONS, spawningMonstersExceptions);
}
public List<String> getWorldBlacklist() {
return configHandle.get(configNodes.WORLD_BLACKLIST);
}
public Try<Void> setWorldBlacklist(List<String> worldBlacklist) {
return configHandle.set(configNodes.WORLD_BLACKLIST, worldBlacklist);
}
public void setMVWorld(@NotNull LoadedMultiverseWorld world) {
configNodes.world = world;
}
public boolean hasMVWorld() {
return configNodes.world != null;
}
public void deferenceMVWorld() {
configNodes.world = null;
}
}

View File

@ -4,15 +4,24 @@ import com.onarandombox.MultiverseCore.configuration.node.ConfigNode;
import com.onarandombox.MultiverseCore.configuration.node.Node;
import com.onarandombox.MultiverseCore.configuration.node.NodeGroup;
import com.onarandombox.MultiverseCore.world.configuration.AllowedPortalType;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import java.util.ArrayList;
import java.util.List;
/**
* Represents nodes in a world configuration.
*/
public class WorldConfigNodes {
static final double CONFIG_VERSION = 1.0;
private final NodeGroup nodes = new NodeGroup();
LoadedMultiverseWorld world = null;
WorldConfigNodes() {
}
@ -26,109 +35,195 @@ public class WorldConfigNodes {
return node;
}
public final ConfigNode<Boolean> ADJUST_SPAWN = node(ConfigNode.builder("adjust-spawn", Boolean.class)
final ConfigNode<Boolean> ADJUST_SPAWN = node(ConfigNode.builder("adjust-spawn", Boolean.class)
.defaultValue(false)
.name("adjust-spawn")
.build());
public final ConfigNode<String> ALIAS = node(ConfigNode.builder("alias", String.class)
final ConfigNode<String> ALIAS = node(ConfigNode.builder("alias", String.class)
.defaultValue("")
.name("alias")
.build());
public final ConfigNode<Boolean> ALLOW_FLIGHT = node(ConfigNode.builder("allow-flight", Boolean.class)
final ConfigNode<Boolean> ALLOW_FLIGHT = node(ConfigNode.builder("allow-flight", Boolean.class)
.defaultValue(false)
.name("allow-flight")
.build());
public final ConfigNode<Boolean> ALLOW_WEATHER = node(ConfigNode.builder("allow-weather", Boolean.class)
final ConfigNode<Boolean> ALLOW_WEATHER = node(ConfigNode.builder("allow-weather", Boolean.class)
.defaultValue(true)
.name("allow-weather")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> {
if (!world.isClearWeather() && !newValue) {
world.setThundering(false);
world.setStorm(false);
}
});
})
.build());
public final ConfigNode<Boolean> AUTO_HEAL = node(ConfigNode.builder("auto-heal", Boolean.class)
final ConfigNode<Boolean> AUTO_HEAL = node(ConfigNode.builder("auto-heal", Boolean.class)
.defaultValue(true)
.name("auto-heal")
.build());
public final ConfigNode<Boolean> AUTO_LOAD = node(ConfigNode.builder("auto-load", Boolean.class)
final ConfigNode<Boolean> AUTO_LOAD = node(ConfigNode.builder("auto-load", Boolean.class)
.defaultValue(true)
.name("auto-load")
.build());
public final ConfigNode<Difficulty> DIFFICULTY = node(ConfigNode.builder("difficulty", Difficulty.class)
final ConfigNode<Boolean> BED_RESPAWN = node(ConfigNode.builder("bed-respawn", Boolean.class)
.defaultValue(true)
.build());
final ConfigNode<Difficulty> DIFFICULTY = node(ConfigNode.builder("difficulty", Difficulty.class)
.defaultValue(Difficulty.NORMAL)
.name("difficulty")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setDifficulty(newValue));
})
.build());
public final ConfigNode<World.Environment> ENVIRONMENT = node(ConfigNode.builder("environment", World.Environment.class)
final ConfigNode<Double> ENTRY_FEE_AMOUNT = node(ConfigNode.builder("entry-fee.amount", Double.class)
.defaultValue(0.0)
.name("entryfee-amount")
.build());
final ConfigNode<Material> ENTRY_FEE_CURRENCY = node(ConfigNode.builder("entry-fee.currency", Material.class)
.defaultValue(Material.AIR) // TODO: Convert from material ID
.name("entryfee-currency")
.build());
final ConfigNode<World.Environment> ENVIRONMENT = node(ConfigNode
.builder("environment", World.Environment.class)
.defaultValue(World.Environment.NORMAL)
.name("environment")
.name(null)
.build());
public final ConfigNode<GameMode> GAMEMODE = node(ConfigNode.builder("gamemode", GameMode.class)
final ConfigNode<GameMode> GAMEMODE = node(ConfigNode.builder("gamemode", GameMode.class)
.defaultValue(GameMode.SURVIVAL)
.name("gamemode")
// TODO: Set all gamemodes of players in world to this gamemode
.build());
public final ConfigNode<String> GENERATOR = node(ConfigNode.builder("generator", String.class)
.defaultValue("")
.name("generator")
final ConfigNode<String> GENERATOR = node(ConfigNode.builder("generator", String.class)
.defaultValue("@error") // this should be set on world creation
.name(null)
.build());
public final ConfigNode<Boolean> HIDDEN = node(ConfigNode.builder("hidden", Boolean.class)
final ConfigNode<Boolean> HIDDEN = node(ConfigNode.builder("hidden", Boolean.class)
.defaultValue(false)
.name("hidden")
.build());
public final ConfigNode<Boolean> HUNGER = node(ConfigNode.builder("hunger", Boolean.class)
final ConfigNode<Boolean> HUNGER = node(ConfigNode.builder("hunger", Boolean.class)
.defaultValue(true)
.name("hunger")
.build());
public final ConfigNode<Boolean> KEEP_SPAWN_IN_MEMORY = node(ConfigNode.builder("keep-spawn-in-memory", Boolean.class)
final ConfigNode<Boolean> KEEP_SPAWN_IN_MEMORY = node(ConfigNode
.builder("keep-spawn-in-memory", Boolean.class)
.defaultValue(true)
.name("keep-spawn-in-memory")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setKeepSpawnInMemory(newValue));
})
.build());
public final ConfigNode<Integer> PLAYER_LIMIT = node(ConfigNode.builder("player-limit", Integer.class)
final ConfigNode<Integer> PLAYER_LIMIT = node(ConfigNode.builder("player-limit", Integer.class)
.defaultValue(-1)
.name("player-limit")
.build());
public final ConfigNode<AllowedPortalType> PORTAL_FORM = node(ConfigNode.builder("portal-form", AllowedPortalType.class)
final ConfigNode<AllowedPortalType> PORTAL_FORM = node(ConfigNode
.builder("portal-form", AllowedPortalType.class)
.defaultValue(AllowedPortalType.ALL)
.name("portal-form")
.build());
public final ConfigNode<Boolean> PVP = node(ConfigNode.builder("pvp", Boolean.class)
final ConfigNode<Boolean> PVP = node(ConfigNode.builder("pvp", Boolean.class)
.defaultValue(true)
.name("pvp")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setPVP(newValue));
})
.build());
public final ConfigNode<String> RESPAWN_WORLD = node(ConfigNode.builder("respawn-world", String.class)
final ConfigNode<String> RESPAWN_WORLD = node(ConfigNode.builder("respawn-world", String.class)
.defaultValue("")
.name("respawn-world")
.build());
public final ConfigNode<Double> SCALE = node(ConfigNode.builder("scale", Double.class)
final ConfigNode<Double> SCALE = node(ConfigNode.builder("scale", Double.class)
.defaultValue(1.0)
.name("scale")
.build());
public final ConfigNode<String> SEED = node(ConfigNode.builder("seed", String.class)
.defaultValue("")
.name("seed")
final ConfigNode<Long> SEED = node(ConfigNode.builder("seed", Long.class)
.defaultValue(Long.MIN_VALUE)
.name(null)
.build());
public final ConfigNode<List> WORLD_BLACKLIST = node(ConfigNode.builder("world-blacklist", List.class)
final ConfigNode<Location> SPAWN_LOCATION = node(ConfigNode.builder("spawn-location", Location.class)
.defaultValue(NullLocation.get())
.name(null)
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
if (newValue == null || newValue instanceof NullLocation) return;
world.getBukkitWorld().peek(bukkitWorld -> {
bukkitWorld.setSpawnLocation(newValue.getBlockX(), newValue.getBlockY(), newValue.getBlockZ());
newValue.setWorld(bukkitWorld);
});
})
.build());
final ConfigNode<Boolean> SPAWNING_ANIMALS = node(ConfigNode.builder("spawning.animals.spawn", Boolean.class)
.defaultValue(true)
.name("spawning-animals")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setSpawnFlags(world.getAllowMonsters(), newValue));
})
.build());
final ConfigNode<Integer> SPAWNING_ANIMALS_TICKS = node(ConfigNode
.builder("spawning.animals.tick-rate", Integer.class)
.defaultValue(-1)
.name("spawning-animals-ticks")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setTicksPerAnimalSpawns(newValue));
})
.build());
final ConfigNode<List> SPAWNING_ANIMALS_EXCEPTIONS = node(ConfigNode
.builder("spawning.animals.exceptions", List.class)
.defaultValue(new ArrayList<>())
.name("world-blacklist")
.name("spawning-animals-exceptions")
.build());
// TODO: Migrate color and style into alias
// TODO: spawning
// TODO: entryfee
// TODO: spawnLocation
// TODO: worldBlacklist
final ConfigNode<Boolean> SPAWNING_MONSTERS = node(ConfigNode
.builder("spawning.monsters.spawn", Boolean.class)
.defaultValue(true)
.name("spawning-monsters")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setSpawnFlags(newValue, world.getAllowAnimals()));
})
.build());
final ConfigNode<Integer> SPAWNING_MONSTERS_TICKS = node(ConfigNode
.builder("spawning.monsters.tick-rate", Integer.class)
.defaultValue(-1)
.name("spawning-monsters-ticks")
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
world.getBukkitWorld().peek(world -> world.setTicksPerMonsterSpawns(newValue));
})
.build());
final ConfigNode<List> SPAWNING_MONSTERS_EXCEPTIONS = node(ConfigNode
.builder("spawning.monsters.exceptions", List.class)
.defaultValue(new ArrayList<>())
.name("spawning-monsters-exceptions")
.build());
final ConfigNode<List> WORLD_BLACKLIST = node(ConfigNode.builder("world-blacklist", List.class)
.defaultValue(new ArrayList<>())
.build());
final ConfigNode<Double> VERSION = node(ConfigNode.builder("version", Double.class)
.defaultValue(CONFIG_VERSION)
.name(null)
.build());
}

View File

@ -1,59 +0,0 @@
package com.onarandombox.MultiverseCore.worldnew.config;
import com.onarandombox.MultiverseCore.MultiverseCore;
import jakarta.inject.Inject;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
@Service
public class WorldsConfigFile {
private static final String CONFIG_FILENAME = "worlds2.yml";
private final File worldConfigFile;
private YamlConfiguration worldConfig;
@Inject
public WorldsConfigFile(@NotNull MultiverseCore core) {
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
}
public void load() {
// TODO: Migration from old worlds.yml
worldConfig = YamlConfiguration.loadConfiguration(worldConfigFile);
}
public boolean isLoaded() {
return worldConfig != null;
}
public void save() {
try {
worldConfig.save(worldConfigFile);
} catch (IOException e) {
e.printStackTrace();
}
}
public Collection<String> getAllWorldsInConfig() {
return worldConfig.getKeys(false);
}
public ConfigurationSection getWorldConfigSection(String worldName) {
return worldConfig.isConfigurationSection(worldName)
? worldConfig.getConfigurationSection(worldName) : worldConfig.createSection(worldName);
}
public WorldConfig getWorldConfig(String worldName) {
return new WorldConfig(getWorldConfigSection(worldName));
}
public void deleteWorldConfigSection(String worldName) {
worldConfig.set(worldName, null);
}
}

View File

@ -0,0 +1,208 @@
package com.onarandombox.MultiverseCore.worldnew.config;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import io.vavr.Tuple2;
import io.vavr.control.Option;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Manages the worlds.yml file.
*/
@Service
public final class WorldsConfigManager {
private static final String CONFIG_FILENAME = "worlds2.yml"; // TODO: Rename to worlds.yml
private final Map<String, WorldConfig> worldConfigMap;
private final File worldConfigFile;
private YamlConfiguration worldsConfig;
@Inject
WorldsConfigManager(@NotNull MultiverseCore core) {
worldConfigMap = new HashMap<>();
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
}
/**
* Loads the worlds.yml file and creates a WorldConfig for each world in the file if it doesn't already exist.
*
* @return A tuple containing a list of the new WorldConfigs added and a list of the worlds removed from the config.
*/
public Try<Tuple2<List<WorldConfig>, List<String>>> load() {
return Try.of(() -> {
loadWorldYmlFile();
return parseNewAndRemovedWorlds();
});
}
/**
* Loads the worlds.yml file.
*
* @throws IOException If an error occurs while loading the file.
* @throws InvalidConfigurationException If the file is not a valid YAML file.
*/
private void loadWorldYmlFile() throws IOException, InvalidConfigurationException {
boolean exists = worldConfigFile.exists();
if (!exists && !worldConfigFile.createNewFile()) {
throw new IllegalStateException("Could not create worlds.yml config file");
}
if (exists) {
migrateRemoveOldConfigSerializable();
}
worldsConfig = new YamlConfiguration();
worldsConfig.load(worldConfigFile);
}
private void migrateRemoveOldConfigSerializable() {
Try.of(() -> Files.readString(worldConfigFile.toPath()))
.mapTry(configData -> {
if (!configData.contains("==: MVWorld")) {
throw new ConfigMigratedException();
}
return configData.replace(" ==: MVWorld\n", "")
.replace(" ==: MVSpawnSettings\n", "")
.replace(" ==: MVSpawnSubSettings\n", "")
.replace(" ==: MVEntryFee\n", "");
})
.andThenTry(configData -> Files.writeString(worldConfigFile.toPath(), configData))
.andThenTry(() -> {
YamlConfiguration config = YamlConfiguration.loadConfiguration(worldConfigFile);
List<ConfigurationSection> worlds = config.getConfigurationSection("worlds")
.getKeys(false)
.stream()
.map(worldName -> config.getConfigurationSection("worlds." + worldName))
.toList();
config.set("worlds", null);
for (ConfigurationSection world : worlds) {
config.createSection(world.getName(), world.getValues(true));
}
config.save(worldConfigFile);
})
.onFailure(e -> {
if (e instanceof ConfigMigratedException) {
Logging.fine("Config already migrated");
return;
}
Logging.warning("Failed to migrate old worlds.yml file: %s", e.getMessage());
e.printStackTrace();
});
}
/**
* Parses the worlds.yml file and creates a WorldConfig for each world in the file if it doesn't already exist.
*
* @return A tuple containing a list of the new WorldConfigs added and a list of the worlds removed from the config.
*/
private Tuple2<List<WorldConfig>, List<String>> parseNewAndRemovedWorlds() {
Set<String> allWorldsInConfig = worldsConfig.getKeys(false);
List<WorldConfig> newWorldsAdded = new ArrayList<>();
for (String worldName : allWorldsInConfig) {
getWorldConfig(worldName)
.peek(config -> config.load(getWorldConfigSection(worldName)))
.onEmpty(() -> {
WorldConfig newWorldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName));
worldConfigMap.put(worldName, newWorldConfig);
newWorldsAdded.add(newWorldConfig);
});
}
List<String> worldsRemoved = worldConfigMap.keySet().stream()
.filter(worldName -> !allWorldsInConfig.contains(worldName))
.toList();
for (String s : worldsRemoved) {
worldConfigMap.remove(s);
}
return new Tuple2<>(newWorldsAdded, worldsRemoved);
}
/**
* Whether the worlds.yml file has been loaded.
*
* @return Whether the worlds.yml file has been loaded.
*/
public boolean isLoaded() {
return worldsConfig != null;
}
/**
* Saves the worlds.yml file.
*
* @return Whether the save was successful or the error that occurred.
*/
public Try<Void> save() {
return Try.run(() -> worldsConfig.save(worldConfigFile));
}
/**
* Gets the {@link WorldConfig} instance of all worlds in the worlds.yml file.
*
* @param worldName The name of the world to check.
* @return The {@link WorldConfig} instance of the world, or empty option if it doesn't exist.
*/
public @NotNull Option<WorldConfig> getWorldConfig(@NotNull String worldName) {
return Option.of(worldConfigMap.get(worldName));
}
/**
* Add a new world to the worlds.yml file. If a world with the given name already exists, an exception is thrown.
*
* @param worldName The name of the world to add.
* @return The newly created {@link WorldConfig} instance.
*/
public @NotNull WorldConfig addWorldConfig(@NotNull String worldName) {
if (worldConfigMap.containsKey(worldName)) {
throw new IllegalArgumentException("WorldConfig for world " + worldName + " already exists.");
}
WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName));
worldConfigMap.put(worldName, worldConfig);
return worldConfig;
}
/**
* Deletes the world config for the given world.
*
* @param worldName The name of the world to delete.
*/
public void deleteWorldConfig(@NotNull String worldName) {
worldConfigMap.remove(worldName);
worldsConfig.set(worldName, null);
}
/**
* Gets the {@link ConfigurationSection} for the given world in the worlds.yml file. If the section doesn't exist,
* it is created.
*
* @param worldName The name of the world.
* @return The {@link ConfigurationSection} for the given world.
*/
private ConfigurationSection getWorldConfigSection(String worldName) {
return worldsConfig.isConfigurationSection(worldName)
? worldsConfig.getConfigurationSection(worldName) : worldsConfig.createSection(worldName);
}
private static final class ConfigMigratedException extends RuntimeException {
private ConfigMigratedException() {
super("Config migrated");
}
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew.config;

View File

@ -1,4 +1,4 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
package com.onarandombox.MultiverseCore.worldnew.entrycheck;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
@ -6,13 +6,22 @@ import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
/**
* Result of a world blacklist check.
*/
public class BlacklistResult {
/**
* Success reasons for a blacklist check.
*/
public enum Success implements SuccessReason {
UNKNOWN_FROM_WORLD,
BYPASSED_BLACKLISTED,
NOT_BLACKLISTED
}
/**
* Failure reasons for a blacklist check.
*/
public enum Failure implements FailureReason {
BLACKLISTED(MVCorei18n.ENTRYCHECK_BLACKLISTED);

View File

@ -1,5 +1,4 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
package com.onarandombox.MultiverseCore.worldnew.entrycheck;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
@ -7,7 +6,13 @@ import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
/**
* Result of a world entry fee check.
*/
public class EntryFeeResult {
/**
* Success reasons for an entry fee check.
*/
public enum Success implements SuccessReason {
FREE_ENTRY,
ENOUGH_MONEY,
@ -15,6 +20,9 @@ public class EntryFeeResult {
CONSOLE_OR_BLOCK_COMMAND_SENDER
}
/**
* Failure reasons for an entry fee check.
*/
public enum Failure implements FailureReason {
NOT_ENOUGH_MONEY(MVCorei18n.ENTRYCHECK_NOTENOUGHMONEY),
CANNOT_PAY_ENTRY_FEE(MVCorei18n.ENTRYCHECK_CANNOTPAYENTRYFEE);

View File

@ -1,4 +1,4 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
package com.onarandombox.MultiverseCore.worldnew.entrycheck;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
@ -6,13 +6,22 @@ import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
/**
* Result of a world player limit check.
*/
public class PlayerLimitResult {
/**
* Success reasons for a player limit check.
*/
public enum Success implements SuccessReason {
NO_PLAYERLIMIT,
WITHIN_PLAYERLIMIT,
BYPASS_PLAYERLIMIT
}
/**
* Failure reasons for a player limit check.
*/
public enum Failure implements FailureReason {
EXCEED_PLAYERLIMIT(MVCorei18n.ENTRYCHECK_EXCEEDPLAYERLIMIT);

View File

@ -1,4 +1,4 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
package com.onarandombox.MultiverseCore.worldnew.entrycheck;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
@ -6,12 +6,21 @@ import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
/**
* Result of a world access check.
*/
public class WorldAccessResult {
/**
* Success reasons for a world access check.
*/
public enum Success implements SuccessReason {
NO_ENFORCE_WORLD_ACCESS,
HAS_WORLD_ACCESS
}
/**
* Failure reasons for a world access check.
*/
public enum Failure implements FailureReason {
NO_WORLD_ACCESS(MVCorei18n.ENTRYCHECK_NOWORLDACCESS);

View File

@ -1,13 +1,15 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
package com.onarandombox.MultiverseCore.worldnew.entrycheck;
import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.permissions.CorePermissionsChecker;
import com.onarandombox.MultiverseCore.utils.result.Result;
import com.onarandombox.MultiverseCore.utils.result.ResultChain;
import com.onarandombox.MultiverseCore.world.configuration.EntryFee;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import com.onarandombox.MultiverseCore.worldnew.MultiverseWorld;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.BlockCommandSender;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
@ -15,8 +17,13 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import static com.onarandombox.MultiverseCore.utils.message.MessageReplacement.replace;
/**
* Checks if a player can enter a world.
*/
public class WorldEntryChecker {
private final @NotNull MVCoreConfig config;
private final @NotNull MVEconomist economist;
@ -24,23 +31,39 @@ public class WorldEntryChecker {
private final @NotNull CommandSender sender;
public WorldEntryChecker(
WorldEntryChecker(
@NotNull MVCoreConfig config,
@NotNull CorePermissionsChecker permissionsChecker,
@NotNull MVEconomist economist,
@NotNull CommandSender sender
) {
@NotNull CommandSender sender) {
this.config = config;
this.permissionsChecker = permissionsChecker;
this.economist = economist;
this.sender = sender;
}
public ResultChain canStayInWorld(@NotNull MVWorld world) {
return canEnterWorld(null, world);
/**
* Checks if the sender have access to be in the world.
*
* @param world The world to check.
* @return The result of the check.
*/
public ResultChain canStayInWorld(@NotNull LoadedMultiverseWorld world) {
return ResultChain.builder()
.then(() -> canAccessWorld(world))
.then(() -> isWithinPlayerLimit(world))
.build();
}
public ResultChain canEnterWorld(@Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) {
/**
* Checks if the sender can enter the given world.
*
* @param fromWorld The world the sender is coming from.
* @param toWorld The world the sender is going to.
* @return The result of the check.
*/
public ResultChain canEnterWorld(
@Nullable LoadedMultiverseWorld fromWorld, @NotNull LoadedMultiverseWorld toWorld) {
return ResultChain.builder()
.then(() -> canAccessWorld(toWorld))
.then(() -> isWithinPlayerLimit(toWorld))
@ -49,7 +72,13 @@ public class WorldEntryChecker {
.build();
}
public Result<WorldAccessResult.Success, WorldAccessResult.Failure> canAccessWorld(@NotNull MVWorld world) {
/**
* Checks if the sender can access the given world.
*
* @param world The world to check.
* @return The result of the check.
*/
public Result<WorldAccessResult.Success, WorldAccessResult.Failure> canAccessWorld(@NotNull MultiverseWorld world) {
if (!config.getEnforceAccess()) {
return Result.success(WorldAccessResult.Success.NO_ENFORCE_WORLD_ACCESS);
}
@ -58,7 +87,14 @@ public class WorldEntryChecker {
: Result.failure(WorldAccessResult.Failure.NO_WORLD_ACCESS);
}
public Result<PlayerLimitResult.Success, PlayerLimitResult.Failure> isWithinPlayerLimit(@NotNull MVWorld world) {
/**
* Checks if the sender is within the player limit of the given world.
*
* @param world The world to check.
* @return The result of the check.
*/
public Result<PlayerLimitResult.Success, PlayerLimitResult.Failure> isWithinPlayerLimit(
@NotNull LoadedMultiverseWorld world) {
final int playerLimit = world.getPlayerLimit();
if (playerLimit <= -1) {
return Result.success(PlayerLimitResult.Success.NO_PLAYERLIMIT);
@ -66,12 +102,23 @@ public class WorldEntryChecker {
if (permissionsChecker.hasPlayerLimitBypassPermission(sender, world)) {
return Result.success(PlayerLimitResult.Success.BYPASS_PLAYERLIMIT);
}
return playerLimit > world.getCBWorld().getPlayers().size()
int numberOfPlayersInWorld = world.getBukkitWorld().map(World::getPlayers)
.map(Collection::size)
.getOrElse(0);
return playerLimit > numberOfPlayersInWorld
? Result.success(PlayerLimitResult.Success.WITHIN_PLAYERLIMIT)
: Result.failure(PlayerLimitResult.Failure.EXCEED_PLAYERLIMIT);
}
public Result<BlacklistResult.Success, BlacklistResult.Failure> isNotBlacklisted(@Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) {
/**
* Checks if the sender is not blacklisted from the given world.
*
* @param fromWorld The world the sender is coming from.
* @param toWorld The world the sender is going to.
* @return The result of the check.
*/
public Result<BlacklistResult.Success, BlacklistResult.Failure> isNotBlacklisted(
@Nullable LoadedMultiverseWorld fromWorld, @NotNull LoadedMultiverseWorld toWorld) {
if (fromWorld == null) {
return Result.success(BlacklistResult.Success.UNKNOWN_FROM_WORLD);
}
@ -80,7 +127,13 @@ public class WorldEntryChecker {
: Result.success(BlacklistResult.Success.NOT_BLACKLISTED);
}
public Result<EntryFeeResult.Success, EntryFeeResult.Failure> canPayEntryFee(MVWorld world) {
/**
* Checks if the sender can pay the entry fee for the given world.
*
* @param world The world to check.
* @return The result of the check.
*/
public Result<EntryFeeResult.Success, EntryFeeResult.Failure> canPayEntryFee(LoadedMultiverseWorld world) {
double price = world.getPrice();
Material currency = world.getCurrency();
if (price == 0D && (currency == null || currency == EntryFee.DISABLED_MATERIAL)) {
@ -97,6 +150,8 @@ public class WorldEntryChecker {
}
return economist.isPlayerWealthyEnough(player, price, currency)
? Result.success(EntryFeeResult.Success.ENOUGH_MONEY)
: Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY, replace("{amount}").with("$##")); // TODO: Money formatting
: Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY,
replace("{amount}").with("$##"));
// TODO: Money formatting
}
}

View File

@ -1,4 +1,4 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
package com.onarandombox.MultiverseCore.worldnew.entrycheck;
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.economy.MVEconomist;
@ -19,8 +19,7 @@ public class WorldEntryCheckerProvider {
WorldEntryCheckerProvider(
@NotNull MVCoreConfig config,
@NotNull MVEconomist economist,
@NotNull CorePermissionsChecker permissionsChecker
) {
@NotNull CorePermissionsChecker permissionsChecker) {
this.config = config;
this.economist = economist;
this.permissionsChecker = permissionsChecker;

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew.entrycheck;

View File

@ -0,0 +1,65 @@
package com.onarandombox.MultiverseCore.worldnew.generators;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
/**
* <p>A generator API for Multiverse.</p>
*
* <p>Any generator plugin can register themselves to Multiverse. This will provide Multiverse with addition
* information about your generator plugin such as generator id suggestions, example usages and link to more
* info on your generator plugin.</p>
*/
public interface GeneratorPlugin {
/**
* <p>Suggest possible generator ids. To be used in command tab-completion.</p>
*
* <p>These suggestions can be static without relying on currentIdInput, or dynamically changed based
* on the currentIdInput.</p>
*
* @param currentIdInput The current state user input. This may be null or empty if user has not started
* any input for generator id.
* @return Collection of suggested generator ids.
*/
@NotNull Collection<String> suggestIds(@Nullable String currentIdInput);
/**
* <p>Gets command usages that users can try to generate a world with your generator plugin. Returning null means
* you do not wish to show any usage examples for your generator plugin.</p>
*
* <p>An example command: '/mv create myworld normal -g CoolGen:FunWorld'</p>
*
* <p>Notes on usage of this method:</p>
* <ul>
* <li>Feel free to have colors in your command usage, but not Multiverse won't parse color codes for you.</li>
* <li>Please include the starting slash '/' in your usage examples.</li>
* <li>We suggest keeping the usage to at most 5 examples.</li>
* <li>This should not be a full explanation on all your generator plugin, just basics usages to get people
* started. For full guide, you can use {@link #getInfoLink()} to direct users.</li>
* </ul>
*
* @return A collection of command usage examples.
*/
@Nullable Collection<String> getExampleUsages();
/**
* <p>Gets a link with more information on your generator plugin. Returning null means you do not wish to link
* users to any website related to your generator plugin.</p>
*
* <p>An example info: 'Click on https://www.amazinggenerator.io ;)'</p>
*
* <p>Some suggested places you can link to are: spigot resource page, github repo or your own plugin site.</p>
*
* @return Link to more info on your generator plugin.
*/
@Nullable String getInfoLink();
/**
* Gets the java plugin for this generator. In short, return your own generator plugin instance.
*
* @return The associated plugin for this generator.
*/
@NotNull String getPluginName();
}

View File

@ -0,0 +1,210 @@
package com.onarandombox.MultiverseCore.worldnew.generators;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import io.vavr.control.Try;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.onarandombox.MultiverseCore.utils.file.FileUtils.getBukkitConfig;
/**
* Parse the default world generators from the bukkit config and load any generator plugins.
* Helps in suggesting and validating generator strings.
*/
@Service
public class GeneratorProvider implements Listener {
private final Map<String, String> defaultGenerators;
private final Map<String, GeneratorPlugin> generatorPlugins;
@Inject
GeneratorProvider(@NotNull MultiverseCore multiverseCore) {
defaultGenerators = new HashMap<>();
generatorPlugins = new HashMap<>();
Bukkit.getPluginManager().registerEvents(this, multiverseCore);
loadDefaultWorldGenerators();
loadPluginGenerators();
}
/**
* Load the default world generators string from the bukkit config.
*/
private void loadDefaultWorldGenerators() {
File bukkitConfigFile = getBukkitConfig();
if (bukkitConfigFile == null) {
Logging.warning("Any default world generators will not be loaded!");
return;
}
FileConfiguration bukkitConfig = YamlConfiguration.loadConfiguration(bukkitConfigFile);
ConfigurationSection worldSection = bukkitConfig.getConfigurationSection("worlds");
if (worldSection != null) {
Set<String> keys = worldSection.getKeys(false);
keys.forEach(key -> defaultGenerators.put(key, bukkitConfig.getString("worlds." + key + ".generator", "")));
}
}
/**
* Find generator plugins from plugins loaded and register them.
*/
private void loadPluginGenerators() {
Arrays.stream(Bukkit.getPluginManager().getPlugins()).forEach(plugin -> {
if (testIsGeneratorPlugin(plugin)) {
registerGeneratorPlugin(new SimpleGeneratorPlugin(plugin.getName()));
}
});
}
/**
* Basic test if a plugin is a generator plugin.
*
* @param plugin The plugin to test.
* @return True if the plugin is a generator plugin, else false.
*/
private boolean testIsGeneratorPlugin(Plugin plugin) {
String worldName = Bukkit.getWorlds().stream().findFirst().map(World::getName).orElse("world");
return Try.of(() -> plugin.getDefaultWorldGenerator(worldName, "") != null)
.recover(IllegalArgumentException.class, true)
.recover(throwable -> {
Logging.warning("Plugin %s threw an exception when testing if it is a generator plugin!",
plugin.getName());
throwable.printStackTrace();
return false;
}).getOrElse(false);
}
/**
* Gets the default generator for a world from the bukkit.yml config.
*
* @param worldName The name of the world.
* @return The default generator string for the world, or null if none.
*/
public @Nullable String getDefaultGeneratorForWorld(String worldName) {
return defaultGenerators.getOrDefault(worldName, null);
}
/**
* Attempts to register a plugin as {@link SimpleGeneratorPlugin}.
*
* @param generatorPlugin The plugin to register.
* @return True if registered successfully, else false.
*/
public boolean registerGeneratorPlugin(@NotNull GeneratorPlugin generatorPlugin) {
var registeredGenerator = generatorPlugins.get(generatorPlugin.getPluginName());
if (registeredGenerator == null || registeredGenerator instanceof SimpleGeneratorPlugin) {
generatorPlugins.put(generatorPlugin.getPluginName(), generatorPlugin);
return true;
}
Logging.severe("Generator plugin with name %s is already registered!", generatorPlugin.getPluginName());
return false;
}
/**
* Unregisters a plugin.
*
* @param pluginName The plugin to unregister.
* @return True if the plugin was present and now unregistered, else false.
*/
public boolean unregisterGeneratorPlugin(@NotNull String pluginName) {
if (generatorPlugins.containsKey(pluginName)) {
generatorPlugins.remove(pluginName);
return true;
}
Logging.severe("Generator plugin with name %s is not registered!", pluginName);
return false;
}
/**
* Whether a plugin is registered as a generator plugin.
*
* @param pluginName The name of the plugin.
* @return True if the plugin is registered, else false.
*/
public boolean isGeneratorPluginRegistered(@NotNull String pluginName) {
return generatorPlugins.containsKey(pluginName);
}
/**
* Gets a generator plugin by name.
*
* @param pluginName The name of the plugin.
* @return The generator plugin, or null if not registered.
*/
public @Nullable GeneratorPlugin getGeneratorPlugin(@NotNull String pluginName) {
return generatorPlugins.get(pluginName);
}
/**
* Auto complete generator strings, used in command tab completion.
*
* @param currentInput The current input from the user.
* @return A collection of suggestions.
*/
public Collection<String> suggestGeneratorString(@Nullable String currentInput) {
String[] genSpilt = currentInput == null ? new String[0] : currentInput.split(":", 2);
List<String> suggestions = new ArrayList<>(generatorPlugins.keySet());
if (genSpilt.length < 2) {
return suggestions;
}
GeneratorPlugin generatorPlugin = generatorPlugins.get(genSpilt[0]);
if (generatorPlugin == null) {
return suggestions;
}
suggestions.addAll(generatorPlugin.suggestIds(genSpilt[1]).stream().map(id -> genSpilt[0] + ":" + id).toList());
return suggestions;
}
/**
* Listen to plugins enabled to see if they are generator plugins.
*
* @param event The plugin enable event.
*/
@EventHandler
private void onPluginEnable(PluginEnableEvent event) {
if (!testIsGeneratorPlugin(event.getPlugin())) {
Logging.finest("Plugin %s is not a generator plugin.", event.getPlugin().getName());
return;
}
if (!registerGeneratorPlugin(new SimpleGeneratorPlugin(event.getPlugin().getName()))) {
Logging.severe("Failed to register generator plugin %s!", event.getPlugin().getName());
}
}
/**
* Listen to plugins disabled to see if they are generator plugins. If so, unregister them.
*
* @param event The plugin disable event.
*/
@EventHandler
private void onPluginDisable(PluginDisableEvent event) {
if (!isGeneratorPluginRegistered(event.getPlugin().getName())) {
Logging.finest("Plugin %s is not a generator plugin.", event.getPlugin().getName());
return;
}
if (!unregisterGeneratorPlugin(event.getPlugin().getName())) {
Logging.severe("Failed to unregister generator plugin %s!", event.getPlugin().getName());
}
}
}

View File

@ -0,0 +1,51 @@
package com.onarandombox.MultiverseCore.worldnew.generators;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
/**
* A default implementation of {@link GeneratorPlugin} for those generator plugins that do not provide their own
* custom {@link GeneratorPlugin} implementation to Multiverse.
*/
public final class SimpleGeneratorPlugin implements GeneratorPlugin {
private final String pluginName;
SimpleGeneratorPlugin(@NotNull String pluginName) {
this.pluginName = pluginName;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull Collection<String> suggestIds(@Nullable String currentIdInput) {
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable Collection<String> getExampleUsages() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public @Nullable String getInfoLink() {
return null;
}
/**
* {@inheritDoc}
*/
@Override
public @NotNull String getPluginName() {
return pluginName;
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew.generators;

View File

@ -0,0 +1,160 @@
package com.onarandombox.MultiverseCore.worldnew.helpers;
import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import io.vavr.control.Try;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.jvnet.hk2.annotations.Service;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* A data store for storing and restoring data from an object.
*
* @param <T> The type of the object to store data from.
*/
@Service
public interface DataStore<T> {
/**
* Stores the data from the given object in this {@link DataStore} instance.
*
* @param object The object to copy data from.
* @return This {@link DataStore} instance.
*/
DataStore<T> copyFrom(T object);
/**
* Copies the data from this {@link DataStore} instance to the given object.
*
* @param object The object to paste data to.
* @return This {@link DataStore} instance.
*/
DataStore<T> pasteTo(T object);
/**
* A {@link DataStore} for storing and restoring game rules for a multiverse world.
*/
class GameRulesStore implements DataStore<LoadedMultiverseWorld> {
private Map<GameRule<?>, Object> gameRuleMap;
/**
* {@inheritDoc}
*/
@Override
public GameRulesStore copyFrom(LoadedMultiverseWorld world) {
this.gameRuleMap = new HashMap<>();
world.getBukkitWorld().peek(bukkitWorld -> {
Arrays.stream(GameRule.values()).forEach(gameRule -> {
Object value = bukkitWorld.getGameRuleValue(gameRule);
gameRuleMap.put(gameRule, value);
});
});
return this;
}
/**
* {@inheritDoc}
*/
@Override
public GameRulesStore pasteTo(LoadedMultiverseWorld world) {
if (gameRuleMap == null) {
return this;
}
world.getBukkitWorld().peek(bukkitWorld -> gameRuleMap.forEach((gameRule, value) -> {
setGameRuleValue(bukkitWorld, gameRule, value).onFailure(e -> {
Logging.warning("Failed to set game rule " + gameRule.getName() + " to " + value);
e.printStackTrace();
});
}));
return this;
}
private <T> Try<Void> setGameRuleValue(World world, GameRule<T> gameRule, Object value) {
return Try.run(() -> world.setGameRule(gameRule, (T) value));
}
}
/**
* A {@link DataStore} for storing and restoring world properties for a multiverse world.
*/
class WorldConfigStore implements DataStore<LoadedMultiverseWorld> {
private Map<String, Object> configMap;
/**
* {@inheritDoc}
*/
@Override
public WorldConfigStore copyFrom(LoadedMultiverseWorld world) {
this.configMap = new HashMap<>();
world.getConfigurablePropertyNames().forEach(name -> world.getProperty(name)
.peek(value -> configMap.put(name, value)).onFailure(e -> {
Logging.warning("Failed to get property " + name + " from world "
+ world.getName() + ": " + e.getMessage());
}));
return this;
}
/**
* {@inheritDoc}
*/
@Override
public WorldConfigStore pasteTo(LoadedMultiverseWorld world) {
if (configMap == null) {
return this;
}
configMap.forEach((name, value) -> {
world.setProperty(name, value).onFailure(e -> {
Logging.warning("Failed to set property %s to %s for world %s: %s",
name, value, world.getName(), e.getMessage());
});
});
return this;
}
}
/**
* A {@link DataStore} for storing and restoring world border properties for a multiverse world.
*/
class WorldBorderStore implements DataStore<LoadedMultiverseWorld> {
private double borderCenterX;
private double borderCenterZ;
private double borderDamageAmount;
private double borderDamageBuffer;
private double borderSize;
private int borderTimeRemaining;
/**
* {@inheritDoc}
*/
@Override
public WorldBorderStore copyFrom(LoadedMultiverseWorld world) {
world.getBukkitWorld().peek(bukkitWorld -> {
borderCenterX = bukkitWorld.getWorldBorder().getCenter().getX();
borderCenterZ = bukkitWorld.getWorldBorder().getCenter().getZ();
borderDamageAmount = bukkitWorld.getWorldBorder().getDamageAmount();
borderDamageBuffer = bukkitWorld.getWorldBorder().getDamageBuffer();
borderSize = bukkitWorld.getWorldBorder().getSize();
borderTimeRemaining = bukkitWorld.getWorldBorder().getWarningTime();
});
return this;
}
/**
* {@inheritDoc}
*/
@Override
public WorldBorderStore pasteTo(LoadedMultiverseWorld world) {
world.getBukkitWorld().peek(bukkitWorld -> {
bukkitWorld.getWorldBorder().setCenter(borderCenterX, borderCenterZ);
bukkitWorld.getWorldBorder().setDamageAmount(borderDamageAmount);
bukkitWorld.getWorldBorder().setDamageBuffer(borderDamageBuffer);
bukkitWorld.getWorldBorder().setSize(borderSize);
bukkitWorld.getWorldBorder().setWarningTime(borderTimeRemaining);
});
return this;
}
}
}

View File

@ -0,0 +1,43 @@
package com.onarandombox.MultiverseCore.worldnew.helpers;
import java.util.ArrayList;
import java.util.List;
/**
* A data transfer for storing and restoring data from multiple {@link DataStore} from one object to another.
*
* @param <T> The type of the object to transfer data from and to.
*/
public class DataTransfer<T> {
private final List<DataStore<T>> dataStores;
/**
* Creates a new {@link DataTransfer} instance.
*/
public DataTransfer() {
this.dataStores = new ArrayList<>();
}
/**
* Adds a {@link DataStore} to this {@link DataTransfer} instance.
*
* @param dataStore The {@link DataStore} to add.
* @param object The object to copy data from.
* @return This {@link DataTransfer} instance.
*/
public DataTransfer<T> addDataStore(DataStore<T> dataStore, T object) {
this.dataStores.add(dataStore.copyFrom(object));
return this;
}
/**
* Copies the data from all {@link DataStore} instances in this {@link DataTransfer} instance to the given object.
*
* @param object The object to paste data to.
* @return This {@link DataTransfer} instance.
*/
public DataTransfer<T> pasteAllTo(T object) {
this.dataStores.forEach(dataStore -> dataStore.pasteTo(object));
return this;
}
}

View File

@ -0,0 +1,145 @@
package com.onarandombox.MultiverseCore.worldnew.helpers;
import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Try;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
/**
* A helper class for manipulating files and folders.
*/
@Service
public class FilesManipulator {
/**
* Deletes the given folder completely.
*
* @param file The folder to delete.
* @return A {@link Try} that will contain {@code null} if the folder was deleted successfully, or an exception if
* the folder could not be deleted.
*/
public Try<Void> deleteFolder(File file) {
return deleteFolder(file.toPath());
}
/**
* Deletes the given folder completely.
*
* @param path The folder to delete.
* @return A {@link Try} that will contain {@code null} if the folder was deleted successfully, or an exception if
* the folder could not be deleted.
*/
public Try<Void> deleteFolder(Path path) {
try (Stream<Path> files = Files.walk(path)) {
files.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
return Try.success(null);
} catch (IOException e) {
Logging.severe("Failed to delete folder: " + path.toAbsolutePath());
e.printStackTrace();
return Try.failure(e);
}
}
/**
* Copies all the content of the given folder to the given target folder.
*
* @param sourceDir The folder to copy.
* @param targetDir The target folder to copy to.
* @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if
* the folder could not be copied.
*/
public Try<Void> copyFolder(File sourceDir, File targetDir) {
return copyFolder(sourceDir.toPath(), targetDir.toPath(), Collections.emptyList());
}
/**
* Copies most of the content of the given folder to the given target folder, except the list of excluded files
* specified.
*
* @param sourceDir The folder to copy.
* @param targetDir The target folder to copy to.
* @param excludeFiles The list of files to exclude from copying.
* @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if
*/
public Try<Void> copyFolder(File sourceDir, File targetDir, List<String> excludeFiles) {
return copyFolder(sourceDir.toPath(), targetDir.toPath(), excludeFiles);
}
/**
* Copies all the content of the given folder to the given target folder.
*
* @param sourceDir The folder to copy.
* @param targetDir The target folder to copy to.
* @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if
* the folder could not be copied.
*/
public Try<Void> copyFolder(Path sourceDir, Path targetDir) {
return copyFolder(sourceDir, targetDir, Collections.emptyList());
}
/**
* Copies most of the content of the given folder to the given target folder, except the list of excluded files
* specified.
*
* @param sourceDir The folder to copy.
* @param targetDir The target folder to copy to.
* @param excludeFiles The list of files to exclude from copying.
* @return A {@link Try} that will contain {@code null} if the folder was copied successfully, or an exception if
*/
public Try<Void> copyFolder(Path sourceDir, Path targetDir, List<String> excludeFiles) {
return Try.run(() -> Files.walkFileTree(sourceDir, new CopyDirFileVisitor(sourceDir, targetDir, excludeFiles)))
.onFailure(e -> {
Logging.severe("Failed to copy folder: " + sourceDir.toAbsolutePath());
e.printStackTrace();
});
}
private static final class CopyDirFileVisitor extends SimpleFileVisitor<Path> {
private final Path sourceDir;
private final Path targetDir;
private final List<String> excludeFiles;
private CopyDirFileVisitor(@NotNull Path sourceDir, @NotNull Path targetDir, @NotNull List<String> excludeFiles) {
this.sourceDir = sourceDir;
this.targetDir = targetDir;
this.excludeFiles = excludeFiles;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
Path newDir = targetDir.resolve(sourceDir.relativize(dir));
if (!Files.isDirectory(newDir)) {
Files.createDirectory(newDir);
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// Pass files that are set to ignore
if (excludeFiles.contains(file.getFileName().toString())) {
Logging.finest("Ignoring file: " + file.getFileName());
return FileVisitResult.CONTINUE;
}
// Copy the files
Path targetFile = targetDir.resolve(sourceDir.relativize(file));
Files.copy(file, targetFile, COPY_ATTRIBUTES);
return FileVisitResult.CONTINUE;
}
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew.helpers;

View File

@ -0,0 +1,118 @@
package com.onarandombox.MultiverseCore.worldnew.options;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import org.jetbrains.annotations.NotNull;
/**
* Options for customizing the cloning of a world.
*/
public final class CloneWorldOptions implements KeepWorldSettingsOptions {
/**
* Creates a new {@link CloneWorldOptions} instance with the given world.
*
* @param world The world to clone.
* @param newWorldName The name of the new world.
* @return A new {@link CloneWorldOptions} instance.
*/
public static CloneWorldOptions fromTo(LoadedMultiverseWorld world, String newWorldName) {
return new CloneWorldOptions(world, newWorldName);
}
private final LoadedMultiverseWorld world;
private final String newWorldName;
private boolean keepGameRule = true;
private boolean keepWorldConfig = true;
private boolean keepWorldBorder = true;
CloneWorldOptions(LoadedMultiverseWorld world, String newWorldName) {
this.world = world;
this.newWorldName = newWorldName;
}
/**
* Gets the world to clone.
*
* @return The world to clone.
*/
public LoadedMultiverseWorld world() {
return world;
}
/**
* Gets the name of the new world.
*
* @return The name of the new world.
*/
public String newWorldName() {
return newWorldName;
}
/**
* Sets whether to keep the game rule of the world during cloning.
*
* @param keepGameRuleInput Whether to keep the game rule of the world during cloning.
* @return This {@link CloneWorldOptions} instance.
*/
@Override
public @NotNull CloneWorldOptions keepGameRule(boolean keepGameRuleInput) {
this.keepGameRule = keepGameRuleInput;
return this;
}
/**
* Gets whether to keep the game rule of the world during cloning.
*
* @return Whether to keep the game rule of the world during cloning.
*/
@Override
public boolean keepGameRule() {
return keepGameRule;
}
/**
* Sets whether to keep the world config of the world during cloning.
*
* @param keepWorldConfigInput Whether to keep the world config of the world.
* @return This {@link CloneWorldOptions} instance.
*/
@Override
public @NotNull CloneWorldOptions keepWorldConfig(boolean keepWorldConfigInput) {
this.keepWorldConfig = keepWorldConfigInput;
return this;
}
/**
* Gets whether to keep the world config of the world during cloning.
*
* @return Whether to keep the world config of the world during cloning.
*/
@Override
public boolean keepWorldConfig() {
return keepWorldConfig;
}
/**
* Sets whether to keep the world border of the world during cloning.
*
* @param keepWorldBorderInput Whether to keep the world border of the world.
* @return This {@link CloneWorldOptions} instance.
*/
@Override
public @NotNull CloneWorldOptions keepWorldBorder(boolean keepWorldBorderInput) {
this.keepWorldBorder = keepWorldBorderInput;
return this;
}
/**
* Gets whether to keep the world border of the world during cloning.
*
* @return Whether to keep the world border of the world during cloning.
*/
@Override
public boolean keepWorldBorder() {
return keepWorldBorder;
}
}

View File

@ -0,0 +1,190 @@
package com.onarandombox.MultiverseCore.worldnew.options;
import org.bukkit.World;
import org.bukkit.WorldType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Random;
/**
* Options for customizing the creation of a new world.
*/
public class CreateWorldOptions {
/**
* Creates a new {@link CreateWorldOptions} instance with the given world name.
*
* @param worldName The name of the world to create.
* @return A new {@link CreateWorldOptions} instance.
*/
public static @NotNull CreateWorldOptions worldName(@NotNull String worldName) {
return new CreateWorldOptions(worldName);
}
private final String worldName;
private World.Environment environment = World.Environment.NORMAL;
private boolean generateStructures = true;
private String generator = null;
private long seed;
private boolean useSpawnAdjust = true;
private WorldType worldType = WorldType.NORMAL;
/**
* Creates a new {@link CreateWorldOptions} instance with the given world name.
*
* @param worldName The name of the world to create.
*/
CreateWorldOptions(@NotNull String worldName) {
this.worldName = worldName;
this.seed = (new Random()).nextLong();
}
/**
* Gets the name of the world to create.
*
* @return The name of the world to create.
*/
public @NotNull String worldName() {
return worldName;
}
/**
* Sets the environment of the world to create.
*
* @param environmentInput The environment of the world to create.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions environment(@NotNull World.Environment environmentInput) {
this.environment = environmentInput;
return this;
}
/**
* Gets the environment of the world to create.
*
* @return The environment of the world to create.
*/
public @NotNull World.Environment environment() {
return environment;
}
/**
* Sets whether structures such as NPC villages should be generated.
*
* @param generateStructuresInput Whether structures such as NPC villages should be generated.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions generateStructures(boolean generateStructuresInput) {
this.generateStructures = generateStructuresInput;
return this;
}
/**
* Gets whether structures such as NPC villages should be generated.
*
* @return Whether structures such as NPC villages should be generated.
*/
public boolean generateStructures() {
return generateStructures;
}
/**
* Sets the custom generator plugin and its parameters.
*
* @param generatorInput The custom generator plugin and its parameters.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions generator(@Nullable String generatorInput) {
this.generator = generatorInput;
return this;
}
/**
* Gets the custom generator plugin and its parameters.
*
* @return The custom generator plugin and its parameters.
*/
public @Nullable String generator() {
return generator;
}
/**
* Sets the seed of the world to create. If the seed is a number, it will be parsed as a long. Otherwise, it will be
* hashed.
*
* @param seedInput The seed of the world to create.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions seed(@Nullable String seedInput) {
if (seedInput == null) {
return this;
}
try {
this.seed = Long.parseLong(seedInput);
} catch (NumberFormatException numberformatexception) {
this.seed = seedInput.hashCode();
}
return this;
}
/**
* Sets the seed of the world to create.
*
* @param seedInput The seed of the world to create.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions seed(long seedInput) {
this.seed = seedInput;
return this;
}
/**
* Gets the seed of the world to create.
*
* @return The seed of the world to create.
*/
public long seed() {
return seed;
}
/**
* Sets whether multiverse will search for a safe spawn location.
*
* @param useSpawnAdjustInput Whether multiverse will search for a safe spawn location.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions useSpawnAdjust(boolean useSpawnAdjustInput) {
this.useSpawnAdjust = useSpawnAdjustInput;
return this;
}
/**
* Gets whether multiverse will search for a safe spawn location.
*
* @return Whether multiverse will search for a safe spawn location.
*/
public boolean useSpawnAdjust() {
return useSpawnAdjust;
}
/**
* Sets the world type.
*
* @param worldTypeInput The world type.
* @return This {@link CreateWorldOptions} instance.
*/
public @NotNull CreateWorldOptions worldType(@NotNull WorldType worldTypeInput) {
this.worldType = worldTypeInput;
return this;
}
/**
* Gets the world type.
*
* @return The world type.
*/
public @NotNull WorldType worldType() {
return worldType;
}
}

View File

@ -0,0 +1,99 @@
package com.onarandombox.MultiverseCore.worldnew.options;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Options for customizing the import of a new world.
*/
public class ImportWorldOptions {
/**
* Creates a new {@link ImportWorldOptions} instance with the given world name.
*
* @param worldName The name of the world to create.
* @return A new {@link ImportWorldOptions} instance.
*/
public static @NotNull ImportWorldOptions worldName(@NotNull String worldName) {
return new ImportWorldOptions(worldName);
}
private final String worldName;
private World.Environment environment = World.Environment.NORMAL;
private String generator = null;
private boolean useSpawnAdjust = true;
ImportWorldOptions(String worldName) {
this.worldName = worldName;
}
/**
* Gets the name of the world to create.
*
* @return The name of the world to create.
*/
public @NotNull String worldName() {
return worldName;
}
/**
* Sets the environment of the world to create.
*
* @param environmentInput The environment of the world to create.
* @return This {@link ImportWorldOptions} instance.
*/
public @NotNull ImportWorldOptions environment(@NotNull World.Environment environmentInput) {
this.environment = environmentInput;
return this;
}
/**
* Gets the environment of the world to create.
*
* @return The environment of the world to create.
*/
public @NotNull World.Environment environment() {
return environment;
}
/**
* Sets the custom generator plugin and its parameters.
*
* @param generatorInput The custom generator plugin and its parameters.
* @return This {@link ImportWorldOptions} instance.
*/
public @NotNull ImportWorldOptions generator(@Nullable String generatorInput) {
this.generator = generatorInput;
return this;
}
/**
* Gets the custom generator plugin and its parameters.
*
* @return The custom generator plugin and its parameters.
*/
public @Nullable String generator() {
return generator;
}
/**
* Sets whether multiverse will search for a safe spawn location.
*
* @param useSpawnAdjustInput Whether multiverse will search for a safe spawn location.
* @return This {@link ImportWorldOptions} instance.
*/
public @NotNull ImportWorldOptions useSpawnAdjust(boolean useSpawnAdjustInput) {
this.useSpawnAdjust = useSpawnAdjustInput;
return this;
}
/**
* Gets whether multiverse will search for a safe spawn location.
*
* @return Whether multiverse will search for a safe spawn location.
*/
public boolean useSpawnAdjust() {
return useSpawnAdjust;
}
}

View File

@ -0,0 +1,54 @@
package com.onarandombox.MultiverseCore.worldnew.options;
import org.jetbrains.annotations.NotNull;
/**
* Options for customizing the keeping of world settings. Used by clone and regen.
*/
public sealed interface KeepWorldSettingsOptions permits CloneWorldOptions, RegenWorldOptions {
/**
* Sets whether to keep the game rule of the world.
*
* @param keepGameRuleInput Whether to keep the game rule of the world.
* @return This {@link KeepWorldSettingsOptions} instance.
*/
@NotNull KeepWorldSettingsOptions keepGameRule(boolean keepGameRuleInput);
/**
* Gets whether to keep the game rule of the world.
*
* @return Whether to keep the game rule of the world.
*/
boolean keepGameRule();
/**
* Sets whether to keep the world config of the world.
*
* @param keepWorldConfigInput Whether to keep the world config of the world.
* @return This {@link KeepWorldSettingsOptions} instance.
*/
@NotNull KeepWorldSettingsOptions keepWorldConfig(boolean keepWorldConfigInput);
/**
* Gets whether to keep the world config of the world.
*
* @return Whether to keep the world config of the world.
*/
boolean keepWorldConfig();
/**
* Sets whether to keep the world border of the world.
*
* @param keepWorldBorderInput Whether to keep the world border of the world.
* @return This {@link KeepWorldSettingsOptions} instance.
*/
@NotNull KeepWorldSettingsOptions keepWorldBorder(boolean keepWorldBorderInput);
/**
* Gets whether to keep the world border of the world.
*
* @return Whether to keep the world border of the world.
*/
boolean keepWorldBorder();
}

View File

@ -0,0 +1,180 @@
package com.onarandombox.MultiverseCore.worldnew.options;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Random;
/**
* Options for customizing the regeneration of a world.
*/
public final class RegenWorldOptions implements KeepWorldSettingsOptions {
/**
* Creates a new {@link RegenWorldOptions} instance with the given world.
*
* @param world The world to regenerate.
* @return A new {@link RegenWorldOptions} instance.
*/
public static @NotNull RegenWorldOptions world(@NotNull LoadedMultiverseWorld world) {
return new RegenWorldOptions(world);
}
private final LoadedMultiverseWorld world;
private boolean keepGameRule = true;
private boolean keepWorldConfig = true;
private boolean keepWorldBorder = true;
private boolean randomSeed = false;
private long seed = Long.MIN_VALUE;
RegenWorldOptions(@NotNull LoadedMultiverseWorld world) {
this.world = world;
}
/**
* Gets the world to regenerate.
*
* @return The world to regenerate.
*/
public @NotNull LoadedMultiverseWorld world() {
return world;
}
/**
* Sets whether to keep the game rule of the world during regeneration.
*
* @param keepGameRuleInput Whether to keep the game rule of the world during regeneration.
* @return This {@link RegenWorldOptions} instance.
*/
@Override
public @NotNull RegenWorldOptions keepGameRule(boolean keepGameRuleInput) {
this.keepGameRule = keepGameRuleInput;
return this;
}
/**
* Gets whether to keep the game rule of the world during regeneration.
*
* @return Whether to keep the game rule of the world during regeneration.
*/
@Override
public boolean keepGameRule() {
return keepGameRule;
}
/**
* Sets whether to keep the world config of the world during regeneration.
*
* @param keepWorldConfigInput Whether to keep the world config of the world during regeneration.
* @return This {@link RegenWorldOptions} instance.
*/
@Override
public @NotNull RegenWorldOptions keepWorldConfig(boolean keepWorldConfigInput) {
this.keepWorldConfig = keepWorldConfigInput;
return this;
}
/**
* Gets whether to keep the world config of the world during regeneration.
*
* @return Whether to keep the world config of the world during regeneration.
*/
@Override
public boolean keepWorldConfig() {
return keepWorldConfig;
}
/**
* Sets whether to keep the world border of the world during regeneration.
*
* @param keepWorldBorderInput Whether to keep the world border of the world.
* @return This {@link RegenWorldOptions} instance.
*/
@Override
public @NotNull RegenWorldOptions keepWorldBorder(boolean keepWorldBorderInput) {
this.keepWorldBorder = keepWorldBorderInput;
return this;
}
/**
* Gets whether to keep the world border of the world during regeneration.
*
* @return Whether to keep the world border of the world during regeneration.
*/
@Override
public boolean keepWorldBorder() {
return keepWorldBorder;
}
/**
* Sets whether to use a random seed for the world to regenerate. Cannot be set to true when seed is set.
*
* @param randomSeedInput Whether to use a random seed for the world to regenerate.
* @return This {@link RegenWorldOptions} instance.
*/
public @NotNull RegenWorldOptions randomSeed(boolean randomSeedInput) {
if (randomSeedInput && seed != Long.MIN_VALUE) {
throw new IllegalStateException("Cannot set randomSeed to true when seed is set");
}
this.randomSeed = randomSeedInput;
return this;
}
/**
* Gets whether to use a random seed for the world to regenerate.
*
* @return Whether to use a random seed for the world to regenerate.
*/
public boolean randomSeed() {
return randomSeed;
}
/**
* Sets the seed for the world to regenerate. Random seed will be disabled.
*
* @param seedInput The seed for the world to regenerate.
* @return This {@link RegenWorldOptions} instance.
*/
public @NotNull RegenWorldOptions seed(@Nullable String seedInput) {
if (seedInput == null) {
this.seed = Long.MIN_VALUE;
return this;
}
if (randomSeed) {
randomSeed(false);
}
try {
this.seed = Long.parseLong(seedInput);
} catch (NumberFormatException numberformatexception) {
this.seed = seedInput.hashCode();
}
return this;
}
/**
* Sets the seed for the world to regenerate. Random seed will be disabled.
*
* @param seedInput The seed for the world to regenerate.
* @return This {@link RegenWorldOptions} instance.
*/
public @NotNull RegenWorldOptions seed(long seedInput) {
this.seed = seedInput;
return this;
}
/**
* Gets the seed for the world to regenerate.
*
* @return The seed for the world to regenerate.
*/
public long seed() {
if (randomSeed) {
return new Random().nextLong();
} else if (seed == Long.MIN_VALUE) {
return world.getSeed();
}
return seed;
}
}

View File

@ -0,0 +1,76 @@
package com.onarandombox.MultiverseCore.worldnew.options;
import com.onarandombox.MultiverseCore.worldnew.LoadedMultiverseWorld;
/**
* Options for customizing the unloading of a world.
*/
public class UnloadWorldOptions {
/**
* Creates a new {@link UnloadWorldOptions} instance with the given world.
*
* @param world The world to unload.
* @return A new {@link UnloadWorldOptions} instance.
*/
public static UnloadWorldOptions world(LoadedMultiverseWorld world) {
return new UnloadWorldOptions(world);
}
private final LoadedMultiverseWorld world;
private boolean removePlayers = false;
private boolean saveBukkitWorld = true;
UnloadWorldOptions(LoadedMultiverseWorld world) {
this.world = world;
}
/**
* Gets the world to unload.
*
* @return The world to unload.
*/
public LoadedMultiverseWorld world() {
return world;
}
/**
* Sets whether to teleport the players out from the world before unloading.
*
* @param removePlayersInput Whether to remove players from the world before unloading.
* @return This {@link UnloadWorldOptions} instance.
*/
public UnloadWorldOptions removePlayers(boolean removePlayersInput) {
this.removePlayers = removePlayersInput;
return this;
}
/**
* Gets whether to teleport the players out from the world before unloading.
*
* @return Whether to remove players from the world before unloading.
*/
public boolean removePlayers() {
return removePlayers;
}
/**
* Sets whether to save the bukkit world before unloading.
*
* @param saveBukkitWorldInput Whether to save the bukkit world before unloading.
* @return This {@link UnloadWorldOptions} instance.
*/
public UnloadWorldOptions saveBukkitWorld(boolean saveBukkitWorldInput) {
this.saveBukkitWorld = saveBukkitWorldInput;
return this;
}
/**
* Gets whether to save the bukkit world before unloading.
*
* @return Whether to save the bukkit world before unloading.
*/
public boolean saveBukkitWorld() {
return saveBukkitWorld;
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew.options;

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew;

View File

@ -0,0 +1,52 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world clone operation.
*/
public enum CloneFailureReason implements FailureReason {
/**
* The world name is invalid.
*/
INVALID_WORLDNAME(MVCorei18n.CLONEWORLD_INVALIDWORLDNAME),
/**
* The target new world folder already exists.
*/
WORLD_EXIST_FOLDER(MVCorei18n.CLONEWORLD_WORLDEXISTFOLDER),
/**
* The target new world is already exist but unloaded.
*/
WORLD_EXIST_UNLOADED(MVCorei18n.CLONEWORLD_WORLDEXISTUNLOADED),
/**
* The target new world is already loaded.
*/
WORLD_EXIST_LOADED(MVCorei18n.CLONEWORLD_WORLDEXISTLOADED),
/**
* Failed to copy the world folder contents.
*/
COPY_FAILED(MVCorei18n.CLONEWORLD_COPYFAILED),
/**
* Failed to import the new world.
*/
IMPORT_FAILED(MVCorei18n.GENERIC_FAILURE);
private final MessageKeyProvider message;
CloneFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,47 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world creation operation.
*/
public enum CreateFailureReason implements FailureReason {
/**
* The world name is invalid.
*/
INVALID_WORLDNAME(MVCorei18n.CREATEWORLD_INVALIDWORLDNAME),
/**
* The target new world folder already exists.
*/
WORLD_EXIST_FOLDER(MVCorei18n.CREATEWORLD_WORLDEXISTFOLDER),
/**
* The target new world is already exist but unloaded.
*/
WORLD_EXIST_UNLOADED(MVCorei18n.CREATEWORLD_WORLDEXISTUNLOADED),
/**
* The target new world is already exist and loaded.
*/
WORLD_EXIST_LOADED(MVCorei18n.CREATEWORLD_WORLDEXISTLOADED),
/**
* Bukkit API failed to create the world.
*/
BUKKIT_CREATION_FAILED(MVCorei18n.CREATEWORLD_BUKKITCREATIONFAILED);
private final MessageKeyProvider message;
CreateFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,47 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world deletion operation.
*/
public enum DeleteFailureReason implements FailureReason {
/**
* The world does not exist.
*/
WORLD_NON_EXISTENT(MVCorei18n.DELETEWORLD_WORLDNONEXISTENT),
/**
* The world could not be loaded.
*/
LOAD_FAILED(MVCorei18n.DELETEWORLD_LOADFAILED),
/**
* The world could not be unloaded.
*/
WORLD_FOLDER_NOT_FOUND(MVCorei18n.DELETEWORLD_WORLDFOLDERNOTFOUND),
/**
* The world could not be removed.
*/
REMOVE_FAILED(MVCorei18n.GENERIC_FAILURE),
/**
* The world folder could not be deleted.
*/
FAILED_TO_DELETE_FOLDER(MVCorei18n.DELETEWORLD_FAILEDTODELETEFOLDER);
private final MessageKeyProvider message;
DeleteFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,47 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world import operation.
*/
public enum ImportFailureReason implements FailureReason {
/**
* The world name is invalid.
*/
INVALID_WORLDNAME(MVCorei18n.IMPORTWORLD_INVALIDWORLDNAME),
/**
* The world folder is invalid.
*/
WORLD_FOLDER_INVALID(MVCorei18n.IMPORTWORLD_WORLDFOLDERINVALID),
/**
* The target world folder already exists. You should load it instead.
*/
WORLD_EXIST_UNLOADED(MVCorei18n.IMPORTWORLD_WORLDEXISTUNLOADED),
/**
* The target world is already exist and loaded.
*/
WORLD_EXIST_LOADED(MVCorei18n.IMPORTWORLD_WORLDEXISTLOADED),
/**
* Bukkit API failed to create the world.
*/
BUKKIT_CREATION_FAILED(MVCorei18n.IMPORTWORLD_BUKKITCREATIONFAILED);
private final MessageKeyProvider message;
ImportFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,47 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world loading operation.
*/
public enum LoadFailureReason implements FailureReason {
/**
* Loading operation is underway.
*/
WORLD_ALREADY_LOADING(MVCorei18n.LOADWORLD_WORLDALREADYLOADING),
/**
* The world does not exist.
*/
WORLD_NON_EXISTENT(MVCorei18n.LOADWORLD_WORLDNONEXISTENT),
/**
* The world folder exists but is not known to Multiverse.
*/
WORLD_EXIST_FOLDER(MVCorei18n.LOADWORLD_WORLDEXISTFOLDER),
/**
* The world is already loaded.
*/
WORLD_EXIST_LOADED(MVCorei18n.LOADWORLD_WORLDEXISTLOADED),
/**
* Bukkit API failed to create the world.
*/
BUKKIT_CREATION_FAILED(MVCorei18n.LOADWORLD_BUKKITCREATIONFAILED);
private final MessageKeyProvider message;
LoadFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,32 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world regeneration operation.
*/
public enum RegenFailureReason implements FailureReason {
/**
* The world does not exist.
*/
DELETE_FAILED(MVCorei18n.GENERIC_FAILURE),
/**
* The new world could not be created.
*/
CREATE_FAILED(MVCorei18n.GENERIC_FAILURE);
private final MessageKeyProvider message;
RegenFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,32 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world removal operation.
*/
public enum RemoveFailureReason implements FailureReason {
/**
* The world does not exist.
*/
WORLD_NON_EXISTENT(MVCorei18n.REMOVEWORLD_WORLDNONEXISTENT),
/**
* The world could not be unloaded.
*/
UNLOAD_FAILED(MVCorei18n.GENERIC_FAILURE);
private final MessageKeyProvider message;
RemoveFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1,42 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;
import co.aikar.locales.MessageKey;
import co.aikar.locales.MessageKeyProvider;
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
/**
* Result of a world unloading operation.
*/
public enum UnloadFailureReason implements FailureReason {
/**
* Unloading operation is underway.
*/
WORLD_ALREADY_UNLOADING(MVCorei18n.UNLOADWORLD_WORLDALREADYUNLOADING),
/**
* The world does not exist.
*/
WORLD_NON_EXISTENT(MVCorei18n.UNLOADWORLD_WORLDNONEXISTENT),
/**
* The world is already unloaded.
*/
WORLD_UNLOADED(MVCorei18n.UNLOADWORLD_WORLDUNLOADED),
/**
* Bukkit API failed to unload the world.
*/
BUKKIT_UNLOAD_FAILED(MVCorei18n.UNLOADWORLD_BUKKITUNLOADFAILED);
private final MessageKeyProvider message;
UnloadFailureReason(MessageKeyProvider message) {
this.message = message;
}
@Override
public MessageKey getMessageKey() {
return message.getMessageKey();
}
}

View File

@ -0,0 +1 @@
package com.onarandombox.MultiverseCore.worldnew.reasons;

View File

@ -13,8 +13,7 @@ mv-core.clone.description=Clones a world.
mv-core.clone.world.description=The target world to clone.
mv-core.clone.newWorld.description=The new cloned world name.
mv-core.clone.cloning=Cloning world '{world}' to '{newworld}'...
mv-core.clone.failed=World could not be cloned! &fSee console for more details.
mv-core.clone.success=Cloned world '{world}'!
mv-core.clone.success=&aWorld cloned to '{world}'!
# /mv confirm
mv-core.confirm.description=Confirms dangerous commands before executing them.
@ -41,8 +40,7 @@ mv-core.create.properties.adjustspawn=- Adjust Spawn: &f{adjustSpawn}
mv-core.create.properties.generator=- Generator: &f{generator}
mv-core.create.properties.structures=- Structures: &f{structures}
mv-core.create.loading=Creating world...
mv-core.create.failed=Failed to create world '{worldName}'! &fSee console for details.
mv-core.create.success=&aWorld '{worldName}' created successfully!
mv-core.create.success=&aWorld '{world}' created!
# /mv debug
mv-core.debug.info.description=Show the current debug level.
@ -55,9 +53,8 @@ mv-core.debug.change.level.description=Debug level to set to.
# /mv delete
mv-core.delete.description=Deletes a world on your server PERMANENTLY.
mv-core.delete.deleting=Deleting world '{world}'...
mv-core.delete.failed=There was an issue deleting '{world}'! &fPlease check console for errors.
mv-core.delete.success=&aWorld {world} was deleted!
mv-core.delete.prompt=Are you sure you want to delete world '{world}'?
mv-core.delete.success=&aWorld '{world}' deleted!
# /mv dumps
mv-core.dumps.description=Dumps version info to the console or paste services
@ -84,24 +81,21 @@ mv-core.import.name.description=Name of the world folder.
mv-core.import.env.description=The world's environment. See: /mv env
mv-core.import.other.description=Other world settings. See: https://gg.gg/nn8c2
mv-core.import.importing=Starting import of world '{world}'...
mv-core.import.failed=Failed! &fSee console for more details.
mv-core.import.success=&aComplete!
mv-core.import.success=&aWorld '{world}' imported!
# /mv load
mv-core.load.description=Loads a world. World must be already in worlds.yml, else please use /mv import.
mv-core.load.world.description=Name of world you want to load.
mv-core.load.loading=Loading world '{world}'...
mv-core.load.failed=Error trying to load world '{world}'!
mv-core.load.success=&aLoaded world '{world}'!
mv-core.load.success=&aWorld '{world}' loaded!
# /mv regen
mv-core.regen.description=Regenerates a world on your server. The previous state will be lost PERMANENTLY.
mv-core.regen.world.description=World that you want to regen.
mv-core.regen.other.description=Other world settings. See: http://gg.gg/nn8lk
mv-core.regen.regenerating=Regenerating world '{world}'...
mv-core.regen.failed=There was an issue regenerating '{world}'! &fPlease check console for errors.
mv-core.regen.success=&aWorld {world} was regenerated!
mv-core.regen.prompt=Are you sure you want to regenerate world '{world}'?
mv-core.regen.success=&aWorld '{world}' regenerated!
# /mv reload
mv-core.reload.description=Reloads config files for all Multiverse modules.
@ -111,8 +105,7 @@ mv-core.reload.success=&aReload complete!
# /mv remove
mv-core.remove.description=Unloads a world from Multiverse and removes it from worlds.yml. This does NOT delete the world folder.
mv-core.remove.world.description=World you want to remove from MV's knowledge.
mv-core.remove.failed=Error trying to remove world from config!
mv-core.remove.success=&aWorld '{world}' is removed from config!
mv-core.remove.success=&aWorld '{world}' removed!
# /mv
mv-core.root.title=&a{name} version {version}
@ -128,8 +121,7 @@ mv-core.teleport.success=Teleporting {player} to {destination}...
mv-core.unload.description=Unloads a world from Multiverse. This does NOT remove the world folder. This does NOT remove it from the config file.
mv-core.unload.world.description=Name of the world you want to unload.
mv-core.unload.unloading=Unloading world '{world}'...
mv-core.unload.failure=Error unloading world '{world}'! &fSee console for more details.
mv-core.unload.success=&aUnloaded world '{world}'!
mv-core.unload.success=&aWorld '{world}' unloaded!
# /mv usage
mv-core.usage.description=Show Multiverse-Core command usage.
@ -145,6 +137,43 @@ mv-core.entrycheck.cannotpayentryfee=you do not have the ability to pay entry fe
mv-core.entrycheck.exceedplayerlimit=the world has reached its player limit.
mv-core.entrycheck.noworldaccess=you do not have permissions to access the world.
# world manager result
mv-core.cloneworld.invalidworldname=World '{world}' contains invalid characters!
mv-core.cloneworld.worldexistfolder=World '{world}' exists in server folders! You need to delete it first before cloning.
mv-core.cloneworld.worldexistunloaded=World '{world}' already exists and it's unloaded! You need to delete it first before cloning.
mv-core.cloneworld.worldexistloaded=World '{world}' already exists! You need to delete it first before cloning.
mv-core.cloneworld.copyfailed=Failed to copy world to '{world}': {error}\n&fSee console for more details.
mv-core.createworld.invalidworldname=World '{world}' contains invalid characters!
mv-core.createworld.worldexistfolder=World '{world}' already exists in server folders!&f Type '&a/mv import {world} <environment>&f' if you wish to import it.
mv-core.createworld.worldexistunloaded=World '{world}' already exists, but it's not loaded!&f Type '&a/mv load {world}&f' if you wish to load it.
mv-core.createworld.worldexistloaded=World '{world}' already exists!
mv-core.createworld.bukkitcreationfailed=Bukkit failed to create world '{world}': {error}\n&fSee console for more details.
mv-core.deleteworld.worldnonexistent=World '{world}' not found!
mv-core.deleteworld.loadfailed=Unable to load world '{world}', does the world folder exist?&f You can run '&a/mv remove {world}&f' to remove it from Multiverse, or attempt to load again with '&a/mv load {world}&f'.
mv-core.deleteworld.worldfoldernotfound=World '{world}' folder not found!
mv-core.deleteworld.failedtodeletefolder=Failed to delete world folder '{world}': {error}\n&fSee console for more details.
mv-core.importworld.invalidworldname=World '{world}' contains invalid characters!
mv-core.importworld.worldexistunloaded=World '{world}' already exists, but it's not loaded!&f Type '&a/mv load {world}&f' if you wish to load it.
mv-core.importworld.worldexistloaded=World '{world}' already exists!
mv-core.importworld.worldfolderinvalid=World '{world}' folder contents does not seem to be a valid world!
mv-core.importworld.bukkitcreationfailed=Bukkit failed to import world '{world}': {error}\n&fSee console for more details.
mv-core.loadworld.worldalreadyloading=World '{world}' is already loading! Please wait...
mv-core.loadworld.worldnonexistent=World '{world}' not found! Use '&a/mv create {world} <environment>&f' to create it.
mv-core.loadworld.worldexistfolder=World '{world}' exists in server folders, but it's not known to Multiverse!&f Type '&a/mv import {world} <environment>&f' if you wish to import it.
mv-core.loadworld.worldexistloaded=World '{world}' is already loaded!
mv-core.loadworld.bukkitcreationfailed=Bukkit failed to load world '{world}': {error}\n&fSee console for more details.
mv-core.removeworld.worldnonexistent=World '{world}' not found!
mv-core.unloadworld.worldalreadyunloading=World '{world}' is already unloading! Please wait...
mv-core.unloadworld.worldnonexistent=World '{world}' does not exist!
mv-core.unloadworld.worldunloaded=World '{world}' is already unloaded!
mv-core.unloadworld.bukkitunloadfailed=Bukkit failed to unload world '{world}': {error}\n&fSee console for more details.
# generic
mv-core.generic.success=Success!
mv-core.generic.failure=Failed!

Some files were not shown because too many files have changed in this diff Show More