mirror of
https://github.com/Multiverse/Multiverse-Core.git
synced 2025-01-10 10:17:41 +01:00
feat: Revamp world entry checking
This commit is contained in:
parent
bd78b441ba
commit
ad47ab2340
@ -11,6 +11,7 @@ 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.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager;
|
||||
import com.onarandombox.MultiverseCore.commandtools.MultiverseCommand;
|
||||
import com.onarandombox.MultiverseCore.destination.DestinationsProvider;
|
||||
@ -49,17 +50,17 @@ public class TeleportCommand extends MultiverseCommand {
|
||||
) {
|
||||
// TODO Add warning if teleporting too many players at once.
|
||||
|
||||
String playerName = players.length == 1
|
||||
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
|
||||
: players.length + " players";
|
||||
|
||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
"{player}", playerName, "{destination}", destination.toString());
|
||||
|
||||
CompletableFuture.allOf(Arrays.stream(players)
|
||||
.map(player -> this.destinationsProvider.playerTeleportAsync(issuer, player, destination))
|
||||
.toArray(CompletableFuture[]::new))
|
||||
.thenRun(() -> {
|
||||
String playerName = players.length == 1
|
||||
? issuer.getPlayer() == players[0] ? "you" : players[0].getName()
|
||||
: players.length + " players";
|
||||
|
||||
issuer.sendInfo(MVCorei18n.TELEPORT_SUCCESS,
|
||||
"{player}", playerName, "{destination}", destination.toString());
|
||||
});
|
||||
.thenRun(() -> Logging.finer("Async teleport completed."));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.onarandombox.MultiverseCore.economy;
|
||||
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
@ -91,6 +92,35 @@ public class MVEconomist {
|
||||
return "Sorry, you don't have enough " + (isItemCurrency(currency) ? "items" : "funds") + ". " + message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pays for a given amount of currency either from the player's economy account or inventory if the currency.
|
||||
*
|
||||
* @param player the player to deposit currency into.
|
||||
* @param world the world to take entry fee from.
|
||||
*/
|
||||
public void payEntryFee(Player player, MVWorld world) {
|
||||
payEntryFee(player, world.getPrice(), world.getCurrency());
|
||||
}
|
||||
|
||||
/**
|
||||
* Pays for a given amount of currency either from the player's economy account or inventory if the currency
|
||||
*
|
||||
* @param player the player to take currency from.
|
||||
* @param price the amount to take.
|
||||
* @param currency the type of currency.
|
||||
*/
|
||||
public void payEntryFee(Player player, double price, Material currency) {
|
||||
if (price == 0D) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (price < 0) {
|
||||
this.deposit(player, -price, currency);
|
||||
} else {
|
||||
this.withdraw(player, price, currency);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deposits a given amount of currency either into the player's economy account or inventory if the currency
|
||||
* is not null.
|
||||
|
@ -16,12 +16,17 @@ 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.economy.MVEconomist;
|
||||
import com.onarandombox.MultiverseCore.event.MVRespawnEvent;
|
||||
import com.onarandombox.MultiverseCore.inject.InjectableListener;
|
||||
import com.onarandombox.MultiverseCore.teleportation.TeleportQueue;
|
||||
import com.onarandombox.MultiverseCore.utils.MVPermissions;
|
||||
import com.onarandombox.MultiverseCore.utils.PermissionTools;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.CheckResultChain;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.EntryFeeResult;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import org.bukkit.GameMode;
|
||||
@ -54,6 +59,9 @@ public class MVPlayerListener implements InjectableListener {
|
||||
private final SafeTTeleporter safeTTeleporter;
|
||||
private final Server server;
|
||||
private final TeleportQueue teleportQueue;
|
||||
private final MVEconomist economist;
|
||||
private final WorldEntryCheckerProvider worldEntryCheckerProvider;
|
||||
private final Provider<MVCommandManager> commandManagerProvider;
|
||||
|
||||
private final Map<String, String> playerWorld = new ConcurrentHashMap<String, String>();
|
||||
|
||||
@ -66,8 +74,10 @@ public class MVPlayerListener implements InjectableListener {
|
||||
Provider<MVPermissions> mvPermsProvider,
|
||||
SafeTTeleporter safeTTeleporter,
|
||||
Server server,
|
||||
TeleportQueue teleportQueue
|
||||
) {
|
||||
TeleportQueue teleportQueue,
|
||||
MVEconomist economist,
|
||||
WorldEntryCheckerProvider worldEntryCheckerProvider,
|
||||
Provider<MVCommandManager> commandManagerProvider) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.worldManagerProvider = worldManagerProvider;
|
||||
@ -76,12 +86,19 @@ public class MVPlayerListener implements InjectableListener {
|
||||
this.safeTTeleporter = safeTTeleporter;
|
||||
this.server = server;
|
||||
this.teleportQueue = teleportQueue;
|
||||
this.economist = economist;
|
||||
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
|
||||
this.commandManagerProvider = commandManagerProvider;
|
||||
}
|
||||
|
||||
private MVWorldManager getWorldManager() {
|
||||
return worldManagerProvider.get();
|
||||
}
|
||||
|
||||
private MVCommandManager getCommandManager() {
|
||||
return commandManagerProvider.get();
|
||||
}
|
||||
|
||||
private MVPermissions getMVPerms() {
|
||||
return mvPermsProvider.get();
|
||||
}
|
||||
@ -189,7 +206,7 @@ public class MVPlayerListener implements InjectableListener {
|
||||
return;
|
||||
}
|
||||
Player teleportee = event.getPlayer();
|
||||
CommandSender teleporter = null;
|
||||
CommandSender teleporter;
|
||||
Optional<String> teleporterName = teleportQueue.popFromQueue(teleportee.getName());
|
||||
if (teleporterName.isPresent()) {
|
||||
if (teleporterName.equals("CONSOLE")) {
|
||||
@ -198,6 +215,8 @@ public class MVPlayerListener implements InjectableListener {
|
||||
} else {
|
||||
teleporter = this.server.getPlayerExact(teleporterName.get());
|
||||
}
|
||||
} else {
|
||||
teleporter = teleportee;
|
||||
}
|
||||
Logging.finer("Inferred sender '" + teleporter + "' from name '"
|
||||
+ teleporterName + "', fetched from name '" + teleportee.getName() + "'");
|
||||
@ -215,50 +234,20 @@ public class MVPlayerListener implements InjectableListener {
|
||||
this.stateSuccess(teleportee.getName(), toWorld.getAlias());
|
||||
return;
|
||||
}
|
||||
// TODO: Refactor these lines.
|
||||
// Charge the teleporter
|
||||
event.setCancelled(!pt.playerHasMoneyToEnter(fromWorld, toWorld, teleporter, teleportee, true));
|
||||
if (event.isCancelled() && teleporter != null) {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was DENIED ACCESS to '" + toWorld.getAlias()
|
||||
+ "' because '" + teleporter.getName()
|
||||
+ "' don't have the FUNDS required to enter it.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if player is allowed to enter the world if we're enforcing permissions
|
||||
if (config.getEnforceAccess()) {
|
||||
event.setCancelled(!pt.playerCanGoFromTo(fromWorld, toWorld, teleporter, teleportee));
|
||||
if (event.isCancelled() && teleporter != null) {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was DENIED ACCESS to '" + toWorld.getAlias()
|
||||
+ "' because '" + teleporter.getName()
|
||||
+ "' don't have: multiverse.access." + event.getTo().getWorld().getName());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was allowed to go to '" + toWorld.getAlias() + "' because enforceaccess is off.");
|
||||
}
|
||||
|
||||
// Does a limit actually exist?
|
||||
if (toWorld.getPlayerLimit() > -1) {
|
||||
// Are there equal or more people on the world than the limit?
|
||||
if (toWorld.getCBWorld().getPlayers().size() >= toWorld.getPlayerLimit()) {
|
||||
// Ouch the world is full, lets see if the player can bypass that limitation
|
||||
if (!pt.playerCanBypassPlayerLimit(toWorld, teleporter, teleportee)) {
|
||||
Logging.fine("Player '" + teleportee.getName()
|
||||
+ "' was DENIED ACCESS to '" + toWorld.getAlias()
|
||||
+ "' because the world is full and '" + teleporter.getName()
|
||||
+ "' doesn't have: mv.bypass.playerlimit." + event.getTo().getWorld().getName());
|
||||
CheckResultChain entryResult = worldEntryCheckerProvider.forSender(teleporter).canEnterWorld(fromWorld, toWorld)
|
||||
.onSuccessReason(EntryFeeResult.Success.class, reason -> {
|
||||
if (reason == EntryFeeResult.Success.ENOUGH_MONEY) {
|
||||
economist.payEntryFee((Player) teleporter, toWorld);
|
||||
// Send payment receipt
|
||||
}
|
||||
})
|
||||
.onFailure(results -> {
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
getCommandManager().getCommandIssuer(teleporter).sendError(results.getLastResultMessage());
|
||||
});
|
||||
|
||||
// By this point anything cancelling the event has returned on the method, meaning the teleport is a success \o/
|
||||
this.stateSuccess(teleportee.getName(), toWorld.getAlias());
|
||||
Logging.fine("Teleport result: %s", entryResult);
|
||||
}
|
||||
|
||||
private void stateSuccess(String playerName, String worldName) {
|
||||
@ -305,6 +294,10 @@ public class MVPlayerListener implements InjectableListener {
|
||||
if (event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
if (config.isUsingCustomPortalSearch()) {
|
||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||
}
|
||||
|
||||
MVWorld fromWorld = getWorldManager().getMVWorld(event.getFrom().getWorld().getName());
|
||||
MVWorld toWorld = getWorldManager().getMVWorld(event.getTo().getWorld().getName());
|
||||
if (event.getFrom().getWorld().equals(event.getTo().getWorld())) {
|
||||
@ -312,28 +305,14 @@ public class MVPlayerListener implements InjectableListener {
|
||||
Logging.finer("Player '" + event.getPlayer().getName() + "' is portaling to the same world.");
|
||||
return;
|
||||
}
|
||||
event.setCancelled(!pt.playerHasMoneyToEnter(fromWorld, toWorld, event.getPlayer(), event.getPlayer(), true));
|
||||
if (event.isCancelled()) {
|
||||
Logging.fine("Player '" + event.getPlayer().getName()
|
||||
+ "' was DENIED ACCESS to '" + event.getTo().getWorld().getName()
|
||||
+ "' because they don't have the FUNDS required to enter.");
|
||||
return;
|
||||
}
|
||||
if (config.getEnforceAccess()) {
|
||||
event.setCancelled(!pt.playerCanGoFromTo(fromWorld, toWorld, event.getPlayer(), event.getPlayer()));
|
||||
if (event.isCancelled()) {
|
||||
Logging.fine("Player '" + event.getPlayer().getName()
|
||||
+ "' was DENIED ACCESS to '" + event.getTo().getWorld().getName()
|
||||
+ "' because they don't have: multiverse.access." + event.getTo().getWorld().getName());
|
||||
}
|
||||
} else {
|
||||
Logging.fine("Player '" + event.getPlayer().getName()
|
||||
+ "' was allowed to go to '" + event.getTo().getWorld().getName()
|
||||
+ "' because enforceaccess is off.");
|
||||
}
|
||||
if (!config.isUsingCustomPortalSearch()) {
|
||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||
}
|
||||
|
||||
CheckResultChain entryResult = worldEntryCheckerProvider.forSender(event.getPlayer()).canEnterWorld(fromWorld, toWorld)
|
||||
.onFailure(results -> {
|
||||
event.setCancelled(true);
|
||||
getCommandManager().getCommandIssuer(event.getPlayer()).sendError(results.getLastResultMessage());
|
||||
});
|
||||
|
||||
Logging.fine("Teleport result: %s", entryResult);
|
||||
}
|
||||
|
||||
private void sendPlayerToDefaultWorld(final Player player) {
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.onarandombox.MultiverseCore.permissions;
|
||||
|
||||
public class CorePermissions {
|
||||
public static String WORLD_ACCESS = "multiverse.access";
|
||||
public static String WORLD_EXEMPT = "multiverse.exempt";
|
||||
public static String GAMEMODE_BYPASS = "mv.bypass.gamemode";
|
||||
public static String PLAYERLIMIT_BYPASS = "mv.bypass.playerlimit";
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.onarandombox.MultiverseCore.permissions;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
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) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.WORLD_ACCESS, world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasWorldExemptPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.WORLD_EXEMPT, world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasPlayerLimitBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.PLAYERLIMIT_BYPASS, world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, concatPermission(CorePermissions.GAMEMODE_BYPASS, world.getName()));
|
||||
}
|
||||
|
||||
private String concatPermission(String permission, String...child) {
|
||||
return permission + "." + String.join(".", child);
|
||||
}
|
||||
|
||||
private boolean hasPermission(CommandSender sender, String permission) {
|
||||
if (sender.hasPermission(permission)) {
|
||||
Logging.finer("Checking to see if sender [%s] has permission [%s]... YES", sender.getName(), permission);
|
||||
return true;
|
||||
}
|
||||
Logging.finer("Checking to see if sender [%s] has permission [%s]... NO", sender.getName(), permission);
|
||||
return false;
|
||||
}
|
||||
}
|
@ -80,7 +80,20 @@ public enum MVCorei18n implements MessageKeyProvider {
|
||||
|
||||
// debug command
|
||||
DEBUG_INFO_OFF,
|
||||
DEBUG_INFO_ON;
|
||||
DEBUG_INFO_ON,
|
||||
|
||||
// entry check
|
||||
ENTRYCHECK_BLACKLISTED,
|
||||
ENTRYCHECK_NOTENOUGHMONEY,
|
||||
ENTRYCHECK_CANNOTPAYENTRYFEE,
|
||||
ENTRYCHECK_EXCEEDPLAYERLIMIT,
|
||||
ENTRYCHECK_NOWORLDACCESS,
|
||||
|
||||
// generic
|
||||
GENERIC_SUCCESS,
|
||||
GENERIC_FAILURE
|
||||
|
||||
;
|
||||
|
||||
private final MessageKey key = MessageKey.of("mv-core." + this.name().replace('_', '.').toLowerCase());
|
||||
|
||||
|
@ -0,0 +1,143 @@
|
||||
package com.onarandombox.MultiverseCore.utils.checkresult;
|
||||
|
||||
import co.aikar.commands.CommandIssuer;
|
||||
import com.onarandombox.MultiverseCore.commandtools.PluginLocales;
|
||||
import com.onarandombox.MultiverseCore.utils.message.Message;
|
||||
import com.onarandombox.MultiverseCore.utils.message.MessageReplacement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public sealed interface CheckResult<S extends SuccessReason, F extends FailureReason> permits CheckResult.Success, CheckResult.Failure {
|
||||
static <F extends FailureReason, S extends SuccessReason> CheckResult<S, F> success(S successReason, MessageReplacement...replacements) {
|
||||
return new Success<>(successReason, replacements);
|
||||
}
|
||||
|
||||
static <F extends FailureReason, S extends SuccessReason> CheckResult<S, F> failure(F failureReason, MessageReplacement...replacements) {
|
||||
return new Failure<>(failureReason, replacements);
|
||||
}
|
||||
|
||||
boolean isSuccess();
|
||||
|
||||
boolean isFailure();
|
||||
|
||||
S getSuccessReason();
|
||||
|
||||
F getFailureReason();
|
||||
|
||||
@NotNull Message getReasonMessage();
|
||||
|
||||
default CheckResult<S, F> onSuccess(Consumer<S> consumer) {
|
||||
if (this.isSuccess()) {
|
||||
consumer.accept(this.getSuccessReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default CheckResult<S, F> onFailure(Consumer<F> consumer) {
|
||||
if (this.isFailure()) {
|
||||
consumer.accept(this.getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default CheckResult<S, F> onSuccessReason(S successReason, Consumer<S> consumer) {
|
||||
if (this.isSuccess() && this.getSuccessReason() == successReason) {
|
||||
consumer.accept(this.getSuccessReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default CheckResult<S, F> onFailureReason(F failureReason, Consumer<F> consumer) {
|
||||
if (this.isFailure() && this.getFailureReason() == failureReason) {
|
||||
consumer.accept(this.getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
final class Success<F extends FailureReason, S extends SuccessReason> implements CheckResult<S, F> {
|
||||
private final S successReason;
|
||||
private final MessageReplacement[] replacements;
|
||||
|
||||
public Success(S successReason, MessageReplacement[] replacements) {
|
||||
this.successReason = successReason;
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getSuccessReason() {
|
||||
return successReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
throw new NoSuchElementException("No reason for failure");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Message getReasonMessage() {
|
||||
return Message.of(successReason, "Success!", replacements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Success{" +
|
||||
"reason=" + successReason +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
final class Failure<S extends SuccessReason, F extends FailureReason> implements CheckResult<S, F> {
|
||||
private final F failureReason;
|
||||
private final MessageReplacement[] replacements;
|
||||
|
||||
public Failure(F failureReason, MessageReplacement[] replacements) {
|
||||
this.failureReason = failureReason;
|
||||
this.replacements = replacements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getSuccessReason() {
|
||||
throw new NoSuchElementException("No reason for success");
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
return failureReason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Message getReasonMessage() {
|
||||
return Message.of(failureReason, "Success!", replacements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Failure{" +
|
||||
"reason=" + failureReason +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
package com.onarandombox.MultiverseCore.utils.checkresult;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.onarandombox.MultiverseCore.utils.message.Message;
|
||||
import io.vavr.control.Option;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CheckResultChain {
|
||||
public static Builder builder() {
|
||||
return new Builder(true);
|
||||
}
|
||||
|
||||
public static Builder builder(boolean stopOnFailure) {
|
||||
return new Builder(stopOnFailure);
|
||||
}
|
||||
|
||||
private final boolean isSuccess;
|
||||
private final List<CheckResult<?, ?>> results;
|
||||
|
||||
CheckResultChain(boolean isSuccess, List<CheckResult<?, ?>> results) {
|
||||
this.isSuccess = isSuccess;
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
return !isSuccess;
|
||||
}
|
||||
|
||||
public CheckResultChain onSuccess(Runnable successRunnable) {
|
||||
if (isSuccess) {
|
||||
successRunnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckResultChain onFailure(Runnable failureRunnable) {
|
||||
if (isFailure()) {
|
||||
failureRunnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckResultChain onSuccess(Consumer<CheckResultChain> successRunnable) {
|
||||
if (isSuccess) {
|
||||
successRunnable.accept(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckResultChain onFailure(Consumer<CheckResultChain> failureRunnable) {
|
||||
if (isFailure()) {
|
||||
failureRunnable.accept(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> CheckResultChain onSuccessReason(Class<S> successReasonClass, Consumer<S> successConsumer) {
|
||||
getSuccessReason(successReasonClass).peek(successConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <F extends FailureReason> CheckResultChain onFailureReason(Class<F> failureReasonClass, Consumer<F> failureConsumer) {
|
||||
getFailureReason(failureReasonClass).peek(failureConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> CheckResultChain onSuccessReason(S successReason, Runnable successRunnable) {
|
||||
getSuccessReason(successReason.getClass()).filter(successReason::equals).peek(reason -> successRunnable.run());
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> Option<S> getSuccessReason(Class<S> successReasonClass) {
|
||||
if (isFailure()) {
|
||||
return Option.none();
|
||||
}
|
||||
return Option.ofOptional(results.stream()
|
||||
.map(CheckResult::getSuccessReason)
|
||||
.filter(successReasonClass::isInstance)
|
||||
.map(successReasonClass::cast)
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
public <F extends FailureReason> Option<F> getFailureReason(Class<F> failureReasonClass) {
|
||||
if (isSuccess()) {
|
||||
return Option.none();
|
||||
}
|
||||
return Option.ofOptional(results.stream()
|
||||
.map(CheckResult::getFailureReason)
|
||||
.filter(failureReasonClass::isInstance)
|
||||
.map(failureReasonClass::cast)
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
public Message getLastResultMessage() {
|
||||
return Iterables.getLast(results).getReasonMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResultGroup{" +
|
||||
"isSuccess=" + isSuccess +
|
||||
", results={" + results.stream().map(Objects::toString).collect(Collectors.joining(", ")) + "}" +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private final boolean stopOnFailure;
|
||||
private final List<CheckResult<?, ?>> results;
|
||||
|
||||
private boolean isSuccess = true;
|
||||
|
||||
public Builder(boolean stopOnFailure) {
|
||||
this.stopOnFailure = stopOnFailure;
|
||||
this.results = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Builder then(Supplier<CheckResult<?, ?>> resultSupplier) {
|
||||
if (!isSuccess && stopOnFailure) {
|
||||
return this;
|
||||
}
|
||||
CheckResult<?, ?> result = resultSupplier.get();
|
||||
if (result.isFailure()) {
|
||||
isSuccess = false;
|
||||
}
|
||||
results.add(result);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CheckResultChain build() {
|
||||
return new CheckResultChain(isSuccess, results);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.onarandombox.MultiverseCore.utils.checkresult;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
|
||||
public interface FailureReason extends MessageKeyProvider {
|
||||
default MessageKey getMessageKey() {
|
||||
return MVCorei18n.GENERIC_FAILURE.getMessageKey();
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package com.onarandombox.MultiverseCore.utils.checkresult;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
|
||||
public interface SuccessReason extends MessageKeyProvider {
|
||||
default MessageKey getMessageKey() {
|
||||
return MVCorei18n.GENERIC_SUCCESS.getMessageKey();
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
/**
|
||||
* Captures string replacements for {@link Message}s.
|
||||
*/
|
||||
public final class MessageReplacement {
|
||||
public final class MessageReplacement {
|
||||
|
||||
/**
|
||||
* Creates a replacement key for the given key string.
|
||||
|
@ -22,7 +22,7 @@ public class EntryFee extends SerializationConfig {
|
||||
@Nullable
|
||||
private Material currency;
|
||||
|
||||
private final Material DISABLED_MATERIAL = Material.AIR;
|
||||
public static final Material DISABLED_MATERIAL = Material.AIR;
|
||||
|
||||
public EntryFee() {
|
||||
super();
|
||||
|
@ -0,0 +1,30 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.SuccessReason;
|
||||
|
||||
public class BlacklistResult {
|
||||
public enum Success implements SuccessReason {
|
||||
UNKNOWN_FROM_WORLD,
|
||||
BYPASSED_BLACKLISTED,
|
||||
NOT_BLACKLISTED
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
BLACKLISTED(MVCorei18n.ENTRYCHECK_BLACKLISTED);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.SuccessReason;
|
||||
|
||||
public class EntryFeeResult {
|
||||
public enum Success implements SuccessReason {
|
||||
FREE_ENTRY,
|
||||
ENOUGH_MONEY,
|
||||
EXEMPT_FROM_ENTRY_FEE,
|
||||
CONSOLE_OR_BLOCK_COMMAND_SENDER
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
NOT_ENOUGH_MONEY(MVCorei18n.ENTRYCHECK_NOTENOUGHMONEY),
|
||||
CANNOT_PAY_ENTRY_FEE(MVCorei18n.ENTRYCHECK_CANNOTPAYENTRYFEE);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.SuccessReason;
|
||||
|
||||
public class PlayerLimitResult {
|
||||
public enum Success implements SuccessReason {
|
||||
NO_PLAYERLIMIT,
|
||||
WITHIN_PLAYERLIMIT,
|
||||
BYPASS_PLAYERLIMIT
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
EXCEED_PLAYERLIMIT(MVCorei18n.ENTRYCHECK_EXCEEDPLAYERLIMIT);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import co.aikar.locales.MessageKey;
|
||||
import co.aikar.locales.MessageKeyProvider;
|
||||
import com.onarandombox.MultiverseCore.utils.MVCorei18n;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.SuccessReason;
|
||||
|
||||
public class WorldAccessResult {
|
||||
public enum Success implements SuccessReason {
|
||||
NO_ENFORCE_WORLD_ACCESS,
|
||||
HAS_WORLD_ACCESS
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
NO_WORLD_ACCESS(MVCorei18n.ENTRYCHECK_NOWORLDACCESS);
|
||||
|
||||
private final MessageKeyProvider message;
|
||||
|
||||
Failure(MessageKeyProvider message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKey getMessageKey() {
|
||||
return message.getMessageKey();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package com.onarandombox.MultiverseCore.world.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.checkresult.CheckResult;
|
||||
import com.onarandombox.MultiverseCore.utils.checkresult.CheckResultChain;
|
||||
import com.onarandombox.MultiverseCore.utils.message.MessageReplacement;
|
||||
import com.onarandombox.MultiverseCore.world.configuration.EntryFee;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.BlockCommandSender;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.onarandombox.MultiverseCore.utils.message.MessageReplacement.replace;
|
||||
|
||||
public class WorldEntryChecker {
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull CorePermissionsChecker permissionsChecker;
|
||||
|
||||
private final @NotNull CommandSender sender;
|
||||
|
||||
public WorldEntryChecker(
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull CorePermissionsChecker permissionsChecker,
|
||||
@NotNull MVEconomist economist,
|
||||
@NotNull CommandSender sender
|
||||
) {
|
||||
this.config = config;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
this.economist = economist;
|
||||
this.sender = sender;
|
||||
}
|
||||
|
||||
public CheckResultChain canStayInWorld(@NotNull MVWorld world) {
|
||||
return canEnterWorld(null, world);
|
||||
}
|
||||
|
||||
public CheckResultChain canEnterWorld(@Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) {
|
||||
return CheckResultChain.builder()
|
||||
.then(() -> canAccessWorld(toWorld))
|
||||
.then(() -> isWithinPlayerLimit(toWorld))
|
||||
.then(() -> isNotBlacklisted(fromWorld, toWorld))
|
||||
.then(() -> canPayEntryFee(toWorld))
|
||||
.build();
|
||||
}
|
||||
|
||||
public CheckResult<WorldAccessResult.Success, WorldAccessResult.Failure> canAccessWorld(@NotNull MVWorld world) {
|
||||
if (!config.getEnforceAccess()) {
|
||||
return CheckResult.success(WorldAccessResult.Success.NO_ENFORCE_WORLD_ACCESS);
|
||||
}
|
||||
return permissionsChecker.hasWorldAccessPermission(this.sender, world)
|
||||
? CheckResult.success(WorldAccessResult.Success.HAS_WORLD_ACCESS)
|
||||
: CheckResult.failure(WorldAccessResult.Failure.NO_WORLD_ACCESS);
|
||||
}
|
||||
|
||||
public CheckResult<PlayerLimitResult.Success, PlayerLimitResult.Failure> isWithinPlayerLimit(@NotNull MVWorld world) {
|
||||
final int playerLimit = world.getPlayerLimit();
|
||||
if (playerLimit <= -1) {
|
||||
return CheckResult.success(PlayerLimitResult.Success.NO_PLAYERLIMIT);
|
||||
}
|
||||
if (permissionsChecker.hasPlayerLimitBypassPermission(sender, world)) {
|
||||
return CheckResult.success(PlayerLimitResult.Success.BYPASS_PLAYERLIMIT);
|
||||
}
|
||||
return playerLimit > world.getCBWorld().getPlayers().size()
|
||||
? CheckResult.success(PlayerLimitResult.Success.WITHIN_PLAYERLIMIT)
|
||||
: CheckResult.failure(PlayerLimitResult.Failure.EXCEED_PLAYERLIMIT);
|
||||
}
|
||||
|
||||
public CheckResult<BlacklistResult.Success, BlacklistResult.Failure> isNotBlacklisted(@Nullable MVWorld fromWorld, @NotNull MVWorld toWorld) {
|
||||
if (fromWorld == null) {
|
||||
return CheckResult.success(BlacklistResult.Success.UNKNOWN_FROM_WORLD);
|
||||
}
|
||||
return toWorld.getWorldBlacklist().contains(fromWorld.getName())
|
||||
? CheckResult.failure(BlacklistResult.Failure.BLACKLISTED, replace("{world}").with(fromWorld.getAlias()))
|
||||
: CheckResult.success(BlacklistResult.Success.NOT_BLACKLISTED);
|
||||
}
|
||||
|
||||
public CheckResult<EntryFeeResult.Success, EntryFeeResult.Failure> canPayEntryFee(MVWorld world) {
|
||||
double price = world.getPrice();
|
||||
Material currency = world.getCurrency();
|
||||
if (price == 0D && (currency == null || currency == EntryFee.DISABLED_MATERIAL)) {
|
||||
return CheckResult.success(EntryFeeResult.Success.FREE_ENTRY);
|
||||
}
|
||||
if (sender instanceof ConsoleCommandSender || sender instanceof BlockCommandSender) {
|
||||
return CheckResult.success(EntryFeeResult.Success.CONSOLE_OR_BLOCK_COMMAND_SENDER);
|
||||
}
|
||||
if (permissionsChecker.hasWorldExemptPermission(sender, world)) {
|
||||
return CheckResult.success(EntryFeeResult.Success.EXEMPT_FROM_ENTRY_FEE);
|
||||
}
|
||||
if (!(sender instanceof Player player)) {
|
||||
return CheckResult.failure(EntryFeeResult.Failure.CANNOT_PAY_ENTRY_FEE);
|
||||
}
|
||||
return economist.isPlayerWealthyEnough(player, price, currency)
|
||||
? CheckResult.success(EntryFeeResult.Success.ENOUGH_MONEY)
|
||||
: CheckResult.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY, replace("{amount}").with("$##")); //TODO
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.config.MVCoreConfig;
|
||||
import com.onarandombox.MultiverseCore.economy.MVEconomist;
|
||||
import com.onarandombox.MultiverseCore.permissions.CorePermissionsChecker;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
public class WorldEntryCheckerProvider {
|
||||
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull CorePermissionsChecker permissionsChecker;
|
||||
|
||||
@Inject
|
||||
WorldEntryCheckerProvider(
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull MVEconomist economist,
|
||||
@NotNull CorePermissionsChecker permissionsChecker
|
||||
) {
|
||||
this.config = config;
|
||||
this.economist = economist;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
}
|
||||
|
||||
public @NotNull WorldEntryChecker forSender(@NotNull CommandSender sender) {
|
||||
return new WorldEntryChecker(config, permissionsChecker, economist, sender);
|
||||
}
|
||||
}
|
@ -114,3 +114,14 @@ mv-core.unload.success=&aUnloaded world '{world}'!
|
||||
|
||||
# /mv usage
|
||||
mv-core.usage.description=Show Multiverse-Core command usage.
|
||||
|
||||
# entry check
|
||||
mv-core.entrycheck.blacklisted='{world}' is blacklisted.
|
||||
mv-core.entrycheck.notenoughmoney=you do not have enough money to pay entry fee. You are required to pay {amount}.
|
||||
mv-core.entrycheck.cannotpayentryfee=you do not have the ability to pay entry fee.
|
||||
mv-core.entrycheck.exceedplayerlimit=the world has reached its player limit.
|
||||
mv-core.entrycheck.noworldaccess=you do not have permissions to access the world.
|
||||
|
||||
# generic
|
||||
mv-core.generic.success=Success!
|
||||
mv-core.generic.failure=Failed!
|
||||
|
Loading…
Reference in New Issue
Block a user