Merge pull request #3018 from Multiverse/ben/mv5/player-world-action

Implement player world actions and some refactor
This commit is contained in:
Ben Woo 2023-09-12 14:20:42 +08:00 committed by GitHub
commit 0e43444523
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 257 additions and 89 deletions

View File

@ -15,7 +15,6 @@ import com.dumptruckman.minecraft.util.Logging;
import io.vavr.control.Option;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Server;
@ -32,7 +31,6 @@ import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
import org.mvplugins.multiverse.core.commandtools.MVCommandManager;
@ -49,6 +47,8 @@ import org.mvplugins.multiverse.core.worldnew.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.worldnew.WorldManager;
import org.mvplugins.multiverse.core.worldnew.entrycheck.EntryFeeResult;
import org.mvplugins.multiverse.core.worldnew.entrycheck.WorldEntryCheckerProvider;
import org.mvplugins.multiverse.core.worldnew.helpers.EnforcementHandler;
import org.mvplugins.multiverse.core.worldnew.helpers.PlayerWorldTeleporter;
/**
* Multiverse's Listener for players.
@ -64,8 +64,8 @@ public class MVPlayerListener implements InjectableListener {
private final MVEconomist economist;
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
private final Provider<MVCommandManager> commandManagerProvider;
private final CorePermissionsChecker permissionsChecker;
private final DestinationsProvider destinationsProvider;
private final EnforcementHandler enforcementHandler;
private final Map<String, String> playerWorld = new ConcurrentHashMap<String, String>();
@ -80,8 +80,8 @@ public class MVPlayerListener implements InjectableListener {
MVEconomist economist,
WorldEntryCheckerProvider worldEntryCheckerProvider,
Provider<MVCommandManager> commandManagerProvider,
CorePermissionsChecker permissionsChecker,
DestinationsProvider destinationsProvider) {
DestinationsProvider destinationsProvider,
EnforcementHandler enforcementHandler) {
this.plugin = plugin;
this.config = config;
this.worldManagerProvider = worldManagerProvider;
@ -91,8 +91,8 @@ public class MVPlayerListener implements InjectableListener {
this.economist = economist;
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
this.commandManagerProvider = commandManagerProvider;
this.permissionsChecker = permissionsChecker;
this.destinationsProvider = destinationsProvider;
this.enforcementHandler = enforcementHandler;
}
private WorldManager getWorldManager() {
@ -359,57 +359,21 @@ public class MVPlayerListener implements InjectableListener {
}, 1L);
}
// FOLLOWING 2 Methods and Private class handle Per Player GameModes.
private void handleGameModeAndFlight(Player player, World world) {
LoadedMultiverseWorld mvWorld = getWorldManager().getLoadedWorld(world.getName()).getOrNull();
if (mvWorld != null) {
this.handleGameModeAndFlight(player, mvWorld);
} else {
Logging.finer("Not handling gamemode and flight for world '" + world.getName()
+ "' not managed by Multiverse.");
}
}
/**
* Handles the gamemode for the specified {@link Player}.
*
* @param player The {@link Player}.
* @param world The world the player is in.
* @param world The {@link World} the player is supposed to be in.
*/
public void handleGameModeAndFlight(final Player player, final LoadedMultiverseWorld world) {
private void handleGameModeAndFlight(final Player player, World 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,
new Runnable() {
@Override
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.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);
player.setGameMode(world.getGameMode());
// Check if their flight mode should change
// TODO: need a override permission for this
if (player.getAllowFlight() && !world.getAllowFlight() && player.getGameMode() != GameMode.CREATIVE) {
player.setAllowFlight(false);
if (player.isFlying()) {
player.setFlying(false);
}
} else if (world.getAllowFlight()) {
if (player.getGameMode() == GameMode.CREATIVE) {
player.setAllowFlight(true);
}
}
} else {
Logging.fine("The gamemode/allowfly was NOT changed for player '%s' because he is now in world '%s' instead of world '%s'",
player.getName(), player.getWorld().getName(), world.getName());
}
} else {
Logging.fine("Player: " + player.getName() + " is IMMUNE to gamemode changes!");
}
}
}, 1L);
this.server.getScheduler().scheduleSyncDelayedTask(this.plugin, () -> {
if (!player.isOnline() || !player.getWorld().equals(world)) {
return;
}
enforcementHandler.handleFlightEnforcement(player);
enforcementHandler.handleGameModeEnforcement(player);
}, 1L);
}
}

