All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.plotsquared.core.player.PlotPlayer Maven / Gradle / Ivy

There is a newer version: 7.4.0
Show newest version
/*
 * PlotSquared, a land and world management plugin for Minecraft.
 * Copyright (C) IntellectualSites 
 * Copyright (C) IntellectualSites team and contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package com.plotsquared.core.player;

import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;
import com.plotsquared.core.PlotSquared;
import com.plotsquared.core.collection.ByteArrayUtilities;
import com.plotsquared.core.command.CommandCaller;
import com.plotsquared.core.command.RequiredType;
import com.plotsquared.core.configuration.Settings;
import com.plotsquared.core.configuration.caption.Caption;
import com.plotsquared.core.configuration.caption.CaptionMap;
import com.plotsquared.core.configuration.caption.CaptionUtility;
import com.plotsquared.core.configuration.caption.LocaleHolder;
import com.plotsquared.core.configuration.caption.TranslatableCaption;
import com.plotsquared.core.database.DBFunc;
import com.plotsquared.core.events.TeleportCause;
import com.plotsquared.core.location.Location;
import com.plotsquared.core.permissions.NullPermissionProfile;
import com.plotsquared.core.permissions.PermissionHandler;
import com.plotsquared.core.permissions.PermissionProfile;
import com.plotsquared.core.plot.Plot;
import com.plotsquared.core.plot.PlotArea;
import com.plotsquared.core.plot.PlotCluster;
import com.plotsquared.core.plot.PlotId;
import com.plotsquared.core.plot.PlotWeather;
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
import com.plotsquared.core.plot.world.PlotAreaManager;
import com.plotsquared.core.plot.world.SinglePlotArea;
import com.plotsquared.core.plot.world.SinglePlotAreaManager;
import com.plotsquared.core.synchronization.LockRepository;
import com.plotsquared.core.util.EventDispatcher;
import com.plotsquared.core.util.query.PlotQuery;
import com.plotsquared.core.util.task.RunnableVal;
import com.plotsquared.core.util.task.TaskManager;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.world.gamemode.GameMode;
import com.sk89q.worldedit.world.item.ItemType;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.Tag;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.title.Title;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * The abstract class supporting {@code BukkitPlayer} and {@code SpongePlayer}.
 */
public abstract class PlotPlayer

