feat: Re-implement spawn location override and gamemode enforcement

This commit is contained in:
Ben Woo 2023-04-10 15:45:52 +08:00
parent 4bfcd0d907
commit de16e97d88
No known key found for this signature in database
GPG Key ID: FB2A3645536E12C8
5 changed files with 137 additions and 50 deletions

View File

@ -1,50 +1,93 @@
package com.onarandombox.MultiverseCore.listeners; package com.onarandombox.MultiverseCore.listeners;
import com.dumptruckman.minecraft.util.Logging; import com.dumptruckman.minecraft.util.Logging;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MVWorld; import com.onarandombox.MultiverseCore.api.MVWorld;
import com.onarandombox.MultiverseCore.api.MVWorldManager; import com.onarandombox.MultiverseCore.api.MVWorldManager;
import com.onarandombox.MultiverseCore.config.MVCoreConfig; import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.economy.MVEconomist; import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.inject.InjectableListener; import com.onarandombox.MultiverseCore.inject.InjectableListener;
import com.onarandombox.MultiverseCore.teleportation.TeleportQueue; import com.onarandombox.MultiverseCore.teleportation.TeleportQueue;
import com.onarandombox.MultiverseCore.utils.permissions.PermissionsChecker;
import com.onarandombox.MultiverseCore.utils.result.ResultGroup; import com.onarandombox.MultiverseCore.utils.result.ResultGroup;
import com.onarandombox.MultiverseCore.world.entrycheck.EntryFeeResult; import com.onarandombox.MultiverseCore.world.entrycheck.EntryFeeResult;
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider; import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
import io.vavr.control.Option; import io.vavr.control.Option;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jvnet.hk2.annotations.Service; import org.jvnet.hk2.annotations.Service;
@Service @Service
public class NewMVPlayerListener implements InjectableListener { public class NewMVPlayerListener implements InjectableListener {
private final @NotNull MultiverseCore plugin;
private final @NotNull MVCoreConfig config; private final @NotNull MVCoreConfig config;
private final @NotNull Server server; private final @NotNull Server server;
private final @NotNull TeleportQueue teleportQueue; private final @NotNull TeleportQueue teleportQueue;
private final @NotNull MVWorldManager worldManager; private final @NotNull MVWorldManager worldManager;
private final @NotNull WorldEntryCheckerProvider worldEntryCheckerProvider; private final @NotNull WorldEntryCheckerProvider worldEntryCheckerProvider;
private final @NotNull MVEconomist economist; private final @NotNull MVEconomist economist;
private final @NotNull PermissionsChecker permissionsChecker;
@Inject @Inject
NewMVPlayerListener( NewMVPlayerListener(
@NotNull MultiverseCore plugin,
@NotNull MVCoreConfig config, @NotNull MVCoreConfig config,
@NotNull Server server, @NotNull Server server,
@NotNull TeleportQueue teleportQueue, @NotNull TeleportQueue teleportQueue,
@NotNull MVWorldManager worldManager, @NotNull MVWorldManager worldManager,
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider, @NotNull WorldEntryCheckerProvider worldEntryCheckerProvider,
@NotNull MVEconomist economist @NotNull MVEconomist economist,
@NotNull PermissionsChecker permissionsChecker
) { ) {
this.plugin = plugin;
this.config = config; this.config = config;
this.server = server; this.server = server;
this.teleportQueue = teleportQueue; this.teleportQueue = teleportQueue;
this.worldManager = worldManager; this.worldManager = worldManager;
this.worldEntryCheckerProvider = worldEntryCheckerProvider; this.worldEntryCheckerProvider = worldEntryCheckerProvider;
this.economist = economist; this.economist = economist;
this.permissionsChecker = permissionsChecker;
}
@EventHandler
public void playerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
Location spawnLocation = worldManager.getFirstSpawnWorld().getSpawnLocation();
if (!player.hasPlayedBefore()) {
Logging.finer("Player joined for the FIRST time!");
if (config.getFirstSpawnOverride()) {
Logging.fine("Moving NEW player to spawn location: %s", spawnLocation);
oneTickLater(() -> player.teleport(spawnLocation));
}
return;
}
Logging.finer("Player joined AGAIN!");
MVWorld world = worldManager.getMVWorld(player.getWorld());
if (world == null) {
// We don't want to do anything if the player is not in a world managed by MV.
Logging.fine("Player joined in a world not managed by MV.");
return;
}
worldEntryCheckerProvider.forWorld(player, world)
.canStayInWorld()
.success(() -> oneTickLater(() -> handleGameModeAndFlight(player, world)))
.failure(() -> {
player.sendMessage("[MV] - Sorry you can't be in this world anymore!");
oneTickLater(() -> player.teleport(spawnLocation));
});
} }
@EventHandler(priority = EventPriority.HIGHEST) @EventHandler(priority = EventPriority.HIGHEST)
@ -67,6 +110,8 @@ public class NewMVPlayerListener implements InjectableListener {
: server.getPlayerExact(name)) : server.getPlayerExact(name))
.getOrElse(teleportee); .getOrElse(teleportee);
Logging.fine("Inferred teleporter '%s' for teleportee '%s'.", teleporter.getName(), teleportee.getName());
Option<MVWorld> fromWorld = Option.of(this.worldManager.getMVWorld(event.getFrom().getWorld())); Option<MVWorld> fromWorld = Option.of(this.worldManager.getMVWorld(event.getFrom().getWorld()));
MVWorld toWorld = Option.of(event.getTo()).map(to -> this.worldManager.getMVWorld(to.getWorld())).getOrNull(); MVWorld toWorld = Option.of(event.getTo()).map(to -> this.worldManager.getMVWorld(to.getWorld())).getOrNull();
@ -82,7 +127,8 @@ public class NewMVPlayerListener implements InjectableListener {
return; return;
} }
ResultGroup worldEntryResult = worldEntryCheckerProvider.forWorld(teleportee, fromWorld.getOrNull(), toWorld).canEnterWorld() ResultGroup worldEntryResult = worldEntryCheckerProvider.forWorld(teleportee, toWorld)
.canEnterWorld(fromWorld.getOrNull())
.success(() -> Logging.fine("MV-Core is allowing '%s' to go to '%s'.", teleportee.getName(), toWorld.getName())) .success(() -> Logging.fine("MV-Core is allowing '%s' to go to '%s'.", teleportee.getName(), toWorld.getName()))
.successWithReason(EntryFeeResult.Success.ENOUGH_MONEY, () -> { .successWithReason(EntryFeeResult.Success.ENOUGH_MONEY, () -> {
economist.payEntryFee((Player) teleporter, toWorld); economist.payEntryFee((Player) teleporter, toWorld);
@ -97,4 +143,51 @@ public class NewMVPlayerListener implements InjectableListener {
Logging.fine("World entry result for player '%s', from '%s' to '%s': %s", Logging.fine("World entry result for player '%s', from '%s' to '%s': %s",
teleportee.getName(), fromWorld.map(MVWorld::getName).getOrNull(), toWorld.getName(), worldEntryResult); teleportee.getName(), fromWorld.map(MVWorld::getName).getOrNull(), toWorld.getName(), worldEntryResult);
} }
@EventHandler(priority = EventPriority.MONITOR)
public void playerChangedWorld(PlayerChangedWorldEvent event) {
MVWorld toWorld = worldManager.getMVWorld(event.getPlayer().getWorld());
if (toWorld == null) {
// We don't want to do anything if the destination world is not managed by MV.
Logging.fine("Player '%s' is changing to world '%s' which is not managed by Multiverse-Core. No further actions will be taken by Multiverse-Core.",
event.getPlayer().getName(), event.getPlayer().getWorld());
return;
}
this.handleGameModeAndFlight(event.getPlayer(), toWorld);
}
private void handleGameModeAndFlight(Player player, MVWorld world) {
if (!config.getEnforceGameMode()) {
Logging.fine("GameMode enforcement is disabled. Not changing game mode.");
return;
}
if (!player.getWorld().equals(world.getCBWorld())) {
Logging.fine("Player '%s' is not in world '%s'. Not changing game mode or flight.", player.getName(), world.getName());
return;
}
if (permissionsChecker.hasGameModeBypassPermission(player, world)) {
Logging.fine("Player '%s' has bypass permission. Not changing game mode or flight.", player.getName());
return;
}
GameMode targetGameMode = world.getGameMode();
Logging.fine("Handling gamemode for player %s: Changing to %s", player.getName(), targetGameMode.toString());
player.setGameMode(targetGameMode);
// 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);
}
}
}
private void oneTickLater(Runnable runnable) {
server.getScheduler().scheduleSyncDelayedTask(plugin, runnable, 1L);
}
} }

