/* * This file is part of GriefDefender, licensed under the MIT License (MIT). * * Copyright (c) bloodmc * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.griefdefender; import java.lang.ref.WeakReference; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.reflect.TypeToken; import com.griefdefender.api.Tristate; import com.griefdefender.api.User; import com.griefdefender.api.claim.Claim; import com.griefdefender.api.claim.ClaimType; import com.griefdefender.api.claim.ShovelType; import com.griefdefender.api.claim.ShovelTypes; import com.griefdefender.api.data.PlayerData; import com.griefdefender.api.permission.Context; import com.griefdefender.api.permission.option.Options; import com.griefdefender.api.permission.option.type.CreateModeType; import com.griefdefender.api.permission.option.type.CreateModeTypes; import com.griefdefender.api.permission.option.type.GameModeType; import com.griefdefender.api.permission.option.type.GameModeTypes; import com.griefdefender.api.permission.option.type.WeatherType; import com.griefdefender.cache.EventResultCache; import com.griefdefender.cache.MessageCache; import com.griefdefender.cache.PermissionHolderCache; import com.griefdefender.claim.GDClaim; import com.griefdefender.configuration.MessageStorage; import com.griefdefender.internal.block.BlockSnapshot; import com.griefdefender.internal.block.BlockTransaction; import com.griefdefender.internal.util.NMSUtil; import com.griefdefender.permission.GDPermissionManager; import com.griefdefender.permission.GDPermissionUser; import com.griefdefender.permission.GDPermissions; import com.griefdefender.permission.option.GDOptions; import com.griefdefender.provider.VaultProvider; import com.griefdefender.storage.BaseStorage; import com.griefdefender.task.ClaimVisualRevertTask; import com.griefdefender.util.PermissionUtil; import net.kyori.text.Component; public class GDPlayerData implements PlayerData { public UUID playerID; public UUID worldUniqueId; private String worldName; private WeakReference playerSubject; private Set claimList; private Set optionContexts; public Location lastAfkCheckLocation; public Location lastShovelLocation; public Location endShovelLocation; public Location lastValidInspectLocation; public Location lastNonAirInspectLocation; public boolean isInvestigating = false; public boolean claimMode = false; public boolean claimTool = true; public ShovelType shovelMode = ShovelTypes.BASIC; public GDClaim claimResizing; public GDClaim claimSubdividing; public Map createBlockVisualRevertRunnables = new HashMap<>(); public Map> createBlockVisualTransactions = new HashMap<>(); public Map claimVisualRevertTasks = new HashMap<>(); public Map> visualClaimBlocks = new HashMap<>(); public List queuedVisuals = new ArrayList<>(); public UUID tempVisualUniqueId = null; public UUID petRecipientUniqueId; private final VaultProvider vaultProvider = GriefDefenderPlugin.getInstance().getVaultProvider(); public boolean ignoreClaims = false; public boolean debugClaimPermissions = false; public boolean inTown = false; public boolean townChat = false; public boolean lockPlayerDeathDrops = false; public boolean trappedRequest = false; public List chatLines = new ArrayList<>(); public Instant recordChatTimestamp; public Instant commandInputTimestamp; public String commandInput; public Consumer commandConsumer; // Always ignore active contexts by default // This prevents protection issues when other plugins call getActiveContext public boolean ignoreActiveContexts = true; public EventResultCache eventResultCache; // collide event cache public int lastCollideEntityId = 0; public boolean lastCollideEntityResult = false; private String playerName; public boolean allowFlight = false; public boolean ignoreFallDamage = false; public boolean inLiquid = false; public GameModeType lastGameMode = GameModeTypes.UNDEFINED; // teleport data public int teleportDelay = 0; public Location teleportSourceLocation; public Location teleportLocation; public Instant lastPvpTimestamp; public Instant lastTrappedTimestamp; // cached global option values public int minClaimLevel; private CreateModeType optionClaimCreateMode; // cached permission values // admin cache public boolean canManageAdminClaims = false; public boolean canManageWilderness = false; public boolean canManageGlobalOptions = false; public boolean canManageAdminOptions = false; public boolean canManageOverrideOptions = false; public boolean canManageFlagDefaults = false; public boolean canManageFlagOverrides = false; public boolean bypassBorderCheck = false; public boolean ignoreAdminClaims = false; public boolean ignoreBasicClaims = false; public boolean ignoreTowns = false; public boolean ignoreWilderness = false; // user cache public boolean userOptionPerkFlyOwner = false; public boolean userOptionPerkFlyAccessor = false; public boolean userOptionPerkFlyBuilder = false; public boolean userOptionPerkFlyContainer = false; public boolean userOptionPerkFlyManager = false; public boolean userOptionBypassPlayerDenyFlight = false; public boolean userOptionBypassPlayerDenyGodmode = false; public boolean userOptionBypassPlayerGamemode = false; // option cache public Boolean optionNoGodMode = null; public Double optionFlySpeed = null; public Double optionWalkSpeed = null; public GameModeType optionGameModeType = null; public WeatherType optionWeatherType = null; public boolean dataInitialized = false; public boolean showNoClaimsFoundMessage = true; public boolean useRestoreSchematic = false; private final int worldMaxHeight; public GDPlayerData(UUID worldUniqueId, String worldName, UUID playerUniqueId, Set claims) { this.worldUniqueId = worldUniqueId; this.worldName = worldName; this.playerID = playerUniqueId; this.claimList = claims; final Set contexts = new HashSet<>(); if (!BaseStorage.USE_GLOBAL_PLAYER_STORAGE) { contexts.add(new Context("server", PermissionUtil.getInstance().getServerName())); contexts.add(new Context("world", this.worldName.toLowerCase())); } else { final String contextType = GriefDefenderPlugin.getGlobalConfig().getConfig().playerdata.contextType; if (contextType.equalsIgnoreCase("world")) { contexts.add(new Context("world", this.worldName.toLowerCase())); } else if (contextType.equalsIgnoreCase("global")) { contexts.add(new Context("server", "global")); } else { contexts.add(new Context("server", PermissionUtil.getInstance().getServerName())); } } this.worldMaxHeight = GriefDefenderPlugin.getInstance().dataStore.getClaimWorldManager(this.worldUniqueId).getWorldMaxHeight(); this.optionContexts = contexts; this.refreshPlayerOptions(); } // Run async public void refreshPlayerOptions() { //final GriefDefenderConfig activeConfig = GriefDefenderPlugin.getActiveConfig(this.worldUniqueId); GriefDefenderPlugin.getInstance().executor.execute(() -> { if (this.playerSubject == null || this.playerSubject.get() == null) { GDPermissionUser subject = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID); this.playerSubject = new WeakReference<>(subject); } final GDPermissionUser subject = this.playerSubject.get(); final Set activeContexts = new HashSet<>(); PermissionUtil.getInstance().addActiveContexts(activeContexts, subject); // admin permissions this.bypassBorderCheck = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.BYPASS_BORDER_CHECK, activeContexts).asBoolean(); this.ignoreAdminClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_ADMIN, activeContexts).asBoolean(); this.ignoreTowns = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_TOWN, activeContexts).asBoolean(); this.ignoreWilderness = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_WILDERNESS, activeContexts).asBoolean(); this.ignoreBasicClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.IGNORE_CLAIMS_BASIC, activeContexts).asBoolean(); this.canManageAdminClaims = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.COMMAND_ADMIN_CLAIMS, activeContexts).asBoolean(); this.canManageWilderness = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_WILDERNESS, activeContexts).asBoolean(); this.canManageOverrideOptions = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_OVERRIDE_OPTIONS, activeContexts).asBoolean(); this.canManageGlobalOptions = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_GLOBAL_OPTIONS, activeContexts).asBoolean(); this.canManageAdminOptions = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_ADMIN_OPTIONS, activeContexts).asBoolean(); this.canManageFlagDefaults = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_FLAG_DEFAULTS, activeContexts).asBoolean(); this.canManageFlagOverrides = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.MANAGE_FLAG_OVERRIDES, activeContexts).asBoolean(); // user permissions this.userOptionPerkFlyOwner = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.USER_OPTION_PERK_FLY_OWNER, activeContexts).asBoolean(); this.userOptionPerkFlyManager = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.USER_OPTION_PERK_FLY_MANAGER, activeContexts).asBoolean(); this.userOptionPerkFlyBuilder = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.USER_OPTION_PERK_FLY_BUILDER, activeContexts).asBoolean(); this.userOptionPerkFlyContainer = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.USER_OPTION_PERK_FLY_CONTAINER, activeContexts).asBoolean(); this.userOptionPerkFlyAccessor = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.USER_OPTION_PERK_FLY_ACCESSOR, activeContexts).asBoolean(); this.userOptionBypassPlayerDenyFlight = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.BYPASS_OPTION + "." + Options.PLAYER_DENY_FLIGHT.getName().toLowerCase(), activeContexts).asBoolean(); this.userOptionBypassPlayerDenyGodmode = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.BYPASS_OPTION + "." + Options.PLAYER_DENY_GODMODE.getName().toLowerCase(), activeContexts).asBoolean(); this.userOptionBypassPlayerGamemode = PermissionUtil.getInstance().getPermissionValue(subject, GDPermissions.BYPASS_OPTION + "." + Options.PLAYER_GAMEMODE.getName().toLowerCase(), activeContexts).asBoolean(); this.playerID = subject.getUniqueId(); this.dataInitialized = true; }); } @Override public User getUser() { return this.getSubject(); } @Override public String getName() { if (this.playerName == null) { GDPermissionUser user = null; if (this.playerSubject != null) { user = this.playerSubject.get(); } if (user == null) { user = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID); } if (user != null) { this.playerName = user.getFriendlyName(); } if (this.playerName == null) { this.playerName = "[unknown]"; } } return this.playerName; } @Override public void revertAllVisuals() { this.lastShovelLocation = null; this.claimResizing = null; this.claimSubdividing = null; final Player player = this.getSubject().getOnlinePlayer(); if (player == null) { return; } if (this.visualClaimBlocks.isEmpty()) { return; } for (Map.Entry mapEntry : this.claimVisualRevertTasks.entrySet()) { mapEntry.getValue().cancel(); } final List visualIds = new ArrayList<>(this.visualClaimBlocks.keySet()); for (UUID visualUniqueId : visualIds) { final Claim claim = GriefDefenderPlugin.getInstance().dataStore.getClaim(player.getWorld().getUID(), visualUniqueId); this.revertVisualBlocks(player, (GDClaim) claim, visualUniqueId); } if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) { GriefDefenderPlugin.getInstance().getWorldEditProvider().revertAllVisuals(this.playerID); } // Revert any temp visuals this.revertTempVisuals(); } public void revertTempVisuals() { if (this.tempVisualUniqueId != null) { this.revertClaimVisual(null, this.tempVisualUniqueId); this.tempVisualUniqueId = null; } } @Override public void revertVisual(UUID uuid) { final Claim claim = GriefDefenderPlugin.getInstance().dataStore.getClaim(this.worldUniqueId, uuid); this.revertClaimVisual((GDClaim) claim, uuid); } @Override public void revertVisual(Claim claim) { this.revertClaimVisual((GDClaim) claim); } public void revertClaimVisual(GDClaim claim) { this.revertClaimVisual(claim, claim.getUniqueId()); } public void revertClaimVisual(GDClaim claim, UUID visualUniqueId) { final Player player = this.getSubject().getOnlinePlayer(); if (player == null) { return; } for (Map.Entry mapEntry : this.claimVisualRevertTasks.entrySet()) { if (visualUniqueId.equals(mapEntry.getKey())) { mapEntry.getValue().cancel(); break; } } if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) { GriefDefenderPlugin.getInstance().getWorldEditProvider().revertClaimCUIVisual(visualUniqueId, this.playerID); } this.revertVisualBlocks(player, claim, visualUniqueId); } private void revertVisualBlocks(Player player, GDClaim claim, UUID visualUniqueId) { final List visualTransactions = this.visualClaimBlocks.get(visualUniqueId); if (visualTransactions == null || visualTransactions.isEmpty()) { return; } // Gather create block visuals final List createBlockVisualTransactions = new ArrayList<>(); for (Runnable runnable : this.createBlockVisualRevertRunnables.values()) { final ClaimVisualRevertTask revertTask = (ClaimVisualRevertTask) runnable; if (revertTask.getVisualUniqueId().equals(visualUniqueId)) { continue; } final List blockTransactions = this.createBlockVisualTransactions.get(revertTask.getVisualUniqueId()); if (blockTransactions != null) { createBlockVisualTransactions.addAll(blockTransactions); } } for (int i = 0; i < visualTransactions.size(); i++) { BlockSnapshot snapshot = visualTransactions.get(i).getOriginal(); // If original block does not exist or chunk is not loaded, do not send to player if (!snapshot.matchesWorldState()) { if (claim != null) { claim.markVisualDirty = true; } continue; } boolean ignoreVisual = false; for (BlockTransaction createVisualTransaction : createBlockVisualTransactions) { if (createVisualTransaction.getOriginal().getLocation().equals(snapshot.getLocation())) { ignoreVisual = true; break; } } if (ignoreVisual) { continue; } NMSUtil.getInstance().sendBlockChange(player, snapshot); } if (claim != null) { claim.playersWatching.remove(this.playerID); } this.claimVisualRevertTasks.remove(visualUniqueId); this.visualClaimBlocks.remove(visualUniqueId); this.createBlockVisualRevertRunnables.remove(visualUniqueId); this.createBlockVisualTransactions.remove(visualUniqueId); } @Override public int getBlocksAccruedPerHour() { final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.BLOCKS_ACCRUED_PER_HOUR); if (value == null) { return Options.BLOCKS_ACCRUED_PER_HOUR.getDefaultValue(); } return value; } @Override public int getChestClaimExpiration() { final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.CHEST_EXPIRATION); if (value == null) { return Options.CHEST_EXPIRATION.getDefaultValue(); } return value; } @Override public int getCreateClaimLimit(ClaimType type) { final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.CREATE_LIMIT, type); if (value == null) { return Options.CREATE_LIMIT.getDefaultValue(); } return value; } public CreateModeType getCreateMode() { final CreateModeType value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(CreateModeType.class), this.getSubject(), Options.CREATE_MODE); if (value == null || value == CreateModeTypes.UNDEFINED) { return CreateModeTypes.AREA; } return value; } @Override public int getInitialClaimBlocks() { final Integer value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.INITIAL_BLOCKS); if (value == null) { return Options.INITIAL_BLOCKS.getDefaultValue(); } return value; } public double getInternalEconomyBlockCost() { final Double value = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_COST); if (value == null) { return Options.ECONOMY_BLOCK_COST.getDefaultValue(); } return value; } public int getInternalRemainingClaimBlocks() { final int initialClaimBlocks = this.getInitialClaimBlocks(); int remainingBlocks = initialClaimBlocks + this.getAccruedClaimBlocks() + this.getBonusClaimBlocks(); for (Claim claim : this.claimList) { if (claim.isSubdivision()) { continue; } GDClaim gpClaim = (GDClaim) claim; if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) { remainingBlocks -= claim.getClaimBlocks(); } } return remainingBlocks; } @Override public int getRemainingClaimBlocks() { final int initialClaimBlocks = this.getInitialClaimBlocks(); int remainingBlocks = initialClaimBlocks + this.getAccruedClaimBlocks() + this.getBonusClaimBlocks(); if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { return this.getInternalEconomyAvailablePurchaseCost(); } else { for (Claim claim : this.claimList) { if (claim.isSubdivision()) { continue; } GDClaim gpClaim = (GDClaim) claim; if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) { remainingBlocks -= claim.getClaimBlocks(); } } } return remainingBlocks; } public int getInternalEconomyAvailablePurchaseCost() { if (GriefDefenderPlugin.getInstance().isEconomyModeEnabled()) { if (!this.vaultProvider.getApi().hasAccount(this.getSubject().getOfflinePlayer())) { return 0; } final double currentFunds = this.vaultProvider.getApi().getBalance(this.getSubject().getOfflinePlayer()); final Double economyBlockCost = this.getInternalEconomyBlockCost(); return (int) Math.round((currentFunds / economyBlockCost)); } return 0; } public int getTotalClaimsCost() { int totalCost = 0; for (Claim claim : this.claimList) { if (claim.isSubdivision()) { continue; } final GDClaim gpClaim = (GDClaim) claim; if ((gpClaim.parent == null || gpClaim.parent.isAdminClaim()) && claim.getData().requiresClaimBlocks()) { totalCost += claim.getClaimBlocks(); } } return totalCost; } public double getRemainingChunks() { final double remainingChunks = this.getRemainingClaimBlocks() / 65536.0; return Math.round(remainingChunks * 100.0)/100.0; } @Override public int getAccruedClaimBlocks() { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.ACCRUED_BLOCKS, new HashSet<>(this.optionContexts)); } public boolean addAccruedClaimBlocks(int newAccruedClaimBlocks) { int currentTotal = this.getAccruedClaimBlocks(); if ((currentTotal + newAccruedClaimBlocks) > this.getMaxAccruedClaimBlocks()) { return false; } this.setAccruedClaimBlocks(currentTotal + newAccruedClaimBlocks); return true; } public boolean setAccruedClaimBlocks(int newAccruedClaimBlocks) { return this.setAccruedClaimBlocks(newAccruedClaimBlocks, true); } public boolean setAccruedClaimBlocks(int newAccruedClaimBlocks, boolean checkMax) { if (checkMax && newAccruedClaimBlocks > this.getMaxAccruedClaimBlocks()) { return false; } GDPermissionManager.getInstance().setOption(Options.ACCRUED_BLOCKS, this.getSubject(), String.valueOf(newAccruedClaimBlocks), new HashSet<>(this.optionContexts)); return true; } public int getBonusClaimBlocks() { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.BONUS_BLOCKS, new HashSet<>(this.optionContexts)); } public void setBonusClaimBlocks(int bonusClaimBlocks) { GDPermissionManager.getInstance().setOption(Options.BONUS_BLOCKS, this.getSubject(), String.valueOf(bonusClaimBlocks), new HashSet<>(this.optionContexts)); } public CreateModeType getClaimCreateMode() { if (this.optionClaimCreateMode == null) { CreateModeType mode = this.getCreateMode(); // default to 0 if invalid if (mode == null) { mode = CreateModeTypes.AREA; } this.optionClaimCreateMode = mode; } return this.optionClaimCreateMode; } public void setClaimCreateMode(CreateModeType mode) { this.optionClaimCreateMode = mode; } public boolean canCreateClaim(Player player) { return canCreateClaim(player, false); } public boolean canCreateClaim(Player player, boolean sendMessage) { final CreateModeType createMode = this.getClaimCreateMode(); if (this.shovelMode == ShovelTypes.BASIC) { if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.CLAIM_CREATE_BASIC)) { if (sendMessage) { GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_CREATE); } return false; } if (createMode == CreateModeTypes.VOLUME && !player.hasPermission(GDPermissions.CLAIM_CUBOID_BASIC)) { if (sendMessage) { GriefDefenderPlugin.sendMessage(player,MessageCache.getInstance().PERMISSION_CUBOID); GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CUBOID_DISABLED); } return false; } } else if (this.shovelMode == ShovelTypes.SUBDIVISION) { if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.CLAIM_CREATE_SUBDIVISION)) { if (sendMessage) { GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CLAIM_CREATE); } return false; } else if (createMode == CreateModeTypes.VOLUME && !player.hasPermission(GDPermissions.CLAIM_CUBOID_SUBDIVISION)) { if (sendMessage) { GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().PERMISSION_CUBOID); GriefDefenderPlugin.sendMessage(player, MessageCache.getInstance().COMMAND_CUBOID_DISABLED); } return false; } } else if (this.shovelMode == ShovelTypes.ADMIN) { if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.COMMAND_ADMIN_CLAIMS)) { return false; } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_ADMIN)) { return false; } } else if (this.shovelMode == ShovelTypes.TOWN) { if (createMode == CreateModeTypes.AREA && !player.hasPermission(GDPermissions.CLAIM_CREATE_TOWN)) { return false; } else if (!player.hasPermission(GDPermissions.CLAIM_CUBOID_TOWN)) { return false; } } return true; } public Set getClaims() { return ImmutableSet.copyOf(this.claimList); } public Set getInternalClaims() { return this.claimList; } public int getClaimTypeCount(ClaimType type) { int count = 0; for (Claim claim : this.claimList) { if (claim.getType() == type) { count++; } } return count; } public void setLastCollideEntityData(int entityId, boolean result) { this.lastCollideEntityId = entityId; this.lastCollideEntityResult = result; } public void setIgnoreClaims(boolean flag) { this.ignoreClaims = flag; } @Override public boolean canIgnoreClaim(Claim claim) { if (claim == null || this.ignoreClaims == false) { return false; } if (claim.isAdminClaim()) { return this.ignoreAdminClaims; } else if (claim.isWilderness()) { return this.ignoreWilderness; } else if (claim.isTown()) { return this.ignoreTowns; } return this.ignoreBasicClaims; } @Override public int getMaxAccruedClaimBlocks() { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_ACCRUED_BLOCKS); } @Override public double getAbandonedReturnRatio(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ABANDON_RETURN_RATIO); } @Override public int getMaxClaimX(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_SIZE_X, type); } @Override public int getMaxClaimY(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_SIZE_Y, type); } @Override public int getMaxClaimZ(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_SIZE_Z, type); } @Override public int getMinClaimX(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_SIZE_X, type); } @Override public int getMinClaimY(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_SIZE_Y, type); } @Override public int getMinClaimZ(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_SIZE_Z, type); } @Override public int getMaxClaimLevel() { int maxClaimLevel = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MAX_LEVEL); if (this.worldMaxHeight > -1 && this.worldMaxHeight < maxClaimLevel) { maxClaimLevel = this.worldMaxHeight; } return maxClaimLevel; } @Override public int getMinClaimLevel() { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), this.getSubject(), Options.MIN_LEVEL); } @Override public double getEconomyClaimBlockCost() { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_COST); } @Override public double getEconomyClaimBlockReturn() { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.ECONOMY_BLOCK_SELL_RETURN); } @Override public double getTaxRate(ClaimType type) { return GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), this.getSubject(), Options.TAX_RATE, type); } @Override public UUID getUniqueId() { return this.getSubject().getUniqueId(); } public GDPermissionUser getSubject() { if (this.playerSubject == null || this.playerSubject.get() == null) { GDPermissionUser user = PermissionHolderCache.getInstance().getOrCreateUser(this.playerID); this.playerSubject = new WeakReference<>(user); } return this.playerSubject.get(); } public void sendTaxExpireMessage(Player player, GDClaim claim) { final double taxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), player, Options.TAX_RATE, claim); final double taxOwed = claim.getClaimBlocks() * taxRate; final double remainingDays = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.TAX_EXPIRATION_DAYS_KEEP, claim); final Component message = GriefDefenderPlugin.getInstance().messageData.getMessage(MessageStorage.TAX_EXPIRED, ImmutableMap.of( "days", remainingDays, "amount", taxOwed)); GriefDefenderPlugin.sendClaimDenyMessage(claim, player, message); } public double getTotalTax() { double totalTax = 0; final GDPermissionUser subject = this.getSubject(); for (Claim claim : this.getInternalClaims()) { double playerTaxRate = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Double.class), subject, Options.TAX_RATE, claim); totalTax += (claim.getClaimBlocks() / 256) * playerTaxRate; } return totalTax; } @Override public boolean canPvp(Claim claim) { if (!((GDClaim) claim).getWorld().getPVP()) { return false; } if (!claim.isPvpAllowed()) { return false; } if (GDOptions.PVP) { final Tristate result = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Tristate.class), this.getSubject(), Options.PVP, claim); if (result != Tristate.UNDEFINED) { return result.asBoolean(); } } return true; } @Override public boolean inPvpCombat() { final Player player = this.getSubject().getOnlinePlayer(); if (this.lastPvpTimestamp == null || player == null) { return false; } final Instant now = Instant.now(); final int combatTimeout = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.PVP_COMBAT_TIMEOUT); if (combatTimeout <= 0) { return false; } if (this.lastPvpTimestamp.plusSeconds(combatTimeout).isBefore(now)) { this.lastPvpTimestamp = null; return false; } return true; } @Override public int getRemainingPvpCombatTime(Claim claim) { return this.getPvpCombatTimeRemaining((GDClaim) claim); } public int getPvpCombatTimeRemaining(GDClaim claim) { final Player player = this.getSubject().getOnlinePlayer(); if (this.lastPvpTimestamp == null || player == null) { return 0; } final Instant now = Instant.now(); int combatTimeout = 0; if (GDOptions.PVP_COMBAT_TIMEOUT) { combatTimeout = GDPermissionManager.getInstance().getInternalOptionValue(TypeToken.of(Integer.class), player, Options.PVP_COMBAT_TIMEOUT, claim); } if (combatTimeout <= 0) { return 0; } if (this.lastPvpTimestamp.plusSeconds(combatTimeout).isBefore(now)) { this.lastPvpTimestamp = null; return 0; } final int duration = (int) Duration.between(this.lastPvpTimestamp, now).getSeconds(); return combatTimeout - duration; } public void updateRecordChat() { final Player player = this.getSubject().getOnlinePlayer(); if (this.recordChatTimestamp == null || player == null) { return; } final Instant now = Instant.now(); final int timeout = GriefDefenderPlugin.getGlobalConfig().getConfig().gui.chatCaptureIdleTimeout; if (timeout <= 0) { return; } if (this.recordChatTimestamp.plusSeconds(timeout).isBefore(now)) { this.recordChatTimestamp = null; } } public boolean isRecordingChat() { return this.recordChatTimestamp != null; } public void updateCommandInput() { final Player player = this.getSubject().getOnlinePlayer(); if (this.commandInputTimestamp == null || player == null) { return; } final Instant now = Instant.now(); final int timeout = GriefDefenderPlugin.getGlobalConfig().getConfig().gui.commandInputIdleTimeout; if (timeout <= 0) { return; } if (this.commandInputTimestamp.plusSeconds(timeout).isBefore(now)) { this.commandInputTimestamp = null; } } public boolean isWaitingForInput() { return this.commandInputTimestamp != null; } public void onClaimDelete() { this.lastShovelLocation = null; this.eventResultCache = null; this.claimResizing = null; this.claimSubdividing = null; } public void resetOptionCache() { this.optionNoGodMode = null; this.optionFlySpeed = null; this.optionWalkSpeed = null; this.optionGameModeType = null; this.optionWeatherType = null; } public void onDisconnect() { this.claimVisualRevertTasks.clear(); this.visualClaimBlocks.clear(); this.createBlockVisualTransactions.clear(); this.createBlockVisualRevertRunnables.clear(); this.queuedVisuals.clear(); this.claimMode = false; this.claimTool = true; this.debugClaimPermissions = false; this.ignoreClaims = false; this.lastShovelLocation = null; this.eventResultCache = null; this.claimResizing = null; this.claimSubdividing = null; this.commandInputTimestamp = null; this.recordChatTimestamp = null; this.tempVisualUniqueId = null; if (GriefDefenderPlugin.getInstance().getWorldEditProvider() != null) { GriefDefenderPlugin.getInstance().getWorldEditProvider().revertAllVisuals(this.playerID); } } }