implements CommandCaller, OfflinePlotPlayer, LocaleHolder { private static final String NON_EXISTENT_CAPTION = "PlotSquared does not recognize the caption: "; private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + PlotPlayer.class.getSimpleName()); // Used to track debug mode private static final Set> debugModeEnabled = Collections.synchronizedSet(new HashSet<>()); @SuppressWarnings("rawtypes") private static final Map, PlotPlayerConverter> converters = new HashMap<>(); private final LockRepository lockRepository = new LockRepository(); private final PlotAreaManager plotAreaManager; private final EventDispatcher eventDispatcher; private final PermissionHandler permissionHandler; private Map metaMap = new HashMap<>(); /** * The metadata map. */ private ConcurrentHashMap meta; private int hash; private Locale locale; // Delayed initialisation private PermissionProfile permissionProfile; public PlotPlayer( final @NonNull PlotAreaManager plotAreaManager, final @NonNull EventDispatcher eventDispatcher, final @NonNull PermissionHandler permissionHandler ) { this.plotAreaManager = plotAreaManager; this.eventDispatcher = eventDispatcher; this.permissionHandler = permissionHandler; } @SuppressWarnings({"rawtypes", "unchecked"}) public static PlotPlayer from(final @NonNull T object) { // fast path if (converters.containsKey(object.getClass())) { return converters.get(object.getClass()).convert(object); } // slow path, meant to only run once per object#getClass instance Queue> toVisit = new ArrayDeque<>(); toVisit.add(object.getClass()); Class current; while ((current = toVisit.poll()) != null) { PlotPlayerConverter converter = converters.get(current); if (converter != null) { if (current != object.getClass()) { // register shortcut for this sub type to avoid further loops converters.put(object.getClass(), converter); LOGGER.info("Registered {} as with converter for {}", object.getClass(), current); } return converter.convert(object); } // no converter found yet if (current.getSuperclass() != null) { toVisit.add(current.getSuperclass()); // add super class if available } toVisit.addAll(Arrays.asList(current.getInterfaces())); // add interfaces } throw new IllegalArgumentException(String .format( "There is no registered PlotPlayer converter for type %s", object.getClass().getSimpleName() )); } public static void registerConverter( final @NonNull Class clazz, final PlotPlayerConverter converter ) { converters.put(clazz, converter); } public static Collection> getDebugModePlayers() { return Collections.unmodifiableCollection(debugModeEnabled); } public static Collection> getDebugModePlayersInPlot(final @NonNull Plot plot) { if (debugModeEnabled.isEmpty()) { return Collections.emptyList(); } final Collection> players = new LinkedList<>(); for (final PlotPlayer player : debugModeEnabled) { if (player.getCurrentPlot().equals(plot)) { players.add(player); } } return players; } protected void setupPermissionProfile() { this.permissionProfile = permissionHandler.getPermissionProfile(this).orElse( NullPermissionProfile.INSTANCE); } @Override public final boolean hasPermission( final @Nullable String world, final @NonNull String permission ) { return this.permissionProfile.hasPermission(world, permission); } @Override public final boolean hasKeyedPermission( final @Nullable String world, final @NonNull String permission, final @NonNull String key ) { return this.permissionProfile.hasKeyedPermission(world, permission, key); } @Override public final boolean hasPermission(@NonNull String permission, boolean notify) { if (!hasPermission(permission)) { if (notify) { sendMessage( TranslatableCaption.of("permission.no_permission_event"), TagResolver.resolver("node", Tag.inserting(Component.text(permission))) ); } return false; } return true; } public abstract Actor toActor(); public abstract P getPlatformPlayer(); /** * Set some session only metadata for this player. * * @param key * @param value */ void setMeta(String key, Object value) { if (value == null) { deleteMeta(key); } else { if (this.meta == null) { this.meta = new ConcurrentHashMap<>(); } this.meta.put(key, value); } } /** * Get the session metadata for a key. * * @param key the name of the metadata key * @param the object type to return * @return the value assigned to the key or null if it does not exist */ @SuppressWarnings("unchecked") T getMeta(String key) { if (this.meta != null) { return (T) this.meta.get(key); } return null; } T getMeta(String key, T defaultValue) { T meta = getMeta(key); if (meta == null) { return defaultValue; } return meta; } public ConcurrentHashMap getMeta() { return meta; } /** * Delete the metadata for a key. * - metadata is session only * - deleting other plugin's metadata may cause issues * * @param key */ Object deleteMeta(String key) { return this.meta == null ? null : this.meta.remove(key); } /** * Returns the name of the player. * * @return the name of the player */ @Override public String toString() { return getName(); } /** * Get this player's current plot. * * @return the plot the player is standing on or null if standing on a road or not in a {@link PlotArea} */ public Plot getCurrentPlot() { try (final MetaDataAccess lastPlotAccess = this.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { if (lastPlotAccess.get().orElse(null) == null && !Settings.Enabled_Components.EVENTS) { return this.getLocation().getPlot(); } return lastPlotAccess.get().orElse(null); } } /** * Get the total number of allowed plots * * @return number of allowed plots within the scope (globally, or in the player's current world as defined in the settings.yml) */ public int getAllowedPlots() { final int calculatedLimit = hasPermissionRange("plots.plot", Settings.Limit.MAX_PLOTS); return this.eventDispatcher.callPlayerPlotLimit(this, calculatedLimit).limit(); } /** * Get the number of plots this player owns. * * @return number of plots within the scope (globally, or in the player's current world as defined in the settings.yml) * @see #getPlotCount(String) * @see #getPlots() */ public int getPlotCount() { if (!Settings.Limit.GLOBAL) { return getPlotCount(getLocation().getWorldName()); } final AtomicInteger count = new AtomicInteger(0); final UUID uuid = getUUID(); this.plotAreaManager.forEachPlotArea(value -> { if (!Settings.Done.COUNTS_TOWARDS_LIMIT) { for (Plot plot : value.getPlotsAbs(uuid)) { if (!DoneFlag.isDone(plot)) { count.incrementAndGet(); } } } else { count.addAndGet(value.getPlotsAbs(uuid).size()); } }); return count.get(); } public int getClusterCount() { if (!Settings.Limit.GLOBAL) { return getClusterCount(getLocation().getWorldName()); } final AtomicInteger count = new AtomicInteger(0); this.plotAreaManager.forEachPlotArea(value -> { for (PlotCluster cluster : value.getClusters()) { if (cluster.isOwner(getUUID())) { count.incrementAndGet(); } } }); return count.get(); } /** * Get the number of plots this player owns in the world. * * @param world the name of the plotworld to check. * @return plot count */ public int getPlotCount(String world) { UUID uuid = getUUID(); int count = 0; for (PlotArea area : this.plotAreaManager.getPlotAreasSet(world)) { if (!Settings.Done.COUNTS_TOWARDS_LIMIT) { count += area.getPlotsAbs(uuid).stream().filter(plot -> !DoneFlag.isDone(plot)).count(); } else { count += area.getPlotsAbs(uuid).size(); } } return count; } public int getClusterCount(String world) { int count = 0; for (PlotArea area : this.plotAreaManager.getPlotAreasSet(world)) { for (PlotCluster cluster : area.getClusters()) { if (cluster.isOwner(getUUID())) { count++; } } } return count; } /** * Get a {@link Set} of plots owned by this player. * *