View File

@ -48,10 +48,10 @@ public class PermissionsChecker {
private boolean hasPermission(CommandSender sender, Permission permission) { private boolean hasPermission(CommandSender sender, Permission permission) {
if (sender.hasPermission(permission)) { if (sender.hasPermission(permission)) {
Logging.finer("Checking to see if sender [" + sender.getName() + "] has permission [" + permission + "]... YES"); Logging.finer("Checking to see if sender [%s] has permission [%s]... YES", sender.getName(), permission.getName());
return true; return true;
} }
Logging.finer("Checking to see if sender [" + sender.getName() + "] has permission [" + permission + "]... NO"); Logging.finer("Checking to see if sender [%s] has permission [%s]... NO", sender.getName(), permission.getName());
return false; return false;
} }
} }

View File

@ -1,15 +0,0 @@
package com.onarandombox.MultiverseCore.world.entrycheck;
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
public class GameModeResult {
public enum Success implements SuccessReason {
ENFORCE_GAME_MODE
}
public enum Failure implements FailureReason {
NO_ENFORCE_GAME_MODE,
BYPASS_ENFORCE_GAME_MODE
}
}

View File

@ -20,7 +20,6 @@ import java.util.Objects;
public class WorldEntryChecker { public class WorldEntryChecker {
private final @NotNull CommandSender sender; private final @NotNull CommandSender sender;
private final @Nullable MVWorld fromWorld;
private final @NotNull MVWorld toWorld; private final @NotNull MVWorld toWorld;
private final @NotNull MVCoreConfig config; private final @NotNull MVCoreConfig config;
@ -29,29 +28,38 @@ public class WorldEntryChecker {
public WorldEntryChecker( public WorldEntryChecker(
@NotNull CommandSender sender, @NotNull CommandSender sender,
@Nullable MVWorld fromWorld,
@NotNull MVWorld toWorld, @NotNull MVWorld toWorld,
@NotNull MVCoreConfig config, @NotNull MVCoreConfig config,
@NotNull PermissionsChecker permissionsChecker, @NotNull PermissionsChecker permissionsChecker,
@NotNull MVEconomist economist @NotNull MVEconomist economist
) { ) {
this.sender = sender; this.sender = sender;
this.fromWorld = fromWorld;
this.toWorld = toWorld; this.toWorld = toWorld;
this.config = config; this.config = config;
this.permissionsChecker = permissionsChecker; this.permissionsChecker = permissionsChecker;
this.economist = economist; this.economist = economist;
} }
public ResultGroup canEnterWorld() { public ResultGroup canStayInWorld() {
return canEnterWorld(true); return canStayInWorld(true);
} }
public ResultGroup canEnterWorld(boolean stopOnFailure) { public ResultGroup canStayInWorld(boolean stopOnFailure) {
return ResultGroup.builder(stopOnFailure) return ResultGroup.builder(stopOnFailure)
.then(this::canAccessWorld) .then(this::canAccessWorld)
.then(this::isNotBlacklisted)
.then(this::isWithinPlayerLimit) .then(this::isWithinPlayerLimit)
.build();
}
public ResultGroup canEnterWorld(@Nullable MVWorld fromWorld) {
return canEnterWorld(fromWorld, true);
}
public ResultGroup canEnterWorld(@Nullable MVWorld fromWorld, boolean stopOnFailure) {
return ResultGroup.builder(stopOnFailure)
.then(this::canAccessWorld)
.then(this::isWithinPlayerLimit)
.then(() -> isNotBlacklisted(fromWorld))
.then(this::canPayEntryFee) .then(this::canPayEntryFee)
.build(); .build();
} }
@ -65,6 +73,28 @@ public class WorldEntryChecker {
: Result.failure(WorldAccessResult.Failure.NO_WORLD_ACCESS); : Result.failure(WorldAccessResult.Failure.NO_WORLD_ACCESS);
} }
public Result<PlayerLimitResult.Success, PlayerLimitResult.Failure> isWithinPlayerLimit() {
final int playerLimit = toWorld.getPlayerLimit();
if (playerLimit <= -1) {
return Result.success(PlayerLimitResult.Success.NO_PLAYERLIMIT);
}
if (permissionsChecker.hasPlayerLimitBypassPermission(sender, toWorld)) {
return Result.success(PlayerLimitResult.Success.BYPASS_PLAYERLIMIT);
}
return playerLimit > toWorld.getCBWorld().getPlayers().size()
? Result.success(PlayerLimitResult.Success.WITHIN_PLAYERLIMIT)
: Result.failure(PlayerLimitResult.Failure.EXCEED_PLAYERLIMIT);
}
public Result<BlacklistResult.Success, BlacklistResult.Failure> isNotBlacklisted(@Nullable MVWorld fromWorld) {
if (fromWorld == null) {
return Result.success(BlacklistResult.Success.UNKNOWN_FROM_WORLD);
}
return toWorld.getWorldBlacklist().contains(fromWorld.getName())
? Result.failure(BlacklistResult.Failure.BLACKLISTED)
: Result.success(BlacklistResult.Success.NOT_BLACKLISTED);
}
public Result<EntryFeeResult.Success, EntryFeeResult.Failure> canPayEntryFee() { public Result<EntryFeeResult.Success, EntryFeeResult.Failure> canPayEntryFee() {
double price = toWorld.getPrice(); double price = toWorld.getPrice();
Material currency = toWorld.getCurrency(); Material currency = toWorld.getCurrency();
@ -84,26 +114,4 @@ public class WorldEntryChecker {
? Result.success(EntryFeeResult.Success.ENOUGH_MONEY) ? Result.success(EntryFeeResult.Success.ENOUGH_MONEY)
: Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY); : Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY);
} }
public Result<PlayerLimitResult.Success, PlayerLimitResult.Failure> isWithinPlayerLimit() {
final int playerLimit = toWorld.getPlayerLimit();
if (playerLimit <= -1) {
return Result.success(PlayerLimitResult.Success.NO_PLAYERLIMIT);
}
if (permissionsChecker.hasPlayerLimitBypassPermission(sender, toWorld)) {
return Result.success(PlayerLimitResult.Success.BYPASS_PLAYERLIMIT);
}
return playerLimit > toWorld.getCBWorld().getPlayers().size()
? Result.success(PlayerLimitResult.Success.WITHIN_PLAYERLIMIT)
: Result.failure(PlayerLimitResult.Failure.EXCEED_PLAYERLIMIT);
}
public Result<BlacklistResult.Success, BlacklistResult.Failure> isNotBlacklisted() {
if (fromWorld == null) {
return Result.success(BlacklistResult.Success.UNKNOWN_FROM_WORLD);
}
return toWorld.getWorldBlacklist().contains(fromWorld.getName())
? Result.failure(BlacklistResult.Failure.BLACKLISTED)
: Result.success(BlacklistResult.Success.NOT_BLACKLISTED);
}
} }

View File

@ -5,6 +5,7 @@ import com.onarandombox.MultiverseCore.config.MVCoreConfig;
import com.onarandombox.MultiverseCore.economy.MVEconomist; import com.onarandombox.MultiverseCore.economy.MVEconomist;
import com.onarandombox.MultiverseCore.utils.permissions.PermissionsChecker; import com.onarandombox.MultiverseCore.utils.permissions.PermissionsChecker;
import jakarta.inject.Inject; import jakarta.inject.Inject;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -28,7 +29,7 @@ public class WorldEntryCheckerProvider {
this.permissionsChecker = permissionsChecker; this.permissionsChecker = permissionsChecker;
} }
public @NotNull WorldEntryChecker forWorld(@NotNull Player player, @Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) { public @NotNull WorldEntryChecker forWorld(@NotNull CommandSender sender, @NotNull MVWorld toWorld) {
return new WorldEntryChecker(player, fromWorld, toWorld, config, permissionsChecker, economist); return new WorldEntryChecker(sender, toWorld, config, permissionsChecker, economist);
} }
} }