View File

@ -37,6 +37,7 @@ import org.mvplugins.multiverse.core.worldnew.generators.GeneratorProvider;
import org.mvplugins.multiverse.core.worldnew.helpers.DataStore.GameRulesStore;
import org.mvplugins.multiverse.core.worldnew.helpers.DataTransfer;
import org.mvplugins.multiverse.core.worldnew.helpers.FilesManipulator;
import org.mvplugins.multiverse.core.worldnew.helpers.PlayerWorldTeleporter;
import org.mvplugins.multiverse.core.worldnew.options.CloneWorldOptions;
import org.mvplugins.multiverse.core.worldnew.options.CreateWorldOptions;
import org.mvplugins.multiverse.core.worldnew.options.ImportWorldOptions;
@ -71,6 +72,7 @@ public class WorldManager {
private final WorldsConfigManager worldsConfigManager;
private final WorldNameChecker worldNameChecker;
private final GeneratorProvider generatorProvider;
private final PlayerWorldTeleporter playerWorldActions;
private final FilesManipulator filesManipulator;
private final BlockSafety blockSafety;
private final SafeTTeleporter safetyTeleporter;
@ -81,6 +83,7 @@ public class WorldManager {
@NotNull WorldsConfigManager worldsConfigManager,
@NotNull WorldNameChecker worldNameChecker,
@NotNull GeneratorProvider generatorProvider,
@NotNull PlayerWorldTeleporter playerWorldActions,
@NotNull FilesManipulator filesManipulator,
@NotNull BlockSafety blockSafety,
@NotNull SafeTTeleporter safetyTeleporter,
@ -93,6 +96,7 @@ public class WorldManager {
this.worldsConfigManager = worldsConfigManager;
this.worldNameChecker = worldNameChecker;
this.generatorProvider = generatorProvider;
this.playerWorldActions = playerWorldActions;
this.filesManipulator = filesManipulator;
this.blockSafety = blockSafety;
this.safetyTeleporter = safetyTeleporter;
@ -374,7 +378,7 @@ public class WorldManager {
}
if (options.removePlayers()) {
removePlayersFromWorld(world);
playerWorldActions.removeFromWorld(world);
}
return unloadBukkitWorld(world.getBukkitWorld().getOrNull(), options.saveBukkitWorld()).fold(
@ -392,16 +396,6 @@ 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.
@ -623,20 +617,11 @@ public class WorldManager {
// different seed.
newWorld.setSpawnLocation(spawnLocation);
}
teleportPlayersToWorld(playersInWorld, newWorld);
playerWorldActions.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);
}

View File

@ -24,6 +24,7 @@ import org.mvplugins.multiverse.core.configuration.migration.NullStringMigratorA
import org.mvplugins.multiverse.core.configuration.migration.VersionMigrator;
import org.mvplugins.multiverse.core.world.configuration.AllowedPortalType;
import org.mvplugins.multiverse.core.worldnew.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.worldnew.helpers.EnforcementHandler;
/**
* Represents a world configuration.
@ -34,9 +35,12 @@ public final class WorldConfig {
private final WorldConfigNodes configNodes;
private final ConfigurationSectionHandle configHandle;
WorldConfig(@NotNull String worldName, @NotNull final ConfigurationSection configSection) {
WorldConfig(
@NotNull String worldName,
@NotNull ConfigurationSection configSection,
@NotNull EnforcementHandler enforcementHandler) {
this.worldName = worldName;
this.configNodes = new WorldConfigNodes();
this.configNodes = new WorldConfigNodes(enforcementHandler);
this.configHandle = ConfigurationSectionHandle.builder(configSection)
.logger(Logging.getLogger())
.nodes(configNodes.getNodes())
@ -364,14 +368,14 @@ public final class WorldConfig {
}
public void setMVWorld(@NotNull LoadedMultiverseWorld world) {
configNodes.world = world;
configNodes.setWorld(world);
}
public boolean hasMVWorld() {
return configNodes.world != null;
return configNodes.getWorld() != null;
}
public void deferenceMVWorld() {
configNodes.world = null;
configNodes.setWorld(null);
}
}

View File

@ -8,23 +8,35 @@ import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.jetbrains.annotations.NotNull;
import org.mvplugins.multiverse.core.configuration.node.ConfigNode;
import org.mvplugins.multiverse.core.configuration.node.Node;
import org.mvplugins.multiverse.core.configuration.node.NodeGroup;
import org.mvplugins.multiverse.core.world.configuration.AllowedPortalType;
import org.mvplugins.multiverse.core.worldnew.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.worldnew.helpers.EnforcementHandler;
/**
* Represents nodes in a world configuration.
*/
public class WorldConfigNodes {
static final double CONFIG_VERSION = 1.0;
private static final double CONFIG_VERSION = 1.0;
private final NodeGroup nodes = new NodeGroup();
LoadedMultiverseWorld world = null;
private EnforcementHandler enforcementHandler;
private LoadedMultiverseWorld world = null;
WorldConfigNodes() {
WorldConfigNodes(@NotNull EnforcementHandler enforcementHandler) {
this.enforcementHandler = enforcementHandler;
}
LoadedMultiverseWorld getWorld() {
return world;
}
void setWorld(LoadedMultiverseWorld world) {
this.world = world;
}
public NodeGroup getNodes() {
@ -36,6 +48,11 @@ public class WorldConfigNodes {
return node;
}
// BEGIN CHECKSTYLE-SUPPRESSION: Javadoc
// BEGIN CHECKSTYLE-SUPPRESSION: MemberName
// BEGIN CHECKSTYLE-SUPPRESSION: Abbreviation
// BEGIN CHECKSTYLE-SUPPRESSION: VisibilityModifier
final ConfigNode<Boolean> ADJUST_SPAWN = node(ConfigNode.builder("adjust-spawn", Boolean.class)
.defaultValue(false)
.build());
@ -46,6 +63,10 @@ public class WorldConfigNodes {
final ConfigNode<Boolean> ALLOW_FLIGHT = node(ConfigNode.builder("allow-flight", Boolean.class)
.defaultValue(false)
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
enforcementHandler.handleAllFlightEnforcement(world);
})
.build());
final ConfigNode<Boolean> ALLOW_WEATHER = node(ConfigNode.builder("allow-weather", Boolean.class)
@ -87,7 +108,8 @@ public class WorldConfigNodes {
.build());
final ConfigNode<Material> ENTRY_FEE_CURRENCY = node(ConfigNode.builder("entry-fee.currency", Material.class)
.defaultValue(Material.AIR) // TODO: Convert from material ID
// TODO: Convert from material ID
.defaultValue(Material.AIR)
.name("entryfee-currency")
.build());
@ -99,11 +121,15 @@ public class WorldConfigNodes {
final ConfigNode<GameMode> GAMEMODE = node(ConfigNode.builder("gamemode", GameMode.class)
.defaultValue(GameMode.SURVIVAL)
// TODO: Set all gamemodes of players in world to this gamemode
.onSetValue((oldValue, newValue) -> {
if (world == null) return;
enforcementHandler.handleAllGameModeEnforcement(world);
})
.build());
final ConfigNode<String> GENERATOR = node(ConfigNode.builder("generator", String.class)
.defaultValue("@error") // this should be set on world creation
// this should be set on world creation, if @error is shown in config, something went wrong
.defaultValue("@error")
.name(null)
.build());
@ -126,7 +152,6 @@ public class WorldConfigNodes {
final ConfigNode<Integer> PLAYER_LIMIT = node(ConfigNode.builder("player-limit", Integer.class)
.defaultValue(-1)
.name("player-limit")
.build());
final ConfigNode<AllowedPortalType> PORTAL_FORM = node(ConfigNode
@ -227,4 +252,9 @@ public class WorldConfigNodes {
.defaultValue(CONFIG_VERSION)
.name(null)
.build());
// END CHECKSTYLE-SUPPRESSION: Javadoc
// END CHECKSTYLE-SUPPRESSION: MemberName
// END CHECKSTYLE-SUPPRESSION: Abbreviation
// END CHECKSTYLE-SUPPRESSION: VisibilityModifier
}

View File

@ -21,22 +21,28 @@ import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.MultiverseCore;
import org.mvplugins.multiverse.core.worldnew.helpers.EnforcementHandler;
/**
* Manages the worlds.yml file.
*/
@Service
public final class WorldsConfigManager {
private static final String CONFIG_FILENAME = "worlds2.yml"; // TODO: Rename to worlds.yml
// TODO: Rename to worlds.yml
private static final String CONFIG_FILENAME = "worlds2.yml";
private final Map<String, WorldConfig> worldConfigMap;
private final File worldConfigFile;
private YamlConfiguration worldsConfig;
private final EnforcementHandler enforcementHandler;
@Inject
WorldsConfigManager(@NotNull MultiverseCore core) {
WorldsConfigManager(@NotNull MultiverseCore core, @NotNull EnforcementHandler enforcementHandler) {
worldConfigMap = new HashMap<>();
worldConfigFile = core.getDataFolder().toPath().resolve(CONFIG_FILENAME).toFile();
this.enforcementHandler = enforcementHandler;
}
/**
@ -119,7 +125,10 @@ public final class WorldsConfigManager {
getWorldConfig(worldName)
.peek(config -> config.load(getWorldConfigSection(worldName)))
.onEmpty(() -> {
WorldConfig newWorldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName));
WorldConfig newWorldConfig = new WorldConfig(
worldName,
getWorldConfigSection(worldName),
enforcementHandler);
worldConfigMap.put(worldName, newWorldConfig);
newWorldsAdded.add(newWorldConfig);
});
@ -174,7 +183,7 @@ public final class WorldsConfigManager {
if (worldConfigMap.containsKey(worldName)) {
throw new IllegalArgumentException("WorldConfig for world " + worldName + " already exists.");
}
WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName));
WorldConfig worldConfig = new WorldConfig(worldName, getWorldConfigSection(worldName), enforcementHandler);
worldConfigMap.put(worldName, worldConfig);
return worldConfig;
}

View File

@ -0,0 +1,87 @@
package org.mvplugins.multiverse.core.worldnew.helpers;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.permissions.CorePermissionsChecker;
import org.mvplugins.multiverse.core.worldnew.LoadedMultiverseWorld;
import org.mvplugins.multiverse.core.worldnew.WorldManager;
@Service
public class EnforcementHandler {
private final CorePermissionsChecker permissionsChecker;
private final Provider<WorldManager> worldManagerProvider;
@Inject
EnforcementHandler(CorePermissionsChecker permissionsChecker, Provider<WorldManager> worldManagerProvider) {
this.permissionsChecker = permissionsChecker;
this.worldManagerProvider = worldManagerProvider;
}
/**
* Teleports all players to the given location.
*
* @param world The world to teleport players to.
*/
public void handleAllGameModeEnforcement(@NotNull LoadedMultiverseWorld world) {
world.getPlayers().peek(players -> players.forEach(this::handleGameModeEnforcement));
}
/**
* Handles game mode enforcement for the given player in the world they are currently in.
*
* @param player The player to enforce game mode for.
*/
public void handleGameModeEnforcement(@NotNull Player player) {
worldManagerProvider.get().getLoadedWorld(player.getWorld()).peek(world -> {
if (permissionsChecker.hasGameModeBypassPermission(player, world)) {
Logging.finer("Player is immune to gamemode enforcement: %s", player.getName());
return;
}
Logging.finer("Handling gamemode for player in world '%s': %s, Changing to %s",
world.getName(), player.getName(), world.getGameMode());
player.setGameMode(world.getGameMode());
}).onEmpty(() -> {
Logging.fine("Player %s is not in a Multiverse world, gamemode enforcement will not apply",
player.getName());
});
}
/**
* Handles flight enforcement for all players in the given world.
*
* @param world The world to enforce flight in.
*/
public void handleAllFlightEnforcement(@NotNull LoadedMultiverseWorld world) {
world.getPlayers().peek(players -> players.forEach(this::handleFlightEnforcement));
}
/**
* Handles flight enforcement for the given player in the world they are currently in.
*
* @param player The player to enforce flight for.
*/
public void handleFlightEnforcement(@NotNull Player player) {
worldManagerProvider.get().getLoadedWorld(player.getWorld()).peek(world -> {
if (player.getAllowFlight() && !world.getAllowFlight() && player.getGameMode() != GameMode.CREATIVE) {
player.setAllowFlight(false);
if (player.isFlying()) {
player.setFlying(false);
}
} else if (world.getAllowFlight()) {
if (player.getGameMode() == GameMode.CREATIVE) {
player.setAllowFlight(true);
}
}
}).onEmpty(() -> {
Logging.fine("Player %s is not in a Multiverse world, flight enforcement will not apply",
player.getName());
});
}
}

View File

@ -0,0 +1,89 @@
package org.mvplugins.multiverse.core.worldnew.helpers;
import java.util.List;
import com.dumptruckman.minecraft.util.Logging;
import jakarta.inject.Inject;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service;
import org.mvplugins.multiverse.core.api.SafeTTeleporter;
import org.mvplugins.multiverse.core.worldnew.LoadedMultiverseWorld;
/**
* Handles all player actions that need to be done when a change in world related activity occurs.
*/
@Service
public class PlayerWorldTeleporter {
private final SafeTTeleporter safetyTeleporter;
@Inject
PlayerWorldTeleporter(@NotNull SafeTTeleporter safetyTeleporter) {
this.safetyTeleporter = safetyTeleporter;
}
/**
* Removes all players from the given world.
*
* @param world The world to remove all players from.
*/
public void removeFromWorld(@NotNull LoadedMultiverseWorld world) {
// TODO: Better handling of fallback world
World toWorld = Bukkit.getWorlds().get(0);
transferFromWorldTo(world, toWorld);
}
/**
* Transfers all players from the given world to another world's spawn location.
*
* @param from The world to transfer players from.
* @param to The location to transfer players to.
*/
public void transferFromWorldTo(@NotNull LoadedMultiverseWorld from, @NotNull LoadedMultiverseWorld to) {
transferAllFromWorldToLocation(from, to.getSpawnLocation());
}
/**
* Transfers all players from the given world to another world's spawn location.
*
* @param from The world to transfer players from.
* @param to The world to transfer players to.
*/
public void transferFromWorldTo(@NotNull LoadedMultiverseWorld from, @NotNull World to) {
transferAllFromWorldToLocation(from, to.getSpawnLocation());
}
/**
* Transfers all players from the given world to the given location.
*
* @param world The world to transfer players from.
* @param location The location to transfer players to.
*/
public void transferAllFromWorldToLocation(@NotNull LoadedMultiverseWorld world, @NotNull Location location) {
world.getPlayers().peek(players -> players.forEach(player -> {
if (player.isOnline()) {
Logging.fine("Teleporting player '%s' to world spawn: %s", player.getName(), location);
safetyTeleporter.safelyTeleport(null, player, location, true);
}
}));
}
/**
* Teleports all players to the given world's spawn location.
*
* @param players The players to teleport.
* @param world The world to teleport players to.
*/
public 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);
}
});
}
}