* Take a look at {@link PlotSquared} for more searching functions. * See {@link #getPlotCount()} for the number of plots. *

* * @return a {@link Set} of plots owned by the player */ public Set getPlots() { return PlotQuery.newQuery().ownedBy(this).asSet(); } /** * Return the PlotArea this player is currently in, or null. * * @return Plot area the player is currently in, or {@code null} */ public @Nullable PlotArea getPlotAreaAbs() { return this.plotAreaManager.getPlotArea(getLocation()); } public PlotArea getApplicablePlotArea() { return this.plotAreaManager.getApplicablePlotArea(getLocation()); } @Override public @NonNull RequiredType getSuperCaller() { return RequiredType.PLAYER; } /** * Get this player's last recorded location or null if they don't any plot relevant location. * * @return The location */ public @NonNull Location getLocation() { Location location = getMeta("location"); if (location != null) { return location; } return getLocationFull(); } /////////////// PLAYER META /////////////// ////////////// PARTIALLY IMPLEMENTED /////////// /** * Get this player's full location (including yaw/pitch) * * @return location */ public abstract Location getLocationFull(); //////////////////////////////////////////////// /** * Get this player's UUID. * === !IMPORTANT ===
* The UUID is dependent on the mode chosen in the settings.yml and may not be the same as Bukkit has * (especially if using an old version of Bukkit that does not support UUIDs) * * @return UUID */ @Override public @NonNull abstract UUID getUUID(); public boolean canTeleport(final @NonNull Location location) { Preconditions.checkNotNull(location, "Specified location cannot be null"); final Location current = getLocationFull(); teleport(location); boolean result = getLocation().equals(location); teleport(current); return result; } /** * Teleport this player to a location. * * @param location the target location */ public void teleport(Location location) { teleport(location, TeleportCause.PLUGIN); } /** * Teleport this player to a location. * * @param location the target location * @param cause the cause of the teleport */ public abstract void teleport(Location location, TeleportCause cause); /** * Kick this player to a location * * @param location the target location */ public void plotkick(Location location) { setMeta("kick", true); teleport(location, TeleportCause.KICK); deleteMeta("kick"); } /** * Set this compass target. * * @param location the target location */ public abstract void setCompassTarget(Location location); /** * Set player data that will persist restarts. * - Please note that this is not intended to store large values * - For session only data use meta * * @param key metadata key */ public void setAttribute(String key) { setPersistentMeta("attrib_" + key, new byte[]{(byte) 1}); } /** * Retrieves the attribute of this player. * * @param key metadata key * @return the attribute will be either {@code true} or {@code false} */ public boolean getAttribute(String key) { if (!hasPersistentMeta("attrib_" + key)) { return false; } return getPersistentMeta("attrib_" + key)[0] == 1; } /** * Remove an attribute from a player. * * @param key metadata key */ public void removeAttribute(String key) { removePersistentMeta("attrib_" + key); } /** * Sets the local weather for this Player. * * @param weather the weather visible to the player */ public abstract void setWeather(@NonNull PlotWeather weather); /** * Get this player's gamemode. * * @return the gamemode of the player. */ public abstract @NonNull GameMode getGameMode(); /** * Set this player's gameMode. * * @param gameMode the gamemode to set */ public abstract void setGameMode(@NonNull GameMode gameMode); /** * Set this player's local time (ticks). * * @param time the time visible to the player */ public abstract void setTime(long time); /** * Determines whether or not the player can fly. * * @return {@code true} if the player is allowed to fly */ public abstract boolean getFlight(); /** * Sets whether or not this player can fly. * * @param fly {@code true} if the player can fly, otherwise {@code false} */ public abstract void setFlight(boolean fly); /** * Play music at a location for this player. * * @param location where to play the music * @param id the record item id */ public abstract void playMusic(@NonNull Location location, @NonNull ItemType id); /** * Check if this player is banned. * * @return {@code true} if the player is banned, {@code false} otherwise. */ public abstract boolean isBanned(); /** * Kick this player from the game. * * @param message the reason for the kick */ public abstract void kick(String message); public void refreshDebug() { final boolean debug = this.getAttribute("debug"); if (debug && !debugModeEnabled.contains(this)) { debugModeEnabled.add(this); } else if (!debug) { debugModeEnabled.remove(this); } } /** * Called when this player quits. */ public void unregister() { Plot plot = getCurrentPlot(); if (plot != null && Settings.Enabled_Components.PERSISTENT_META && plot .getArea() instanceof SinglePlotArea) { PlotId id = plot.getId(); int x = id.getX(); int z = id.getY(); ByteBuffer buffer = ByteBuffer.allocate(13); buffer.putShort((short) x); buffer.putShort((short) z); Location location = getLocation(); buffer.putInt(location.getX()); buffer.put((byte) location.getY()); buffer.putInt(location.getZ()); setPersistentMeta("quitLoc", buffer.array()); } else if (hasPersistentMeta("quitLoc")) { removePersistentMeta("quitLoc"); } if (plot != null) { this.eventDispatcher.callLeave(this, plot); } if (Settings.Enabled_Components.BAN_DELETER && isBanned()) { for (Plot owned : getPlots()) { owned.getPlotModificationManager().deletePlot(null, null); LOGGER.info("Plot {} was deleted + cleared due to {} getting banned", owned.getId(), getName()); } } if (PlotSquared.platform().expireManager() != null) { PlotSquared.platform().expireManager().storeDate(getUUID(), System.currentTimeMillis()); } PlotSquared.platform().playerManager().removePlayer(this); PlotSquared.platform().unregister(this); debugModeEnabled.remove(this); } /** * Get the amount of clusters this player owns in the specific world. * * @param world world * @return number of clusters owned */ public int getPlayerClusterCount(String world) { return PlotSquared.get().getClusters(world).stream() .filter(cluster -> getUUID().equals(cluster.owner)).mapToInt(PlotCluster::getArea) .sum(); } /** * Get the amount of clusters this player owns. * * @return the number of clusters this player owns */ public int getPlayerClusterCount() { final AtomicInteger count = new AtomicInteger(); this.plotAreaManager.forEachPlotArea(value -> count.addAndGet(value.getClusters().size())); return count.get(); } /** * Return a {@code Set} of all plots this player owns in a certain world. * * @param world the world to retrieve plots from * @return a {@code Set} of plots this player owns in the provided world */ public Set getPlots(String world) { return PlotQuery.newQuery().inWorld(world).ownedBy(getUUID()).asSet(); } public void populatePersistentMetaMap() { if (Settings.Enabled_Components.PERSISTENT_META) { DBFunc.getPersistentMeta(getUUID(), new RunnableVal<>() { @Override public void run(Map value) { try { PlotPlayer.this.metaMap = value; if (value.isEmpty()) { return; } if (PlotPlayer.this.getAttribute("debug")) { debugModeEnabled.add(PlotPlayer.this); } if (!Settings.Teleport.ON_LOGIN) { return; } PlotAreaManager manager = PlotPlayer.this.plotAreaManager; if (!(manager instanceof SinglePlotAreaManager)) { return; } PlotArea area = ((SinglePlotAreaManager) manager).getArea(); byte[] arr = PlotPlayer.this.getPersistentMeta("quitLoc"); if (arr == null) { return; } removePersistentMeta("quitLoc"); if (!getMeta("teleportOnLogin", true)) { return; } ByteBuffer quitWorld = ByteBuffer.wrap(arr); final int plotX = quitWorld.getShort(); final int plotZ = quitWorld.getShort(); PlotId id = PlotId.of(plotX, plotZ); int x = quitWorld.getInt(); int y = quitWorld.get() & 0xFF; int z = quitWorld.getInt(); Plot plot = area.getOwnedPlot(id); if (plot == null) { return; } final Location location = Location.at(plot.getWorldName(), x, y, z); if (plot.isLoaded()) { TaskManager.runTask(() -> { if (getMeta("teleportOnLogin", true)) { teleport(location, TeleportCause.LOGIN); sendMessage( TranslatableCaption.of("teleport.teleported_to_plot")); } }); } else if (!PlotSquared.get().isMainThread(Thread.currentThread())) { if (getMeta("teleportOnLogin", true)) { plot.teleportPlayer( PlotPlayer.this, result -> TaskManager.runTask(() -> { if (getMeta("teleportOnLogin", true)) { if (plot.isLoaded()) { teleport(location, TeleportCause.LOGIN); sendMessage(TranslatableCaption .of("teleport.teleported_to_plot")); } } }) ); } } } catch (Throwable e) { e.printStackTrace(); } } }); } } byte[] getPersistentMeta(String key) { return this.metaMap.get(key); } Object removePersistentMeta(String key) { final Object old = this.metaMap.remove(key); if (Settings.Enabled_Components.PERSISTENT_META) { DBFunc.removePersistentMeta(getUUID(), key); } return old; } /** * Access keyed persistent meta data for this player. This returns a meta data * access instance, that MUST be closed. It is meant to be used with try-with-resources, * like such: *
{@code
     * try (final MetaDataAccess access = player.accessPersistentMetaData(PlayerMetaKeys.GRANTS)) {
     *     int grants = access.get();
     *     access.set(grants + 1);
     * }
     * }
* * @param key Meta data key * @param Meta data type * @return Meta data access. MUST be closed after being used */ public @NonNull MetaDataAccess accessPersistentMetaData(final @NonNull MetaDataKey key) { return new PersistentMetaDataAccess<>(this, key, this.lockRepository.lock(key.getLockKey())); } /** * Access keyed temporary meta data for this player. This returns a meta data * access instance, that MUST be closed. It is meant to be used with try-with-resources, * like such: *
{@code
     * try (final MetaDataAccess access = player.accessTemporaryMetaData(PlayerMetaKeys.GRANTS)) {
     *     int grants = access.get();
     *     access.set(grants + 1);
     * }
     * }
* * @param key Meta data key * @param Meta data type * @return Meta data access. MUST be closed after being used */ public @NonNull MetaDataAccess accessTemporaryMetaData(final @NonNull MetaDataKey key) { return new TemporaryMetaDataAccess<>(this, key, this.lockRepository.lock(key.getLockKey())); } void setPersistentMeta( final @NonNull MetaDataKey key, final @NonNull T value ) { if (key.getType().getRawType().equals(Integer.class)) { this.setPersistentMeta(key.toString(), Ints.toByteArray((int) (Object) value)); } else if (key.getType().getRawType().equals(Boolean.class)) { this.setPersistentMeta(key.toString(), ByteArrayUtilities.booleanToBytes((boolean) (Object) value)); } else { throw new IllegalArgumentException(String.format("Unknown meta data type '%s'", key.getType())); } } @SuppressWarnings("unchecked") @Nullable T getPersistentMeta(final @NonNull MetaDataKey key) { final byte[] value = this.getPersistentMeta(key.toString()); if (value == null) { return null; } final Object returnValue; if (key.getType().getRawType().equals(Integer.class)) { returnValue = Ints.fromByteArray(value); } else if (key.getType().getRawType().equals(Boolean.class)) { returnValue = ByteArrayUtilities.bytesToBoolean(value); } else { throw new IllegalArgumentException(String.format("Unknown meta data type '%s'", key.getType())); } return (T) returnValue; } void setPersistentMeta(String key, byte[] value) { boolean delete = hasPersistentMeta(key); this.metaMap.put(key, value); if (Settings.Enabled_Components.PERSISTENT_META) { DBFunc.addPersistentMeta(getUUID(), key, value, delete); } } /** * Send a title to the player that fades in, in 10 ticks, stays for 50 ticks and fades * out in 20 ticks * * @param title Title text * @param subtitle Subtitle text * @param replacements Variable replacements */ public void sendTitle( final @NonNull Caption title, final @NonNull Caption subtitle, final @NonNull TagResolver... replacements ) { sendTitle( title, subtitle, Settings.Titles.TITLES_FADE_IN, Settings.Titles.TITLES_STAY, Settings.Titles.TITLES_FADE_OUT, replacements ); } /** * Send a title to the player * * @param title Title * @param subtitle Subtitle * @param fadeIn Fade in time (in ticks) * @param stay The title stays for (in ticks) * @param fadeOut Fade out time (in ticks) * @param replacements Variable replacements */ public void sendTitle( final @NonNull Caption title, final @NonNull Caption subtitle, final int fadeIn, final int stay, final int fadeOut, final @NonNull TagResolver... replacements ) { final Component titleComponent = MiniMessage.miniMessage().deserialize(title.getComponent(this), replacements); final Component subtitleComponent = MiniMessage.miniMessage().deserialize(subtitle.getComponent(this), replacements); final Title.Times times = Title.Times.times( Duration.of(Settings.Titles.TITLES_FADE_IN * 50L, ChronoUnit.MILLIS), Duration.of(Settings.Titles.TITLES_STAY * 50L, ChronoUnit.MILLIS), Duration.of(Settings.Titles.TITLES_FADE_OUT * 50L, ChronoUnit.MILLIS) ); getAudience().showTitle(Title .title(titleComponent, subtitleComponent, times)); } /** * Method designed to send an ActionBar to a player. * * @param caption Caption * @param replacements Variable replacements */ public void sendActionBar( final @NonNull Caption caption, final @NonNull TagResolver... replacements ) { String message; try { message = caption.getComponent(this); } catch (final CaptionMap.NoSuchCaptionException exception) { // This sends feedback to the player message = NON_EXISTENT_CAPTION + ((TranslatableCaption) caption).getKey(); // And this also prints it to the console exception.printStackTrace(); } if (message.isEmpty()) { return; } // Replace placeholders, etc message = CaptionUtility.format(this, message) .replace('\u2010', '%').replace('\u2020', '&').replace('\u2030', '&') .replace("", TranslatableCaption.of("core.prefix").getComponent(this)); final Component component = MiniMessage.miniMessage().deserialize(message, replacements); getAudience().sendActionBar(component); } @Override public void sendMessage( final @NonNull Caption caption, final @NonNull TagResolver... replacements ) { String message; try { message = caption.getComponent(this); } catch (final CaptionMap.NoSuchCaptionException exception) { // This sends feedback to the player message = NON_EXISTENT_CAPTION + ((TranslatableCaption) caption).getKey(); // And this also prints it to the console exception.printStackTrace(); } if (message.isEmpty()) { return; } // Replace placeholders, etc message = CaptionUtility.format(this, message) .replace('\u2010', '%').replace('\u2020', '&').replace('\u2030', '&') .replace("", TranslatableCaption.of("core.prefix").getComponent(this)); // Parse the message final Component component = MiniMessage.miniMessage().deserialize(message, replacements); if (!Objects.equal(component, this.getMeta("lastMessage")) || System.currentTimeMillis() - this.getMeta("lastMessageTime") > 5000) { setMeta("lastMessage", component); setMeta("lastMessageTime", System.currentTimeMillis()); getAudience().sendMessage(component); } } /** * Sends a message to the command caller, when the future is resolved * * @param caption Caption to send * @param asyncReplacement Async variable replacement * @return A Future to be resolved, after the message was sent * @since 7.1.0 */ public final CompletableFuture sendMessage( @NonNull Caption caption, CompletableFuture<@NonNull TagResolver> asyncReplacement ) { return sendMessage(caption, new CompletableFuture[]{asyncReplacement}); } /** * Sends a message to the command caller, when all futures are resolved * * @param caption Caption to send * @param asyncReplacements Async variable replacements * @param replacements Sync variable replacements * @return A Future to be resolved, after the message was sent * @since 7.1.0 */ public final CompletableFuture sendMessage( @NonNull Caption caption, CompletableFuture<@NonNull TagResolver>[] asyncReplacements, @NonNull TagResolver... replacements ) { return CompletableFuture.allOf(asyncReplacements).whenComplete((unused, throwable) -> { Set resolvers = new HashSet<>(Arrays.asList(replacements)); if (throwable != null) { sendMessage( TranslatableCaption.of("errors.error"), TagResolver.resolver("value", Tag.inserting( Component.text("Failed to resolve asynchronous caption replacements") )) ); LOGGER.error("Failed to resolve asynchronous tagresolver(s) for " + caption, throwable); } else { for (final CompletableFuture asyncReplacement : asyncReplacements) { resolvers.add(asyncReplacement.join()); } } sendMessage(caption, resolvers.toArray(TagResolver[]::new)); }); } // Redefine from PermissionHolder as it's required from CommandCaller @Override public boolean hasPermission(@NonNull String permission) { return hasPermission(null, permission); } boolean hasPersistentMeta(String key) { return this.metaMap.containsKey(key); } /** * Check if the player is able to see the other player. * This does not mean that the other player is in line of sight of the player, * but rather that the player is permitted to see the other player. * * @param other Other player * @return {@code true} if the player is able to see the other player, {@code false} if not */ public abstract boolean canSee(PlotPlayer other); public abstract void stopSpectating(); public boolean hasDebugMode() { return this.getAttribute("debug"); } @NonNull @Override public Locale getLocale() { if (this.locale == null) { this.locale = Locale.forLanguageTag(Settings.Enabled_Components.DEFAULT_LOCALE); } return this.locale; } @Override public void setLocale(final @NonNull Locale locale) { if (!PlotSquared.get().getCaptionMap(TranslatableCaption.DEFAULT_NAMESPACE).supportsLocale(locale)) { this.locale = Locale.forLanguageTag(Settings.Enabled_Components.DEFAULT_LOCALE); } else { this.locale = locale; } } @Override public int hashCode() { if (this.hash == 0 || this.hash == 485) { this.hash = 485 + this.getUUID().hashCode(); } return this.hash; } @Override public boolean equals(final Object obj) { if (!(obj instanceof final PlotPlayer other)) { return false; } return this.getUUID().equals(other.getUUID()); } /** * Get the {@link Audience} that represents this plot player * * @return Player audience */ public @NonNull abstract Audience getAudience(); /** * Get this player's {@link LockRepository} * * @return Lock repository instance */ public @NonNull LockRepository getLockRepository() { return this.lockRepository; } /** * Removes any effects present of the given type. * * @param name the name of the type to remove * @since 6.10.0 */ public abstract void removeEffect(@NonNull String name); @FunctionalInterface public interface PlotPlayerConverter { PlotPlayer convert(BaseObject object); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy