mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2024-11-10 21:01:17 +01:00
Add support for removing players before unloading world
This commit is contained in:
parent
379e0172c4
commit
481933b948
@ -4,15 +4,19 @@ 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.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;
|
||||
@ -23,6 +27,14 @@ public class UnloadCommand extends MultiverseCommand {
|
||||
|
||||
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 WorldManager worldManager) {
|
||||
super(commandManager);
|
||||
@ -31,17 +43,27 @@ public class UnloadCommand extends MultiverseCommand {
|
||||
|
||||
@Subcommand("unload")
|
||||
@CommandPermission("multiverse.core.unload")
|
||||
@CommandCompletion("@mvworlds")
|
||||
@CommandCompletion("@mvworlds @flags:groupName=mvunloadcommand")
|
||||
@Syntax("<world>")
|
||||
@Description("{@@mv-core.unload.description}")
|
||||
public void onUnloadCommand(MVCommandIssuer issuer,
|
||||
public void onUnloadCommand(
|
||||
MVCommandIssuer issuer,
|
||||
|
||||
@Syntax("<world>")
|
||||
@Description("{@@mv-core.unload.world.description}")
|
||||
LoadedMultiverseWorld world
|
||||
) {
|
||||
LoadedMultiverseWorld world,
|
||||
|
||||
@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());
|
||||
worldManager.unloadWorld(world)
|
||||
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());
|
||||
|
@ -10,6 +10,7 @@ package com.onarandombox.MultiverseCore.listeners;
|
||||
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;
|
||||
@ -42,11 +43,12 @@ public class MVWorldListener implements InjectableListener {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
worldManager.unloadWorld(event.getWorld()).onFailure(failure -> {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,8 +12,10 @@ 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;
|
||||
|
||||
public class LoadedMultiverseWorld extends MultiverseWorld {
|
||||
@ -110,6 +112,10 @@ public class LoadedMultiverseWorld extends MultiverseWorld {
|
||||
return getBukkitWorld().map(World::canGenerateStructures);
|
||||
}
|
||||
|
||||
public Option<List<Player>> getPlayers() {
|
||||
return getBukkitWorld().map(World::getPlayers);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -19,6 +19,7 @@ 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;
|
||||
@ -31,9 +32,11 @@ import io.vavr.control.Option;
|
||||
import io.vavr.control.Try;
|
||||
import jakarta.inject.Inject;
|
||||
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;
|
||||
@ -354,47 +357,26 @@ public class WorldManager {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads an existing multiverse world. It will still remain as an unloaded world in mv config.
|
||||
*
|
||||
* @param world The bukkit world to unload.
|
||||
* @return The result of the unload action.
|
||||
*/
|
||||
public Attempt<MultiverseWorld, UnloadFailureReason> unloadWorld(@NotNull World world) {
|
||||
return unloadWorld(world.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads an existing multiverse world. It will still remain as an unloaded world in mv config.
|
||||
*
|
||||
* @param worldName The name of the world to unload.
|
||||
* @return The result of the unload action.
|
||||
*/
|
||||
public Attempt<MultiverseWorld, UnloadFailureReason> unloadWorld(@NotNull String worldName) {
|
||||
return getLoadedWorld(worldName)
|
||||
.map(this::unloadWorld)
|
||||
.getOrElse(() -> isUnloadedWorld(worldName)
|
||||
? worldActionResult(UnloadFailureReason.WORLD_UNLOADED, worldName)
|
||||
: worldActionResult(UnloadFailureReason.WORLD_NON_EXISTENT, worldName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads an existing multiverse world. It will still remain as an unloaded world.
|
||||
*
|
||||
* @param world The multiverse world to unload.
|
||||
* @param options The options for customizing the unloading of a world.
|
||||
* @return The result of the unload action.
|
||||
*/
|
||||
public Attempt<MultiverseWorld, UnloadFailureReason> unloadWorld(
|
||||
@NotNull LoadedMultiverseWorld world) {
|
||||
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());
|
||||
}
|
||||
|
||||
// TODO: removePlayersFromWorld?
|
||||
if (options.removePlayers()) {
|
||||
removePlayersFromWorld(world);
|
||||
}
|
||||
|
||||
return unloadBukkitWorld(world.getBukkitWorld().getOrNull()).fold(
|
||||
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(
|
||||
@ -409,6 +391,16 @@ public class WorldManager {
|
||||
}));
|
||||
}
|
||||
|
||||
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.
|
||||
@ -444,7 +436,8 @@ public class WorldManager {
|
||||
* @return The result of the remove.
|
||||
*/
|
||||
public Attempt<String, RemoveFailureReason> removeWorld(@NotNull LoadedMultiverseWorld loadedWorld) {
|
||||
return unloadWorld(loadedWorld)
|
||||
// TODO: Config option on removePlayers
|
||||
return unloadWorld(UnloadWorldOptions.world(loadedWorld).removePlayers(true))
|
||||
.transform(RemoveFailureReason.UNLOAD_FAILED)
|
||||
.mapAttempt(this::removeWorldFromConfig);
|
||||
}
|
||||
@ -603,9 +596,8 @@ public class WorldManager {
|
||||
* @return The result of the regeneration.
|
||||
*/
|
||||
public Attempt<LoadedMultiverseWorld, RegenFailureReason> regenWorld(@NotNull RegenWorldOptions options) {
|
||||
// TODO: Teleport players out of world, and back in after regen
|
||||
|
||||
LoadedMultiverseWorld world = options.world();
|
||||
List<Player> playersInWorld = world.getPlayers().getOrElse(Collections.emptyList());
|
||||
DataTransfer<LoadedMultiverseWorld> dataTransfer = transferData(options, world);
|
||||
CreateWorldOptions createWorldOptions = CreateWorldOptions.worldName(world.getName())
|
||||
.environment(world.getEnvironment())
|
||||
@ -619,10 +611,20 @@ public class WorldManager {
|
||||
.mapAttempt(() -> createWorld(createWorldOptions).transform(RegenFailureReason.CREATE_FAILED))
|
||||
.onSuccess(newWorld -> {
|
||||
dataTransfer.pasteAllTo(newWorld);
|
||||
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);
|
||||
}
|
||||
@ -674,10 +676,13 @@ public class WorldManager {
|
||||
* @param world The bukkit world to unload.
|
||||
* @return The unloaded world.
|
||||
*/
|
||||
private Try<Void> unloadBukkitWorld(World world) {
|
||||
private Try<Void> unloadBukkitWorld(World world, boolean save) {
|
||||
return Try.run(() -> {
|
||||
if (world == null) {
|
||||
return;
|
||||
}
|
||||
unloadTracker.add(world.getName());
|
||||
if (!Bukkit.unloadWorld(world, true)) {
|
||||
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!");
|
||||
}
|
||||
|
@ -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 removePlayers Whether to remove players from the world before unloading.
|
||||
* @return This {@link UnloadWorldOptions} instance.
|
||||
*/
|
||||
public UnloadWorldOptions removePlayers(boolean removePlayers) {
|
||||
this.removePlayers = removePlayers;
|
||||
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 saveBukkitWorld Whether to save the bukkit world before unloading.
|
||||
* @return This {@link UnloadWorldOptions} instance.
|
||||
*/
|
||||
public UnloadWorldOptions saveBukkitWorld(boolean saveBukkitWorld) {
|
||||
this.saveBukkitWorld = saveBukkitWorld;
|
||||
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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user