Compare commits
10
Commits
main
...
revamp-perms-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
787e9a81b0 | ||
|
|
fad2890e10 | ||
|
|
6e72e72fb0 | ||
|
|
ef14a398c3 | ||
|
|
302c22f690 | ||
|
|
f124dcab55 | ||
|
|
bf640e2b4d | ||
|
|
5f727889cc | ||
|
|
de16e97d88 | ||
|
|
4bfcd0d907 |
No files matched your search
@@ -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,16 +50,18 @@ 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());
|
||||
Logging.finer("Async teleport completed.");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -43,11 +43,7 @@ public class MVChatListener implements InjectableListener {
|
||||
// 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 world = event.getPlayer().getWorld().getName();
|
||||
String prefix = "";
|
||||
// If we're not a MV world, don't do anything
|
||||
if (!this.worldManager.isMVWorld(world)) {
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
/******************************************************************************
|
||||
* 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 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.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.permissions.PermissionsChecker;
|
||||
import com.onarandombox.MultiverseCore.utils.result.ResultGroup;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.BlacklistResult;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.EntryFeeResult;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.PlayerLimitResult;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.WorldAccessResult;
|
||||
import com.onarandombox.MultiverseCore.world.entrycheck.WorldEntryCheckerProvider;
|
||||
import io.vavr.control.Option;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.inject.Provider;
|
||||
import org.bukkit.GameMode;
|
||||
@@ -38,366 +34,298 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerPortalEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
/**
|
||||
* Multiverse's Listener for players.
|
||||
*/
|
||||
@Service
|
||||
public class MVPlayerListener implements InjectableListener {
|
||||
private final Plugin plugin;
|
||||
private final MVCoreConfig config;
|
||||
private final Provider<MVWorldManager> worldManagerProvider;
|
||||
private final PermissionTools pt;
|
||||
private final Provider<MVPermissions> mvPermsProvider;
|
||||
private final SafeTTeleporter safeTTeleporter;
|
||||
private final Server server;
|
||||
private final TeleportQueue teleportQueue;
|
||||
|
||||
private final Map<String, String> playerWorld = new ConcurrentHashMap<String, String>();
|
||||
private final @NotNull MultiverseCore plugin;
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull Server server;
|
||||
private final @NotNull TeleportQueue teleportQueue;
|
||||
private final @NotNull Provider<MVWorldManager> worldManagerProvider;
|
||||
private final @NotNull WorldEntryCheckerProvider worldEntryCheckerProvider;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull PermissionsChecker permissionsChecker;
|
||||
private final @NotNull SafeTTeleporter safeTTeleporter;
|
||||
|
||||
@Inject
|
||||
public MVPlayerListener(
|
||||
MultiverseCore plugin,
|
||||
MVCoreConfig config,
|
||||
Provider<MVWorldManager> worldManagerProvider,
|
||||
PermissionTools permissionTools,
|
||||
Provider<MVPermissions> mvPermsProvider,
|
||||
SafeTTeleporter safeTTeleporter,
|
||||
Server server,
|
||||
TeleportQueue teleportQueue
|
||||
) {
|
||||
MVPlayerListener(
|
||||
@NotNull MultiverseCore plugin,
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull Server server,
|
||||
@NotNull TeleportQueue teleportQueue,
|
||||
@NotNull Provider<MVWorldManager> worldManagerProvider,
|
||||
@NotNull WorldEntryCheckerProvider worldEntryCheckerProvider,
|
||||
@NotNull MVEconomist economist,
|
||||
@NotNull PermissionsChecker permissionsChecker,
|
||||
@NotNull SafeTTeleporter safeTTeleporter
|
||||
) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.worldManagerProvider = worldManagerProvider;
|
||||
this.pt = permissionTools;
|
||||
this.mvPermsProvider = mvPermsProvider;
|
||||
this.safeTTeleporter = safeTTeleporter;
|
||||
this.server = server;
|
||||
this.teleportQueue = teleportQueue;
|
||||
this.worldManagerProvider = worldManagerProvider;
|
||||
this.worldEntryCheckerProvider = worldEntryCheckerProvider;
|
||||
this.economist = economist;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
this.safeTTeleporter = safeTTeleporter;
|
||||
}
|
||||
|
||||
private MVWorldManager getWorldManager() {
|
||||
return worldManagerProvider.get();
|
||||
}
|
||||
@EventHandler
|
||||
public void playerJoin(PlayerJoinEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
Location spawnLocation = getWorldManager().getFirstSpawnWorld().getSpawnLocation();
|
||||
|
||||
private MVPermissions getMVPerms() {
|
||||
return mvPermsProvider.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the playerWorld-map
|
||||
*/
|
||||
public Map<String, String> getPlayerWorld() {
|
||||
return playerWorld;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a player respawns.
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void playerRespawn(PlayerRespawnEvent event) {
|
||||
World world = event.getPlayer().getWorld();
|
||||
MVWorld mvWorld = getWorldManager().getMVWorld(world.getName());
|
||||
// If it's not a World MV manages we stop.
|
||||
if (mvWorld == null) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (mvWorld.getBedRespawn() && (event.isBedSpawn() || event.isAnchorSpawn())) {
|
||||
Logging.finer("Player joined AGAIN!");
|
||||
MVWorld world = getWorldManager().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()
|
||||
.onSuccess(() -> oneTickLater(() -> handleGameModeAndFlight(player, world)))
|
||||
.onFailure(() -> {
|
||||
player.sendMessage("[MV] - Sorry you can't be in this world anymore!");
|
||||
oneTickLater(() -> player.teleport(spawnLocation));
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void playerRespawn(PlayerRespawnEvent event) {
|
||||
MVWorld playerWorld = getWorldManager().getMVWorld(event.getPlayer().getWorld());
|
||||
if (playerWorld == null) {
|
||||
// We don't want to do anything if the player is not in a world managed by MV.
|
||||
Logging.fine("Player respawned in a world not managed by MV.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerWorld.getBedRespawn() && (event.isBedSpawn() || event.isAnchorSpawn())) {
|
||||
Logging.fine("Spawning %s at their %s.", event.getPlayer().getName(), event.isBedSpawn() ? "BED" : "ANCHOR");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the instance of the World the player should respawn at.
|
||||
MVWorld respawnWorld = null;
|
||||
if (getWorldManager().isMVWorld(mvWorld.getRespawnToWorld())) {
|
||||
respawnWorld = getWorldManager().getMVWorld(mvWorld.getRespawnToWorld());
|
||||
}
|
||||
|
||||
// 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 has been set to the appropriate world
|
||||
Location respawnLocation = getMostAccurateRespawnLocation(world);
|
||||
|
||||
MVRespawnEvent respawnEvent = new MVRespawnEvent(respawnLocation, event.getPlayer(), "compatability");
|
||||
Location respawnLocation = getMostAccurateRespawnLocation(playerWorld.getRespawnToWorld());
|
||||
MVRespawnEvent respawnEvent = new MVRespawnEvent(respawnLocation, event.getPlayer(), "compatability"); //TODO: Update this event with proper respawn method
|
||||
this.server.getPluginManager().callEvent(respawnEvent);
|
||||
event.setRespawnLocation(respawnEvent.getPlayersRespawnLocation());
|
||||
}
|
||||
|
||||
private Location getMostAccurateRespawnLocation(World w) {
|
||||
MVWorld mvw = getWorldManager().getMVWorld(w.getName());
|
||||
if (mvw != null) {
|
||||
return mvw.getSpawnLocation();
|
||||
}
|
||||
return w.getSpawnLocation();
|
||||
private Location getMostAccurateRespawnLocation(@NotNull World respawnWorld) {
|
||||
return Option.of(getWorldManager().getMVWorld(respawnWorld))
|
||||
.map(MVWorld::getSpawnLocation)
|
||||
.getOrElse(respawnWorld.getSpawnLocation());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a player joins the server.
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@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);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
Logging.finer("Player joined AGAIN!");
|
||||
if (config.getEnforceAccess() // check this only if we're enforcing access!
|
||||
&& !this.getMVPerms().hasPermission(p, "multiverse.access." + p.getWorld().getName(), false)) {
|
||||
p.sendMessage("[MV] - Sorry you can't be in this world anymore!");
|
||||
this.sendPlayerToDefaultWorld(p);
|
||||
}
|
||||
}
|
||||
// Handle the Players GameMode setting for the new world.
|
||||
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
|
||||
playerWorld.put(p.getName(), p.getWorld().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a player changes worlds.
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void playerChangedWorld(PlayerChangedWorldEvent event) {
|
||||
// Permissions now determine whether or not to handle a gamemode.
|
||||
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
|
||||
playerWorld.put(event.getPlayer().getName(), event.getPlayer().getWorld().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when a player teleports anywhere.
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void playerTeleport(PlayerTeleportEvent event) {
|
||||
Logging.finer("Got teleport event for player '"
|
||||
+ event.getPlayer().getName() + "' with cause '" + event.getCause() + "'");
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
Player teleportee = event.getPlayer();
|
||||
CommandSender teleporter = null;
|
||||
Optional<String> teleporterName = teleportQueue.popFromQueue(teleportee.getName());
|
||||
if (teleporterName.isPresent()) {
|
||||
if (teleporterName.equals("CONSOLE")) {
|
||||
Logging.finer("We know the teleporter is the console! Magical!");
|
||||
teleporter = this.server.getConsoleSender();
|
||||
} else {
|
||||
teleporter = this.server.getPlayerExact(teleporterName.get());
|
||||
}
|
||||
}
|
||||
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());
|
||||
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 "
|
||||
+ "actions will be taken by Multiverse-Core.");
|
||||
return;
|
||||
}
|
||||
if (event.getFrom().getWorld().equals(event.getTo().getWorld())) {
|
||||
// The player is Teleporting to the same world.
|
||||
Logging.finer("Player '" + teleportee.getName() + "' is teleporting to the same world.");
|
||||
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());
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
private void stateSuccess(String playerName, String worldName) {
|
||||
Logging.fine("MV-Core is allowing Player '" + playerName
|
||||
+ "' to go to '" + worldName + "'.");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to adjust the portal location to the actual portal location (and not
|
||||
* right outside of it.
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void playerPortalCheck(PlayerPortalEvent event) {
|
||||
if (event.isCancelled() || event.getFrom() == null) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// REMEMBER! getTo MAY be NULL HERE!!!
|
||||
// If the player was actually outside of the portal, adjust the from location
|
||||
if (event.getFrom().getWorld().getBlockAt(event.getFrom()).getType() != Material.NETHER_PORTAL) {
|
||||
Location newloc = this.safeTTeleporter.findPortalBlockNextTo(event.getFrom());
|
||||
Location newloc = safeTTeleporter.findPortalBlockNextTo(event.getFrom());
|
||||
// TODO: Fix this. Currently, we only check for PORTAL blocks. I'll have to figure out what
|
||||
// TODO: we want to do here.
|
||||
if (newloc != null) {
|
||||
event.setFrom(newloc);
|
||||
}
|
||||
}
|
||||
// Wait for the adjust, then return!
|
||||
if (event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This method is called when a player actually portals via a vanilla style portal.
|
||||
* @param event The Event that was fired.
|
||||
*/
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void playerPortal(PlayerPortalEvent event) {
|
||||
if (event.isCancelled() || (event.getFrom() == null)) {
|
||||
return;
|
||||
}
|
||||
// The adjust should have happened much earlier.
|
||||
if (event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
MVWorld fromWorld = getWorldManager().getMVWorld(event.getFrom().getWorld().getName());
|
||||
MVWorld toWorld = getWorldManager().getMVWorld(event.getTo().getWorld().getName());
|
||||
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.");
|
||||
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.");
|
||||
// We don't want to do anything if the event is already cancelled.
|
||||
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.");
|
||||
|
||||
Location toLocation = event.getTo();
|
||||
if (toLocation == null) {
|
||||
// We don't want to do anything if the destination is null.
|
||||
Logging.fine("Player '%s' entering a portal with null destination location.", event.getPlayer().getName());
|
||||
return;
|
||||
}
|
||||
if (!config.isUsingCustomPortalSearch()) {
|
||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||
|
||||
Player player = event.getPlayer();
|
||||
|
||||
MVWorld fromWorld = getWorldManager().getMVWorld(event.getFrom().getWorld());
|
||||
MVWorld toWorld = getWorldManager().getMVWorld(toLocation.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 teleporting to world '%s' which is not managed by Multiverse-Core. No further actions will be taken by Multiverse-Core.",
|
||||
player.getName(), toLocation.getWorld());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void sendPlayerToDefaultWorld(final Player player) {
|
||||
// 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());
|
||||
}
|
||||
}, 1L);
|
||||
}
|
||||
|
||||
// FOLLOWING 2 Methods and Private class handle Per Player GameModes.
|
||||
private void handleGameModeAndFlight(Player player, World world) {
|
||||
|
||||
MVWorld mvWorld = getWorldManager().getMVWorld(world.getName());
|
||||
if (mvWorld != null) {
|
||||
this.handleGameModeAndFlight(player, mvWorld);
|
||||
} else {
|
||||
Logging.finer("Not handling gamemode and flight for world '" + world.getName()
|
||||
+ "' not managed by Multiverse.");
|
||||
if (toWorld.equals(fromWorld)) {
|
||||
// We don't want to do anything if the destination world is the same as the origin world.
|
||||
Logging.fine("Player '%s' is teleporting to the same world.", player.getName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the gamemode for the specified {@link Player}.
|
||||
* @param player The {@link Player}.
|
||||
* @param world The world the player is in.
|
||||
*/
|
||||
public void handleGameModeAndFlight(final Player player, final MVWorld 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 (!MVPlayerListener.this.pt.playerCanIgnoreGameModeRestriction(world, player)) {
|
||||
// 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()) {
|
||||
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!");
|
||||
}
|
||||
ResultGroup worldEntryResult = worldEntryCheckerProvider.forWorld(player, toWorld).canEnterWorld(fromWorld)
|
||||
.onSuccess(() -> {
|
||||
Logging.fine("Player '%s' is allowed to use portals to enter world '%s'.", player.getName(), toWorld.getName());
|
||||
if (!config.isUsingCustomPortalSearch()) {
|
||||
event.setSearchRadius(config.getCustomPortalSearchRadius());
|
||||
}
|
||||
}, 1L);
|
||||
})
|
||||
.onFailure(() -> {
|
||||
event.setCancelled(true);
|
||||
Logging.fine("Player '%s' is not allowed to use portals to enter world '%s'.", player.getName(), toWorld.getName());
|
||||
});
|
||||
|
||||
Logging.finer("Portal entry result for player '%s', from '%s' to '%s': %s",
|
||||
player.getName(), fromWorld == null ? "null" : fromWorld.getName(), toWorld.getName(), worldEntryResult);
|
||||
|
||||
sendWorldEntryMessage(player, player, fromWorld, toWorld, worldEntryResult);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void playerTeleport(PlayerTeleportEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
// We don't want to do anything if the event is already cancelled.
|
||||
return;
|
||||
}
|
||||
|
||||
Player teleportee = event.getPlayer();
|
||||
|
||||
Option<String> teleporterName = teleportQueue.popFromQueue(teleportee.getName());
|
||||
if (teleporterName.isEmpty() && !config.getTeleportIntercept()) {
|
||||
// We don't want to do anything if teleport interception is disabled, and teleport is not by MV.
|
||||
return;
|
||||
}
|
||||
|
||||
CommandSender teleporter = teleporterName.map(name -> name.equals("CONSOLE")
|
||||
? server.getConsoleSender()
|
||||
: server.getPlayerExact(name))
|
||||
.getOrElse(teleportee);
|
||||
|
||||
Logging.fine("Inferred teleporter '%s' for teleportee '%s'.", teleporter.getName(), teleportee.getName());
|
||||
|
||||
Option<MVWorld> fromWorld = Option.of(getWorldManager().getMVWorld(event.getFrom().getWorld()));
|
||||
MVWorld toWorld = Option.of(event.getTo()).map(to -> getWorldManager().getMVWorld(to.getWorld())).getOrNull();
|
||||
|
||||
if (toWorld == null) {
|
||||
// We don't want to do anything if the destination world is not managed by MV.
|
||||
Logging.fine("Player '%s' is teleporting to world '%s' which is not managed by Multiverse-Core. No further actions will be taken by Multiverse-Core.",
|
||||
teleportee.getName(), event.getTo().getWorld());
|
||||
return;
|
||||
}
|
||||
if (fromWorld.filter(world -> world.equals(toWorld)).isDefined()) {
|
||||
// We don't want to do anything if the destination world is the same as the origin world.
|
||||
Logging.fine("Player '%s' is teleporting to the same world.", teleportee.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
ResultGroup worldEntryResult = worldEntryCheckerProvider.forWorld(teleportee, toWorld)
|
||||
.canEnterWorld(fromWorld.getOrNull())
|
||||
.onSuccess(() -> Logging.fine("MV-Core is allowing '%s' to go to '%s'.", teleportee.getName(), toWorld.getName()))
|
||||
.onSuccessReason(EntryFeeResult.Success.ENOUGH_MONEY, () -> {
|
||||
economist.payEntryFee((Player) teleporter, toWorld);
|
||||
//TODO send payment message
|
||||
})
|
||||
.onFailure(() -> {
|
||||
event.setCancelled(true);
|
||||
Logging.fine("MV-Core is denying '%s' from going to '%s'.", teleportee.getName(), toWorld.getName());
|
||||
});
|
||||
|
||||
Logging.fine("World entry result for player '%s', from '%s' to '%s': %s",
|
||||
teleportee.getName(), fromWorld.map(MVWorld::getName).getOrNull(), toWorld.getName(), worldEntryResult);
|
||||
|
||||
sendWorldEntryMessage(teleporter, teleportee, fromWorld.getOrNull(), toWorld, worldEntryResult);
|
||||
}
|
||||
|
||||
private void sendWorldEntryMessage(CommandSender teleporter, Player teleportee, MVWorld fromWorld, MVWorld toWorld, ResultGroup worldEntryResult) {
|
||||
if (worldEntryResult.isSuccess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (teleportee.equals(teleporter)) {
|
||||
teleporter.sendMessage("You are unable to teleport to world '%s'".formatted(toWorld.getName()));
|
||||
} else {
|
||||
teleporter.sendMessage("You are unable to teleport '%s' to world '%s'".formatted(teleportee.getName(), toWorld.getName()));
|
||||
}
|
||||
|
||||
worldEntryResult
|
||||
.onFailureReason(WorldAccessResult.Failure.class, reason -> {
|
||||
teleporter.sendMessage("You do not have permission to access world '%s'".formatted(toWorld.getName()));
|
||||
})
|
||||
.onFailureReason(PlayerLimitResult.Failure.class, reason -> {
|
||||
teleporter.sendMessage("World '%s' is full".formatted(toWorld.getName()));
|
||||
})
|
||||
.onFailureReason(EntryFeeResult.Failure.class, reason -> {
|
||||
teleporter.sendMessage("You do not have enough money to pay world '%s' entry fee.".formatted(toWorld.getName()));
|
||||
//TODO Formatted entry fee amount
|
||||
})
|
||||
.onFailureReason(BlacklistResult.Failure.class, reason -> {
|
||||
teleporter.sendMessage("World '%s' is blacklisted by world '%s'".formatted(fromWorld.getName(), toWorld.getName()));
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void playerChangedWorld(PlayerChangedWorldEvent event) {
|
||||
MVWorld toWorld = getWorldManager().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);
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
|
||||
private MVWorldManager getWorldManager() {
|
||||
return worldManagerProvider.get();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import io.vavr.control.Option;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
@@ -32,12 +33,12 @@ public class TeleportQueue {
|
||||
* @param playerName The teleported player (the teleportee).
|
||||
* @return The player that teleported the other one (the teleporter).
|
||||
*/
|
||||
public Optional<String> popFromQueue(String playerName) {
|
||||
public Option<String> popFromQueue(String playerName) {
|
||||
if (teleportQueue.containsKey(playerName)) {
|
||||
String teleportee = teleportQueue.get(playerName);
|
||||
teleportQueue.remove(playerName);
|
||||
return Optional.of(teleportee);
|
||||
return Option.of(teleportee);
|
||||
}
|
||||
return Optional.empty();
|
||||
return Option.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.onarandombox.MultiverseCore.utils.permissions;
|
||||
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.bukkit.permissions.PermissionDefault;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ChildPermission extends Permission {
|
||||
|
||||
protected final Permission wildcard = new Permission(this.getName() + ".*", this.getDescription(), this.getDefault());
|
||||
|
||||
public ChildPermission(@NotNull String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable String description) {
|
||||
super(name, description);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable PermissionDefault defaultValue) {
|
||||
super(name, defaultValue);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable String description, @Nullable PermissionDefault defaultValue) {
|
||||
super(name, description, defaultValue);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable Map<String, Boolean> children) {
|
||||
super(name, children);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable String description, @Nullable Map<String, Boolean> children) {
|
||||
super(name, description, children);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable PermissionDefault defaultValue, @Nullable Map<String, Boolean> children) {
|
||||
super(name, defaultValue, children);
|
||||
}
|
||||
|
||||
public ChildPermission(@NotNull String name, @Nullable String description, @Nullable PermissionDefault defaultValue, @Nullable Map<String, Boolean> children) {
|
||||
super(name, description, defaultValue, children);
|
||||
}
|
||||
|
||||
public ChildPermission child(@NotNull String name) {
|
||||
return new ChildPermission(
|
||||
this.getName() + "." + name,
|
||||
this.getDescription(),
|
||||
this.getDefault()
|
||||
);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package com.onarandombox.MultiverseCore.utils.permissions;
|
||||
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
public class MVCorePermissions {
|
||||
public final ChildPermission WORLD_ACCESS = new ChildPermission("multiverse.access");
|
||||
public final ChildPermission WORLD_EXEMPT = new ChildPermission("multiverse.exempt");
|
||||
public final ChildPermission GAMEMODE_BYPASS = new ChildPermission("mv.bypass.gamemode");
|
||||
public final ChildPermission PLAYERLIMIT_BYPASS = new ChildPermission("mv.bypass.playerlimit");
|
||||
public final ChildPermission TELEPORT_SELF = new ChildPermission("multiverse.teleport.self");
|
||||
public final ChildPermission TELEPORT_OTHER = new ChildPermission("multiverse.teleport.other");
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.onarandombox.MultiverseCore.utils.permissions;
|
||||
|
||||
import com.dumptruckman.minecraft.util.Logging;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import com.onarandombox.MultiverseCore.destination.ParsedDestination;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.permissions.Permission;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
public class PermissionsChecker {
|
||||
|
||||
private final MVCorePermissions permissions;
|
||||
|
||||
@Inject
|
||||
PermissionsChecker(MVCorePermissions permissions) {
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
public boolean hasWorldAccessPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, permissions.WORLD_ACCESS.child(world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasWorldExemptPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, permissions.WORLD_EXEMPT.child(world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasPlayerLimitBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, permissions.PLAYERLIMIT_BYPASS.child(world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasGameModeBypassPermission(@NotNull CommandSender sender, @NotNull MVWorld world) {
|
||||
return hasPermission(sender, permissions.GAMEMODE_BYPASS.child(world.getName()));
|
||||
}
|
||||
|
||||
public boolean hasTeleportPermission(@NotNull CommandSender sender, @NotNull ParsedDestination<?> destination, boolean self) {
|
||||
ChildPermission permission = self ? permissions.TELEPORT_SELF : permissions.TELEPORT_OTHER;
|
||||
if (!hasPermission(sender, permission.child(destination.getIdentifier()))) {
|
||||
return false;
|
||||
}
|
||||
if (destination.getFinerPermissionSuffix() == null) {
|
||||
return true;
|
||||
}
|
||||
return hasPermission(sender, permission.child(destination.getIdentifier()).child(destination.getFinerPermissionSuffix()));
|
||||
}
|
||||
|
||||
private boolean hasPermission(CommandSender sender, Permission permission) {
|
||||
if (sender.hasPermission(permission)) {
|
||||
Logging.finer("Checking to see if sender [%s] has permission [%s]... YES", sender.getName(), permission.getName());
|
||||
return true;
|
||||
}
|
||||
Logging.finer("Checking to see if sender [%s] has permission [%s]... NO", sender.getName(), permission.getName());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
public interface FailureReason {
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
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) {
|
||||
return new Success<>(successReason);
|
||||
}
|
||||
|
||||
static <F extends FailureReason, S extends SuccessReason> Result<S, F> failure(F failureReason) {
|
||||
return new Failure<>(failureReason);
|
||||
}
|
||||
|
||||
boolean isSuccess();
|
||||
|
||||
boolean isFailure();
|
||||
|
||||
S getSuccessReason();
|
||||
|
||||
F getFailureReason();
|
||||
|
||||
default Result<S, F> onSuccess(Consumer<S> consumer) {
|
||||
if (this.isSuccess()) {
|
||||
consumer.accept(this.getSuccessReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default Result<S, F> onFailure(Consumer<F> consumer) {
|
||||
if (this.isFailure()) {
|
||||
consumer.accept(this.getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default Result<S, F> onSuccessReason(S successReason, Consumer<S> consumer) {
|
||||
if (this.isSuccess() && this.getSuccessReason() == successReason) {
|
||||
consumer.accept(this.getSuccessReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
default Result<S, F> onFailureReason(F failureReason, Consumer<F> consumer) {
|
||||
if (this.isFailure() && this.getFailureReason() == failureReason) {
|
||||
consumer.accept(this.getFailureReason());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
record Success<F extends FailureReason, S extends SuccessReason>(S getSuccessReason) implements Result<S, F> {
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public F getFailureReason() {
|
||||
throw new NoSuchElementException("No failure reason for success");
|
||||
}
|
||||
}
|
||||
|
||||
record Failure<S extends SuccessReason, F extends FailureReason>(F getFailureReason) implements Result<S, F> {
|
||||
@Override
|
||||
public boolean isSuccess() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFailure() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getSuccessReason() {
|
||||
throw new NoSuchElementException("No success reason for failure");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
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 ResultGroup {
|
||||
|
||||
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<Result<?, ?>> results;
|
||||
|
||||
ResultGroup(boolean isSuccess, List<Result<?, ?>> results) {
|
||||
this.isSuccess = isSuccess;
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
return !isSuccess;
|
||||
}
|
||||
|
||||
public ResultGroup onSuccess(Runnable successRunnable) {
|
||||
if (isSuccess) {
|
||||
successRunnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultGroup onFailure(Runnable failureRunnable) {
|
||||
if (isFailure()) {
|
||||
failureRunnable.run();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> ResultGroup onSuccessReason(Class<S> successReasonClass, Consumer<S> successConsumer) {
|
||||
getSuccessReason(successReasonClass).peek(successConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <F extends FailureReason> ResultGroup onFailureReason(Class<F> failureReasonClass, Consumer<F> failureConsumer) {
|
||||
getFailureReason(failureReasonClass).peek(failureConsumer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S extends SuccessReason> ResultGroup 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(Result::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(Result::getFailureReason)
|
||||
.filter(failureReasonClass::isInstance)
|
||||
.map(failureReasonClass::cast)
|
||||
.findFirst());
|
||||
}
|
||||
|
||||
@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<Result<?, ?>> results;
|
||||
|
||||
private boolean isSuccess = true;
|
||||
|
||||
public Builder(boolean stopOnFailure) {
|
||||
this.stopOnFailure = stopOnFailure;
|
||||
this.results = new ArrayList<>();
|
||||
}
|
||||
|
||||
public Builder then(Supplier<Result<?, ?>> resultSupplier) {
|
||||
if (!isSuccess && stopOnFailure) {
|
||||
return this;
|
||||
}
|
||||
Result<?, ?> result = resultSupplier.get();
|
||||
if (result.isFailure()) {
|
||||
isSuccess = false;
|
||||
}
|
||||
results.add(result);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResultGroup build() {
|
||||
return new ResultGroup(isSuccess, results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.onarandombox.MultiverseCore.utils.result;
|
||||
|
||||
public interface SuccessReason {
|
||||
}
|
||||
@@ -8,7 +8,6 @@
|
||||
package com.onarandombox.MultiverseCore.world;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -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,16 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class BlacklistResult {
|
||||
public enum Success implements SuccessReason {
|
||||
UNKNOWN_FROM_WORLD,
|
||||
BYPASSED_BLACKLISTED,
|
||||
NOT_BLACKLISTED
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
BLACKLISTED
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.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,
|
||||
CANNOT_PAY_ENTRY_FEE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class PlayerLimitResult {
|
||||
public enum Success implements SuccessReason {
|
||||
NO_PLAYERLIMIT,
|
||||
WITHIN_PLAYERLIMIT,
|
||||
BYPASS_PLAYERLIMIT
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
EXCEED_PLAYERLIMIT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.onarandombox.MultiverseCore.world.entrycheck;
|
||||
|
||||
import com.onarandombox.MultiverseCore.utils.result.FailureReason;
|
||||
import com.onarandombox.MultiverseCore.utils.result.SuccessReason;
|
||||
|
||||
public class WorldAccessResult {
|
||||
public enum Success implements SuccessReason {
|
||||
NO_ENFORCE_WORLD_ACCESS,
|
||||
HAS_WORLD_ACCESS
|
||||
}
|
||||
|
||||
public enum Failure implements FailureReason {
|
||||
NO_WORLD_ACCESS
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
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.utils.permissions.PermissionsChecker;
|
||||
import com.onarandombox.MultiverseCore.utils.result.Result;
|
||||
import com.onarandombox.MultiverseCore.utils.result.ResultGroup;
|
||||
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;
|
||||
|
||||
public class WorldEntryChecker {
|
||||
private final @NotNull CommandSender sender;
|
||||
private final @NotNull MVWorld toWorld;
|
||||
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull PermissionsChecker permissionsChecker;
|
||||
|
||||
public WorldEntryChecker(
|
||||
@NotNull CommandSender sender,
|
||||
@NotNull MVWorld toWorld,
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull PermissionsChecker permissionsChecker,
|
||||
@NotNull MVEconomist economist
|
||||
) {
|
||||
this.sender = sender;
|
||||
this.toWorld = toWorld;
|
||||
this.config = config;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
this.economist = economist;
|
||||
}
|
||||
|
||||
public ResultGroup canStayInWorld() {
|
||||
return canStayInWorld(true);
|
||||
}
|
||||
|
||||
public ResultGroup canStayInWorld(boolean stopOnFailure) {
|
||||
return ResultGroup.builder(stopOnFailure)
|
||||
.then(this::canAccessWorld)
|
||||
.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)
|
||||
.build();
|
||||
}
|
||||
|
||||
public Result<WorldAccessResult.Success, WorldAccessResult.Failure> canAccessWorld() {
|
||||
if (!config.getEnforceAccess()) {
|
||||
return Result.success(WorldAccessResult.Success.NO_ENFORCE_WORLD_ACCESS);
|
||||
}
|
||||
return permissionsChecker.hasWorldAccessPermission(this.sender, this.toWorld)
|
||||
? Result.success(WorldAccessResult.Success.HAS_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() {
|
||||
double price = toWorld.getPrice();
|
||||
Material currency = toWorld.getCurrency();
|
||||
if (price == 0D && (currency == null || currency == EntryFee.DISABLED_MATERIAL)) {
|
||||
return Result.success(EntryFeeResult.Success.FREE_ENTRY);
|
||||
}
|
||||
if (sender instanceof ConsoleCommandSender || sender instanceof BlockCommandSender) {
|
||||
return Result.success(EntryFeeResult.Success.CONSOLE_OR_BLOCK_COMMAND_SENDER);
|
||||
}
|
||||
if (permissionsChecker.hasWorldExemptPermission(sender, toWorld)) {
|
||||
return Result.success(EntryFeeResult.Success.EXEMPT_FROM_ENTRY_FEE);
|
||||
}
|
||||
if (!(sender instanceof Player player)) {
|
||||
return Result.failure(EntryFeeResult.Failure.CANNOT_PAY_ENTRY_FEE);
|
||||
}
|
||||
return economist.isPlayerWealthyEnough(player, price, currency)
|
||||
? Result.success(EntryFeeResult.Success.ENOUGH_MONEY)
|
||||
: Result.failure(EntryFeeResult.Failure.NOT_ENOUGH_MONEY);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
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.utils.permissions.PermissionsChecker;
|
||||
import jakarta.inject.Inject;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jvnet.hk2.annotations.Service;
|
||||
|
||||
@Service
|
||||
public class WorldEntryCheckerProvider {
|
||||
|
||||
private final @NotNull MVCoreConfig config;
|
||||
private final @NotNull MVEconomist economist;
|
||||
private final @NotNull PermissionsChecker permissionsChecker;
|
||||
|
||||
@Inject
|
||||
WorldEntryCheckerProvider(
|
||||
@NotNull MVCoreConfig config,
|
||||
@NotNull MVEconomist economist,
|
||||
@NotNull PermissionsChecker permissionsChecker
|
||||
) {
|
||||
this.config = config;
|
||||
this.economist = economist;
|
||||
this.permissionsChecker = permissionsChecker;
|
||||
}
|
||||
|
||||
public @NotNull WorldEntryChecker forWorld(@NotNull CommandSender sender, @NotNull MVWorld toWorld) {
|
||||
return new WorldEntryChecker(sender, toWorld, config, permissionsChecker, economist);
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.onarandombox.MultiverseCore.MultiverseCore;
|
||||
import com.onarandombox.MultiverseCore.api.MVWorld;
|
||||
import com.onarandombox.MultiverseCore.listeners.MVEntityListener;
|
||||
import com.onarandombox.MultiverseCore.listeners.MVPlayerListener;
|
||||
import com.onarandombox.MultiverseCore.listeners.MVWeatherListener;
|
||||
import com.onarandombox.MultiverseCore.utils.file.FileUtils;
|
||||
import com.onarandombox.MultiverseCore.world.SimpleMVWorldManager;
|
||||
import junit.framework.Assert;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
@@ -4,7 +4,6 @@ import com.onarandombox.MultiverseCore.anchor.AnchorManager
|
||||
import com.onarandombox.MultiverseCore.api.BlockSafety
|
||||
import com.onarandombox.MultiverseCore.api.Destination
|
||||
import com.onarandombox.MultiverseCore.api.LocationManipulation
|
||||
import com.onarandombox.MultiverseCore.api.MVConfig
|
||||
import com.onarandombox.MultiverseCore.api.MVWorldManager
|
||||
import com.onarandombox.MultiverseCore.api.SafeTTeleporter
|
||||
import com.onarandombox.MultiverseCore.commandtools.MVCommandManager
|
||||
@@ -14,11 +13,11 @@ import com.onarandombox.MultiverseCore.config.MVCoreConfig
|
||||
import com.onarandombox.MultiverseCore.economy.MVEconomist
|
||||
import com.onarandombox.MultiverseCore.listeners.MVChatListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVEntityListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVPlayerListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVPortalListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVWeatherListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVWorldInitListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVWorldListener
|
||||
import com.onarandombox.MultiverseCore.listeners.MVPlayerListener
|
||||
import com.onarandombox.MultiverseCore.teleportation.SimpleBlockSafety
|
||||
import com.onarandombox.MultiverseCore.teleportation.SimpleLocationManipulation
|
||||
import com.onarandombox.MultiverseCore.teleportation.SimpleSafeTTeleporter
|
||||
@@ -27,9 +26,9 @@ import com.onarandombox.MultiverseCore.utils.UnsafeCallWrapper
|
||||
import com.onarandombox.MultiverseCore.utils.metrics.MetricsConfigurator
|
||||
import com.onarandombox.MultiverseCore.world.SimpleMVWorldManager
|
||||
import org.mvplugins.multiverse.core.TestWithMockBukkit
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class InjectionTest : TestWithMockBukkit() {
|
||||
@@ -89,7 +88,7 @@ class InjectionTest : TestWithMockBukkit() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `MVPlayerListener is available as a service`() {
|
||||
fun `NewMVPlayerListener is available as a service`() {
|
||||
assertNotNull(multiverseCore.getService(MVPlayerListener::class.java))
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user