001/* 002 * PlotSquared, a land and world management plugin for Minecraft. 003 * Copyright (C) IntellectualSites <https://intellectualsites.com> 004 * Copyright (C) IntellectualSites team and contributors 005 * 006 * This program is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program. If not, see <https://www.gnu.org/licenses/>. 018 */ 019package com.plotsquared.core.plot; 020 021import com.google.common.collect.ImmutableMap; 022import com.google.common.collect.ImmutableSet; 023import com.google.common.collect.Lists; 024import com.google.inject.Inject; 025import com.plotsquared.core.PlotSquared; 026import com.plotsquared.core.collection.QuadMap; 027import com.plotsquared.core.configuration.ConfigurationNode; 028import com.plotsquared.core.configuration.ConfigurationSection; 029import com.plotsquared.core.configuration.ConfigurationUtil; 030import com.plotsquared.core.configuration.Settings; 031import com.plotsquared.core.configuration.caption.TranslatableCaption; 032import com.plotsquared.core.configuration.file.YamlConfiguration; 033import com.plotsquared.core.events.PlayerPlotAddRemoveEvent; 034import com.plotsquared.core.events.Result; 035import com.plotsquared.core.generator.GridPlotWorld; 036import com.plotsquared.core.generator.IndependentPlotGenerator; 037import com.plotsquared.core.inject.annotations.WorldConfig; 038import com.plotsquared.core.location.BlockLoc; 039import com.plotsquared.core.location.Direction; 040import com.plotsquared.core.location.Location; 041import com.plotsquared.core.permissions.Permission; 042import com.plotsquared.core.player.ConsolePlayer; 043import com.plotsquared.core.player.MetaDataAccess; 044import com.plotsquared.core.player.PlayerMetaDataKeys; 045import com.plotsquared.core.player.PlotPlayer; 046import com.plotsquared.core.plot.flag.FlagContainer; 047import com.plotsquared.core.plot.flag.FlagParseException; 048import com.plotsquared.core.plot.flag.GlobalFlagContainer; 049import com.plotsquared.core.plot.flag.PlotFlag; 050import com.plotsquared.core.plot.flag.implementations.DoneFlag; 051import com.plotsquared.core.queue.GlobalBlockQueue; 052import com.plotsquared.core.queue.QueueCoordinator; 053import com.plotsquared.core.util.EventDispatcher; 054import com.plotsquared.core.util.MathMan; 055import com.plotsquared.core.util.MinecraftVersion; 056import com.plotsquared.core.util.PlotExpression; 057import com.plotsquared.core.util.RegionUtil; 058import com.plotsquared.core.util.StringMan; 059import com.plotsquared.core.util.task.TaskManager; 060import com.plotsquared.core.util.task.TaskTime; 061import com.sk89q.worldedit.math.BlockVector2; 062import com.sk89q.worldedit.math.BlockVector3; 063import com.sk89q.worldedit.regions.CuboidRegion; 064import com.sk89q.worldedit.world.biome.BiomeType; 065import com.sk89q.worldedit.world.biome.BiomeTypes; 066import com.sk89q.worldedit.world.gamemode.GameMode; 067import com.sk89q.worldedit.world.gamemode.GameModes; 068import net.kyori.adventure.text.Component; 069import net.kyori.adventure.text.ComponentLike; 070import net.kyori.adventure.text.minimessage.MiniMessage; 071import net.kyori.adventure.text.minimessage.tag.Tag; 072import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 073import org.apache.logging.log4j.LogManager; 074import org.apache.logging.log4j.Logger; 075import org.checkerframework.checker.nullness.qual.NonNull; 076import org.checkerframework.checker.nullness.qual.Nullable; 077import org.jetbrains.annotations.NotNull; 078 079import java.text.DecimalFormat; 080import java.util.ArrayList; 081import java.util.Collection; 082import java.util.Collections; 083import java.util.HashMap; 084import java.util.HashSet; 085import java.util.LinkedList; 086import java.util.List; 087import java.util.Map; 088import java.util.Map.Entry; 089import java.util.Set; 090import java.util.UUID; 091import java.util.concurrent.ConcurrentHashMap; 092import java.util.function.Consumer; 093 094/** 095 * @author Jesse Boyd, Alexander Söderberg 096 */ 097public abstract class PlotArea implements ComponentLike { 098 099 private static final Logger LOGGER = LogManager.getLogger("PlotSquared/" + PlotArea.class.getSimpleName()); 100 private static final MiniMessage MINI_MESSAGE = MiniMessage.builder().build(); 101 private static final DecimalFormat FLAG_DECIMAL_FORMAT = new DecimalFormat("0"); 102 103 static { 104 FLAG_DECIMAL_FORMAT.setMaximumFractionDigits(340); 105 } 106 107 protected final ConcurrentHashMap<PlotId, Plot> plots = new ConcurrentHashMap<>(); 108 @NonNull 109 private final String worldName; 110 private final String id; 111 @NonNull 112 private final PlotManager plotManager; 113 private final int worldHash; 114 private final PlotId min; 115 private final PlotId max; 116 @NonNull 117 private final IndependentPlotGenerator generator; 118 /** 119 * Area flag container 120 */ 121 private final FlagContainer flagContainer = 122 new FlagContainer(GlobalFlagContainer.getInstance()); 123 private final FlagContainer roadFlagContainer = 124 new FlagContainer(GlobalFlagContainer.getInstance()); 125 private final YamlConfiguration worldConfiguration; 126 private final GlobalBlockQueue globalBlockQueue; 127 private boolean roadFlags = false; 128 private boolean autoMerge = false; 129 private boolean allowSigns = true; 130 private boolean miscSpawnUnowned = false; 131 private boolean mobSpawning = false; 132 private boolean mobSpawnerSpawning = false; 133 private BiomeType plotBiome = BiomeTypes.FOREST; 134 private boolean plotChat = true; 135 private boolean forcingPlotChat = false; 136 private boolean schematicClaimSpecify = false; 137 private boolean schematicOnClaim = false; 138 private String schematicFile = "null"; 139 private boolean spawnEggs = false; 140 private boolean spawnCustom = true; 141 private boolean spawnBreeding = false; 142 private PlotAreaType type = PlotAreaType.NORMAL; 143 private PlotAreaTerrainType terrain = PlotAreaTerrainType.NONE; 144 private boolean homeAllowNonmember = false; 145 private BlockLoc nonmemberHome; 146 private BlockLoc defaultHome; 147 private int maxBuildHeight = PlotSquared.platform().versionMaxHeight() + 1; // Exclusive 148 private int minBuildHeight = PlotSquared.platform().versionMinHeight() + 1; // Inclusive 149 private int maxGenHeight = PlotSquared.platform().versionMaxHeight(); // Inclusive 150 private int minGenHeight = PlotSquared.platform().versionMinHeight(); // Inclusive 151 private GameMode gameMode = GameModes.CREATIVE; 152 private Map<String, PlotExpression> prices = new HashMap<>(); 153 private List<String> schematics = new ArrayList<>(); 154 private boolean worldBorder = false; 155 private int borderSize = 1; 156 private boolean useEconomy = false; 157 private int hash; 158 private CuboidRegion region; 159 private ConcurrentHashMap<String, Object> meta; 160 private QuadMap<PlotCluster> clusters; 161 private String signMaterial = "OAK_WALL_SIGN"; 162 private String legacySignMaterial = "WALL_SIGN"; 163 // These will be injected 164 @Inject 165 private EventDispatcher eventDispatcher; 166 167 public PlotArea( 168 final @NonNull String worldName, final @Nullable String id, 169 @NonNull IndependentPlotGenerator generator, final @Nullable PlotId min, 170 final @Nullable PlotId max, 171 @WorldConfig final @Nullable YamlConfiguration worldConfiguration, 172 final @NonNull GlobalBlockQueue blockQueue 173 ) { 174 this.worldName = worldName; 175 this.id = id; 176 this.plotManager = createManager(); 177 this.generator = generator; 178 this.globalBlockQueue = blockQueue; 179 if (min == null || max == null) { 180 if (min != max) { 181 throw new IllegalArgumentException( 182 "None of the ids can be null for this constructor"); 183 } 184 this.min = null; 185 this.max = null; 186 } else { 187 this.min = min; 188 this.max = max; 189 } 190 this.worldHash = worldName.hashCode(); 191 this.worldConfiguration = worldConfiguration; 192 } 193 194 private static void parseFlags(FlagContainer flagContainer, List<String> flagStrings) { 195 for (final String key : flagStrings) { 196 final String[] split; 197 if (key.contains(";")) { 198 split = key.split(";"); 199 } else { 200 split = key.split(":"); 201 } 202 final PlotFlag<?, ?> flagInstance = 203 GlobalFlagContainer.getInstance().getFlagFromString(split[0]); 204 if (flagInstance != null) { 205 try { 206 flagContainer.addFlag(flagInstance.parse(split[1])); 207 } catch (final FlagParseException e) { 208 LOGGER.warn( 209 "Failed to parse default flag with key '{}' and value '{}'. " 210 + "Reason: {}. This flag will not be added as a default flag.", 211 e.getFlag().getName(), 212 e.getValue(), 213 e.getErrorMessage() 214 ); 215 e.printStackTrace(); 216 } 217 } else { 218 flagContainer.addUnknownFlag(split[0], split[1]); 219 } 220 } 221 } 222 223 @NonNull 224 protected abstract PlotManager createManager(); 225 226 public QueueCoordinator getQueue() { 227 return this.globalBlockQueue.getNewQueue(PlotSquared.platform().worldUtil().getWeWorld(worldName)); 228 } 229 230 /** 231 * Returns the region for this PlotArea, or a CuboidRegion encompassing 232 * the whole world if none exists. 233 * 234 * @return CuboidRegion 235 */ 236 public CuboidRegion getRegion() { 237 this.region = getRegionAbs(); 238 if (this.region == null) { 239 return new CuboidRegion( 240 BlockVector3.at(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE), 241 BlockVector3.at(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE) 242 ); 243 } 244 return this.region; 245 } 246 247 /** 248 * Returns the region for this PlotArea. 249 * 250 * @return CuboidRegion or null if no applicable region 251 */ 252 private CuboidRegion getRegionAbs() { 253 if (this.region == null) { 254 if (this.min != null) { 255 Location bot = getPlotManager().getPlotBottomLocAbs(this.min); 256 Location top = getPlotManager().getPlotTopLocAbs(this.max); 257 BlockVector3 pos1 = bot.getBlockVector3().subtract(BlockVector3.ONE); 258 BlockVector3 pos2 = top.getBlockVector3().add(BlockVector3.ONE); 259 this.region = new CuboidRegion(pos1, pos2); 260 } 261 } 262 return this.region; 263 } 264 265 /** 266 * Returns the minimum value of a {@link PlotId}. 267 * 268 * @return the minimum value for a {@link PlotId} 269 */ 270 public @NonNull PlotId getMin() { 271 return this.min == null ? PlotId.of(Integer.MIN_VALUE, Integer.MIN_VALUE) : this.min; 272 } 273 274 /** 275 * Returns the max PlotId. 276 * 277 * @return the maximum value for a {@link PlotId} 278 */ 279 public @NonNull PlotId getMax() { 280 return this.max == null ? PlotId.of(Integer.MAX_VALUE, Integer.MAX_VALUE) : this.max; 281 } 282 283 @Override 284 public boolean equals(Object obj) { 285 if (this == obj) { 286 return true; 287 } 288 if (obj == null || getClass() != obj.getClass()) { 289 return false; 290 } 291 PlotArea plotarea = (PlotArea) obj; 292 return this.getWorldHash() == plotarea.getWorldHash() && this.getWorldName() 293 .equals(plotarea.getWorldName()) && StringMan.isEqual(this.getId(), plotarea.getId()); 294 } 295 296 public Set<PlotCluster> getClusters() { 297 return this.clusters == null ? new HashSet<>() : this.clusters.getAll(); 298 } 299 300 /** 301 * Check if a PlotArea is compatible (move/copy etc.). 302 * 303 * @param plotArea the {@link PlotArea} to compare 304 * @return {@code true} if both areas are compatible 305 */ 306 public boolean isCompatible(final @NonNull PlotArea plotArea) { 307 final ConfigurationSection section = this.worldConfiguration.getConfigurationSection("worlds"); 308 for (ConfigurationNode setting : plotArea.getSettingNodes()) { 309 Object constant = section.get(plotArea.worldName + '.' + setting.getConstant()); 310 if (constant == null || !constant 311 .equals(section.get(this.worldName + '.' + setting.getConstant()))) { 312 return false; 313 } 314 } 315 return true; 316 } 317 318 /** 319 * When a world is created, the following method will be called for each. 320 * 321 * @param config Configuration Section 322 */ 323 public void loadDefaultConfiguration(ConfigurationSection config) { 324 if ((this.min != null || this.max != null) && !(this instanceof GridPlotWorld)) { 325 throw new IllegalArgumentException("Must extend GridPlotWorld to provide"); 326 } 327 if (config.contains("generator.terrain")) { 328 this.terrain = ConfigurationUtil.getTerrain(config); 329 this.type = ConfigurationUtil.getType(config); 330 } 331 this.mobSpawning = config.getBoolean("natural_mob_spawning"); 332 this.miscSpawnUnowned = config.getBoolean("misc_spawn_unowned"); 333 this.mobSpawnerSpawning = config.getBoolean("mob_spawner_spawning"); 334 this.autoMerge = config.getBoolean("plot.auto_merge"); 335 this.allowSigns = config.getBoolean("plot.create_signs"); 336 if (MinecraftVersion.current().isOlderOrEqualThan(13)) { 337 this.legacySignMaterial = config.getString("plot.legacy_sign_material"); 338 } else { 339 this.signMaterial = config.getString("plot.sign_material"); 340 } 341 String biomeString = config.getString("plot.biome"); 342 if (!biomeString.startsWith("minecraft:")) { 343 biomeString = "minecraft:" + biomeString; 344 config.set("plot.biome", biomeString.toLowerCase()); 345 } 346 this.plotBiome = ConfigurationUtil.BIOME.parseString(biomeString.toLowerCase()); 347 this.schematicOnClaim = config.getBoolean("schematic.on_claim"); 348 this.schematicFile = config.getString("schematic.file"); 349 this.schematicClaimSpecify = config.getBoolean("schematic.specify_on_claim"); 350 this.schematics = new ArrayList<>(config.getStringList("schematic.schematics")); 351 this.schematics.replaceAll(String::toLowerCase); 352 this.useEconomy = config.getBoolean("economy.use"); 353 ConfigurationSection priceSection = config.getConfigurationSection("economy.prices"); 354 if (this.useEconomy) { 355 this.prices = new HashMap<>(); 356 for (String key : priceSection.getKeys(false)) { 357 String raw = priceSection.getString(key); 358 if (raw.contains("{arg}")) { 359 raw = raw.replace("{arg}", "plots"); 360 priceSection.set(key, raw); // update if replaced 361 } 362 this.prices.put(key, PlotExpression.compile(raw, "plots")); 363 } 364 } 365 this.plotChat = config.getBoolean("chat.enabled"); 366 this.forcingPlotChat = config.getBoolean("chat.forced"); 367 this.worldBorder = config.getBoolean("world.border"); 368 this.borderSize = config.getInt("world.border_size"); 369 this.maxBuildHeight = config.getInt("world.max_height"); 370 this.minBuildHeight = config.getInt("world.min_height"); 371 this.minGenHeight = config.getInt("world.min_gen_height"); 372 this.maxGenHeight = config.getInt("world.max_gen_height"); 373 374 switch (config.getString("world.gamemode").toLowerCase()) { 375 case "creative", "c", "1" -> this.gameMode = GameModes.CREATIVE; 376 case "adventure", "a", "2" -> this.gameMode = GameModes.ADVENTURE; 377 case "spectator", "3" -> this.gameMode = GameModes.SPECTATOR; 378 default -> this.gameMode = GameModes.SURVIVAL; 379 } 380 381 String homeNonMembers = config.getString("home.nonmembers"); 382 String homeDefault = config.getString("home.default"); 383 this.defaultHome = BlockLoc.fromString(homeDefault); 384 this.homeAllowNonmember = homeNonMembers.equalsIgnoreCase(homeDefault); 385 if (this.homeAllowNonmember) { 386 this.nonmemberHome = defaultHome; 387 } else { 388 this.nonmemberHome = BlockLoc.fromString(homeNonMembers); 389 } 390 391 if ("side".equalsIgnoreCase(homeDefault)) { 392 this.defaultHome = null; 393 } else if (StringMan.isEqualIgnoreCaseToAny(homeDefault, "center", "middle", "centre")) { 394 this.defaultHome = new BlockLoc(Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE); 395 } else { 396 try { 397 /*String[] split = homeDefault.split(","); 398 this.DEFAULT_HOME = 399 new PlotLoc(Integer.parseInt(split[0]), Integer.parseInt(split[1]));*/ 400 this.defaultHome = BlockLoc.fromString(homeDefault); 401 } catch (NumberFormatException ignored) { 402 this.defaultHome = null; 403 } 404 } 405 406 this.spawnEggs = config.getBoolean("event.spawn.egg"); 407 this.spawnCustom = config.getBoolean("event.spawn.custom"); 408 this.spawnBreeding = config.getBoolean("event.spawn.breeding"); 409 410 if (PlotSquared.get().isWeInitialised()) { 411 loadFlags(config); 412 } else { 413 ConsolePlayer.getConsole().sendMessage( 414 TranslatableCaption.of("flags.delaying_loading_area_flags"), 415 TagResolver.resolver("area", Tag.inserting(Component.text(this.id == null ? this.worldName : this.id))) 416 ); 417 TaskManager.runTaskLater(() -> loadFlags(config), TaskTime.ticks(1)); 418 } 419 420 loadConfiguration(config); 421 } 422 423 private void loadFlags(ConfigurationSection config) { 424 ConsolePlayer.getConsole().sendMessage( 425 TranslatableCaption.of("flags.loading_area_flags"), 426 TagResolver.resolver("area", Tag.inserting(Component.text(this.id == null ? this.worldName : this.id))) 427 ); 428 List<String> flags = config.getStringList("flags.default"); 429 if (flags.isEmpty()) { 430 flags = config.getStringList("flags"); 431 if (flags.isEmpty()) { 432 flags = new ArrayList<>(); 433 ConfigurationSection section = config.getConfigurationSection("flags"); 434 Set<String> keys = section.getKeys(false); 435 for (String key : keys) { 436 if (!"default".equals(key)) { 437 flags.add(key + ';' + section.get(key)); 438 } 439 } 440 } 441 } 442 parseFlags(this.getFlagContainer(), flags); 443 ConsolePlayer.getConsole().sendMessage( 444 TranslatableCaption.of("flags.area_flags"), 445 TagResolver.resolver("flags", Tag.inserting(Component.text(flags.toString()))) 446 ); 447 448 List<String> roadflags = config.getStringList("road.flags"); 449 if (roadflags.isEmpty()) { 450 roadflags = new ArrayList<>(); 451 ConfigurationSection section = config.getConfigurationSection("road.flags"); 452 Set<String> keys = section.getKeys(false); 453 for (String key : keys) { 454 if (!"default".equals(key)) { 455 roadflags.add(key + ';' + section.get(key)); 456 } 457 } 458 } 459 this.roadFlags = !roadflags.isEmpty(); 460 parseFlags(this.getRoadFlagContainer(), roadflags); 461 ConsolePlayer.getConsole().sendMessage( 462 TranslatableCaption.of("flags.road_flags"), 463 TagResolver.resolver("flags", Tag.inserting(Component.text(roadflags.toString()))) 464 ); 465 } 466 467 public abstract void loadConfiguration(ConfigurationSection config); 468 469 /** 470 * Saving core PlotArea settings. 471 * 472 * @param config Configuration Section 473 */ 474 public void saveConfiguration(ConfigurationSection config) { 475 HashMap<String, Object> options = new HashMap<>(); 476 options.put("natural_mob_spawning", this.isMobSpawning()); 477 options.put("misc_spawn_unowned", this.isMiscSpawnUnowned()); 478 options.put("mob_spawner_spawning", this.isMobSpawnerSpawning()); 479 options.put("plot.auto_merge", this.isAutoMerge()); 480 options.put("plot.create_signs", this.allowSigns()); 481 if (MinecraftVersion.current().isOlderOrEqualThan(13)) { 482 options.put("plot.legacy_sign_material", this.legacySignMaterial); 483 } else { 484 options.put("plot.sign_material", this.signMaterial()); 485 } 486 options.put("plot.biome", "minecraft:forest"); 487 options.put("schematic.on_claim", this.isSchematicOnClaim()); 488 options.put("schematic.file", this.getSchematicFile()); 489 options.put("schematic.specify_on_claim", this.isSchematicClaimSpecify()); 490 options.put("schematic.schematics", this.getSchematics()); 491 options.put("economy.use", this.useEconomy()); 492 options.put("economy.prices.claim", 100); 493 options.put("economy.prices.merge", 100); 494 options.put("economy.prices.sell", 100); 495 options.put("chat.enabled", this.isPlotChat()); 496 options.put("chat.forced", this.isForcingPlotChat()); 497 options.put("flags.default", null); 498 options.put("event.spawn.egg", this.isSpawnEggs()); 499 options.put("event.spawn.custom", this.isSpawnCustom()); 500 options.put("event.spawn.breeding", this.isSpawnBreeding()); 501 options.put("world.border", this.hasWorldBorder()); 502 options.put("world.border_size", this.getBorderSize()); 503 options.put("home.default", "side"); 504 String position = config.getString( 505 "home.nonmembers", 506 config.getBoolean("home.allow-nonmembers", false) ? 507 config.getString("home.default", "side") : 508 "side" 509 ); 510 options.put("home.nonmembers", position); 511 options.put("world.max_height", this.getMaxBuildHeight()); 512 options.put("world.min_height", this.getMinBuildHeight()); 513 options.put("world.min_gen_height", this.getMinGenHeight()); 514 options.put("world.max_gen_height", this.getMaxGenHeight()); 515 options.put("world.gamemode", this.getGameMode().getName().toLowerCase()); 516 options.put("road.flags.default", null); 517 518 if (this.getType() != PlotAreaType.NORMAL) { 519 options.put("generator.terrain", this.getTerrain()); 520 options.put("generator.type", this.getType().toString()); 521 } 522 ConfigurationNode[] settings = getSettingNodes(); 523 /* 524 * Saving generator specific settings 525 */ 526 for (ConfigurationNode setting : settings) { 527 options.put(setting.getConstant(), setting.getValue()); 528 } 529 for (Entry<String, Object> stringObjectEntry : options.entrySet()) { 530 if (!config.contains(stringObjectEntry.getKey())) { 531 config.set(stringObjectEntry.getKey(), stringObjectEntry.getValue()); 532 } 533 } 534 if (!config.contains("flags")) { 535 config.set( 536 "flags.use", 537 "63,64,68,69,71,77,96,143,167,193,194,195,196,197,77,143,69,70,72,147,148,107,183,184,185,186,187,132" 538 ); 539 } 540 if (!config.contains("road.flags")) { 541 config.set("road.flags.liquid-flow", false); 542 } 543 } 544 545 @NonNull 546 @Override 547 public String toString() { 548 if (this.getId() == null) { 549 return this.getWorldName(); 550 } else { 551 return this.getWorldName() + ";" + this.getId(); 552 } 553 } 554 555 @Override 556 public @NotNull Component asComponent() { 557 return Component.text(toString()); 558 } 559 560 @Override 561 public int hashCode() { 562 if (this.hash != 0) { 563 return this.hash; 564 } 565 return this.hash = toString().hashCode(); 566 } 567 568 /** 569 * Used for the <b>/plot setup</b> command Return null if you do not want to support this feature 570 * 571 * @return ConfigurationNode[] 572 */ 573 public abstract ConfigurationNode[] getSettingNodes(); 574 575 /** 576 * Gets the {@link Plot} at a location. 577 * 578 * @param location the location 579 * @return the {@link Plot} or null if none exists 580 */ 581 public @Nullable Plot getPlotAbs(final @NonNull Location location) { 582 final PlotId pid = 583 this.getPlotManager().getPlotId(location.getX(), location.getY(), location.getZ()); 584 if (pid == null) { 585 return null; 586 } 587 return getPlotAbs(pid); 588 } 589 590 /** 591 * Gets the base plot at a location. 592 * 593 * @param location the location 594 * @return base Plot 595 */ 596 public @Nullable Plot getPlot(final @NonNull Location location) { 597 final PlotId pid = 598 this.getPlotManager().getPlotId(location.getX(), location.getY(), location.getZ()); 599 if (pid == null) { 600 return null; 601 } 602 return getPlot(pid); 603 } 604 605 /** 606 * Get the owned base plot at a location. 607 * 608 * @param location the location 609 * @return the base plot or null 610 */ 611 public @Nullable Plot getOwnedPlot(final @NonNull Location location) { 612 final PlotId pid = 613 this.getPlotManager().getPlotId(location.getX(), location.getY(), location.getZ()); 614 if (pid == null) { 615 return null; 616 } 617 Plot plot = this.plots.get(pid); 618 return plot == null ? null : plot.getBasePlot(false); 619 } 620 621 /** 622 * Get the owned plot at a location. 623 * 624 * @param location the location 625 * @return Plot or null 626 */ 627 public @Nullable Plot getOwnedPlotAbs(final @NonNull Location location) { 628 final PlotId pid = 629 this.getPlotManager().getPlotId(location.getX(), location.getY(), location.getZ()); 630 if (pid == null) { 631 return null; 632 } 633 return this.plots.get(pid); 634 } 635 636 /** 637 * Get the owned Plot at a PlotId. 638 * 639 * @param id the {@link PlotId} 640 * @return the plot or null 641 */ 642 public @Nullable Plot getOwnedPlotAbs(final @NonNull PlotId id) { 643 return this.plots.get(id); 644 } 645 646 public @Nullable Plot getOwnedPlot(final @NonNull PlotId id) { 647 Plot plot = this.plots.get(id); 648 return plot == null ? null : plot.getBasePlot(false); 649 } 650 651 public boolean contains(final int x, final int z) { 652 return this.getType() != PlotAreaType.PARTIAL || RegionUtil.contains(getRegionAbs(), x, z); 653 } 654 655 public boolean contains(final @NonNull PlotId id) { 656 return this.min == null || (id.getX() >= this.min.getX() && id.getX() <= this.max.getX() && 657 id.getY() >= this.min.getY() && id.getY() <= this.max.getY()); 658 } 659 660 public boolean contains(final @NonNull Location location) { 661 return StringMan.isEqual(location.getWorldName(), this.getWorldName()) && ( 662 getRegionAbs() == null || this.region.contains(location.getBlockVector3())); 663 } 664 665 /** 666 * Get if the {@code PlotArea}'s build range (min build height -> max build height) contains the given y value 667 * 668 * @param y y height 669 * @return if build height contains y 670 */ 671 public boolean buildRangeContainsY(int y) { 672 return y >= minBuildHeight && y < maxBuildHeight; 673 } 674 675 /** 676 * Utility method to check if the player is attempting to place blocks outside the build area, and notify of this if the 677 * player does not have permissions. 678 * 679 * @param player Player to check 680 * @param y y height to check 681 * @return true if outside build area with no permissions 682 * @since 6.9.1 683 */ 684 public boolean notifyIfOutsideBuildArea(PlotPlayer<?> player, int y) { 685 if (!buildRangeContainsY(y) && !player.hasPermission(Permission.PERMISSION_ADMIN_BUILD_HEIGHT_LIMIT)) { 686 player.sendMessage( 687 TranslatableCaption.of("height.height_limit"), 688 TagResolver.builder() 689 .tag("minheight", Tag.inserting(Component.text(minBuildHeight))) 690 .tag("maxheight", Tag.inserting(Component.text(maxBuildHeight))) 691 .build() 692 ); 693 // Return true if "failed" as the method will always be inverted otherwise 694 return true; 695 } 696 return false; 697 } 698 699 public @NonNull Set<Plot> getPlotsAbs(final UUID uuid) { 700 if (uuid == null) { 701 return Collections.emptySet(); 702 } 703 final HashSet<Plot> myPlots = new HashSet<>(); 704 forEachPlotAbs(value -> { 705 if (uuid.equals(value.getOwnerAbs())) { 706 myPlots.add(value); 707 } 708 }); 709 return myPlots; 710 } 711 712 public @NonNull Set<Plot> getPlots(final @NonNull UUID uuid) { 713 return getPlots().stream().filter(plot -> plot.isBasePlot() && plot.isOwner(uuid)) 714 .collect(ImmutableSet.toImmutableSet()); 715 } 716 717 /** 718 * A collection of the claimed plots in this {@link PlotArea}. 719 * 720 * @return a collection of claimed plots 721 */ 722 public Collection<Plot> getPlots() { 723 return this.plots.values(); 724 } 725 726 public int getPlotCount(final @NonNull UUID uuid) { 727 if (!Settings.Done.COUNTS_TOWARDS_LIMIT) { 728 return (int) getPlotsAbs(uuid).stream().filter(plot -> !DoneFlag.isDone(plot)).count(); 729 } 730 return getPlotsAbs(uuid).size(); 731 } 732 733 /** 734 * Retrieves the plots for the player in this PlotArea. 735 * 736 * @param player player to get plots of 737 * @return set of player's plots 738 * @deprecated Use {@link #getPlots(UUID)} 739 */ 740 @Deprecated 741 public Set<Plot> getPlots(final @NonNull PlotPlayer<?> player) { 742 return getPlots(player.getUUID()); 743 } 744 745 //todo check if this method is needed in this class 746 747 public boolean hasPlot(final @NonNull UUID uuid) { 748 return this.plots.entrySet().stream().anyMatch(entry -> entry.getValue().isOwner(uuid)); 749 } 750 751 public int getPlotCount(final @Nullable PlotPlayer<?> player) { 752 return player != null ? getPlotCount(player.getUUID()) : 0; 753 } 754 755 public @Nullable Plot getPlotAbs(final @NonNull PlotId id) { 756 Plot plot = getOwnedPlotAbs(id); 757 if (plot == null) { 758 if (this.min != null && (id.getX() < this.min.getX() || id.getX() > this.max.getX() || id.getY() < this.min.getY() 759 || id.getY() > this.max.getY())) { 760 return null; 761 } 762 return new Plot(this, id); 763 } 764 return plot; 765 } 766 767 public @Nullable Plot getPlot(final @NonNull PlotId id) { 768 final Plot plot = getOwnedPlotAbs(id); 769 if (plot == null) { 770 if (this.min != null && (id.getX() < this.min.getX() || id.getX() > this.max.getX() || id.getY() < this.min.getY() 771 || id.getY() > this.max.getY())) { 772 return null; 773 } 774 return new Plot(this, id); 775 } 776 return plot.getBasePlot(false); 777 } 778 779 /** 780 * Retrieves the number of claimed plot in the {@link PlotArea}. 781 * 782 * @return the number of claimed plots 783 */ 784 public int getPlotCount() { 785 return this.plots.size(); 786 } 787 788 public @Nullable PlotCluster getCluster(final @NonNull Location location) { 789 final Plot plot = getPlot(location); 790 if (plot == null) { 791 return null; 792 } 793 return this.clusters != null ? this.clusters.get(plot.getId().getX(), plot.getId().getY()) : null; 794 } 795 796 public @Nullable PlotCluster getFirstIntersectingCluster( 797 final @NonNull PlotId pos1, 798 final @NonNull PlotId pos2 799 ) { 800 if (this.clusters == null) { 801 return null; 802 } 803 for (PlotCluster cluster : this.clusters.getAll()) { 804 if (cluster.intersects(pos1, pos2)) { 805 return cluster; 806 } 807 } 808 return null; 809 } 810 811 @Nullable PlotCluster getCluster(final @NonNull PlotId id) { 812 return this.clusters != null ? this.clusters.get(id.getX(), id.getY()) : null; 813 } 814 815 /** 816 * Session only plot metadata (session is until the server stops). 817 * <br> 818 * For persistent metadata use the flag system 819 * 820 * @param key metadata key 821 * @param value metadata value 822 */ 823 public void setMeta(final @NonNull String key, final @Nullable Object value) { 824 if (this.meta == null) { 825 this.meta = new ConcurrentHashMap<>(); 826 } 827 this.meta.put(key, value); 828 } 829 830 public @NonNull <T> T getMeta(final @NonNull String key, final @NonNull T def) { 831 final Object v = getMeta(key); 832 return v == null ? def : (T) v; 833 } 834 835 /** 836 * Get the metadata for a key<br> 837 * <br> 838 * For persistent metadata use the flag system 839 * 840 * @param key metadata key to get value for 841 * @return metadata value 842 */ 843 public @Nullable Object getMeta(final @NonNull String key) { 844 if (this.meta != null) { 845 return this.meta.get(key); 846 } 847 return null; 848 } 849 850 @SuppressWarnings("unused") 851 public @NonNull Set<Plot> getBasePlots() { 852 final HashSet<Plot> myPlots = new HashSet<>(getPlots()); 853 myPlots.removeIf(plot -> !plot.isBasePlot()); 854 return myPlots; 855 } 856 857 private void forEachPlotAbs(Consumer<Plot> run) { 858 for (final Entry<PlotId, Plot> entry : this.plots.entrySet()) { 859 run.accept(entry.getValue()); 860 } 861 } 862 863 public void forEachBasePlot(Consumer<Plot> run) { 864 for (final Plot plot : getPlots()) { 865 if (plot.isBasePlot()) { 866 run.accept(plot); 867 } 868 } 869 } 870 871 /** 872 * Returns an ImmutableMap of PlotId's and Plots in this PlotArea. 873 * 874 * @return map of PlotId against Plot for all plots in this area 875 * @deprecated Poorly implemented. May be removed in future. 876 */ 877 //todo eventually remove 878 @Deprecated 879 public @NonNull Map<PlotId, Plot> getPlotsRaw() { 880 return ImmutableMap.copyOf(plots); 881 } 882 883 public @NonNull Set<Entry<PlotId, Plot>> getPlotEntries() { 884 return this.plots.entrySet(); 885 } 886 887 public boolean addPlot(final @NonNull Plot plot) { 888 for (final PlotPlayer<?> pp : plot.getPlayersInPlot()) { 889 try (final MetaDataAccess<Plot> metaDataAccess = pp.accessTemporaryMetaData( 890 PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { 891 metaDataAccess.set(plot); 892 } 893 } 894 return this.plots.put(plot.getId(), plot) == null; 895 } 896 897 public Plot getNextFreePlot(final PlotPlayer<?> player, @Nullable PlotId start) { 898 int plots; 899 PlotId center; 900 PlotId min = getMin(); 901 PlotId max = getMax(); 902 if (getType() == PlotAreaType.PARTIAL) { 903 center = PlotId.of(MathMan.average(min.getX(), max.getX()), MathMan.average(min.getY(), max.getY())); 904 plots = Math.max(max.getX() - min.getX() + 1, max.getY() - min.getY() + 1) + 1; 905 if (start != null) { 906 start = PlotId.of(start.getX() - center.getX(), start.getY() - center.getY()); 907 } 908 } else { 909 center = PlotId.of(0, 0); 910 plots = Integer.MAX_VALUE; 911 } 912 for (int i = 0; i < plots; i++) { 913 if (start == null) { 914 start = getMeta("lastPlot", PlotId.of(0, 0)); 915 } else { 916 start = start.getNextId(); 917 } 918 PlotId currentId = PlotId.of(center.getX() + start.getX(), center.getY() + start.getY()); 919 Plot plot = getPlotAbs(currentId); 920 if (plot != null && plot.canClaim(player)) { 921 setMeta("lastPlot", start); 922 return plot; 923 } 924 } 925 return null; 926 } 927 928 public boolean addPlotIfAbsent(final @NonNull Plot plot) { 929 if (this.plots.putIfAbsent(plot.getId(), plot) == null) { 930 for (PlotPlayer<?> pp : plot.getPlayersInPlot()) { 931 try (final MetaDataAccess<Plot> metaDataAccess = pp.accessTemporaryMetaData( 932 PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { 933 metaDataAccess.set(plot); 934 } 935 } 936 return true; 937 } 938 return false; 939 } 940 941 public boolean addPlotAbs(final @NonNull Plot plot) { 942 return this.plots.put(plot.getId(), plot) == null; 943 } 944 945 /** 946 * Get the plot border distance for a world<br> 947 * 948 * @return The border distance or Integer.MAX_VALUE if no border is set 949 * @deprecated Use {@link PlotArea#getBorder(boolean)} 950 */ 951 @Deprecated(forRemoval = true, since = "7.2.0") 952 public int getBorder() { 953 final Integer meta = (Integer) getMeta("worldBorder"); 954 if (meta != null) { 955 int border = meta + 1; 956 if (border == 0) { 957 return Integer.MAX_VALUE; 958 } else { 959 return border; 960 } 961 } 962 return Integer.MAX_VALUE; 963 } 964 965 /** 966 * Get the plot border distance for a world, specifying whether the returned value should include the world.border-size 967 * value. This is a player-traversable area, where plots cannot be claimed 968 * 969 * @param getExtended If the extra border given by world.border-size should be included 970 * @return Border distance of Integer.MAX_VALUE if no border is set 971 * @since 7.2.0 972 */ 973 public int getBorder(boolean getExtended) { 974 final Integer meta = (Integer) getMeta("worldBorder"); 975 if (meta != null) { 976 int border = meta + 1; 977 if (border == 0) { 978 return Integer.MAX_VALUE; 979 } else { 980 return getExtended ? border + borderSize : border; 981 } 982 } 983 return Integer.MAX_VALUE; 984 } 985 986 /** 987 * Setup the plot border for a world (usually done when the world is created). 988 */ 989 public void setupBorder() { 990 if (!this.hasWorldBorder()) { 991 return; 992 } 993 final Integer meta = (Integer) getMeta("worldBorder"); 994 if (meta == null) { 995 setMeta("worldBorder", 1); 996 } 997 for (final Plot plot : getPlots()) { 998 plot.updateWorldBorder(); 999 } 1000 } 1001 1002 /** 1003 * Delete the metadata for a key. 1004 * - metadata is session only 1005 * - deleting other plugin's metadata may cause issues 1006 * 1007 * @param key Meta data key 1008 */ 1009 public void deleteMeta(final @NonNull String key) { 1010 if (this.meta != null) { 1011 this.meta.remove(key); 1012 } 1013 } 1014 1015 public @Nullable List<Plot> canClaim( 1016 final @Nullable PlotPlayer<?> player, final @NonNull PlotId pos1, 1017 final @NonNull PlotId pos2 1018 ) { 1019 if (pos1.getX() == pos2.getX() && pos1.getY() == pos2.getY()) { 1020 if (getOwnedPlot(pos1) != null) { 1021 return null; 1022 } 1023 final Plot plot = getPlotAbs(pos1); 1024 if (plot == null) { 1025 return null; 1026 } 1027 if (plot.canClaim(player)) { 1028 return Collections.singletonList(plot); 1029 } else { 1030 return null; 1031 } 1032 } 1033 final List<Plot> plots = new LinkedList<>(); 1034 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 1035 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 1036 final PlotId id = PlotId.of(x, y); 1037 final Plot plot = getPlotAbs(id); 1038 if (plot == null) { 1039 return null; 1040 } 1041 if (!plot.canClaim(player)) { 1042 return null; 1043 } else { 1044 plots.add(plot); 1045 } 1046 } 1047 } 1048 return plots; 1049 } 1050 1051 public boolean removePlot(final @NonNull PlotId id) { 1052 return this.plots.remove(id) != null; 1053 } 1054 1055 /** 1056 * Merge a list of plots together. This is non-blocking for the world-changes that will be made. To run a task when the 1057 * world changes are complete, use {@link PlotArea#mergePlots(List, boolean, Runnable)}; 1058 * 1059 * @param plotIds List of plot IDs to merge 1060 * @param removeRoads If the roads between plots should be removed 1061 * @return if merges were completed successfully. 1062 */ 1063 public boolean mergePlots(final @NonNull List<PlotId> plotIds, final boolean removeRoads) { 1064 return mergePlots(plotIds, removeRoads, null); 1065 } 1066 1067 /** 1068 * Merge a list of plots together. This is non-blocking for the world-changes that will be made. 1069 * 1070 * @param plotIds List of plot IDs to merge 1071 * @param removeRoads If the roads between plots should be removed 1072 * @param whenDone Task to run when any merge world changes are complete. Also runs if no changes were made. Does not 1073 * run if there was an error or if too few plots IDs were supplied. 1074 * @return if merges were completed successfully. 1075 * @since 6.9.0 1076 */ 1077 public boolean mergePlots( 1078 final @NonNull List<PlotId> plotIds, final boolean removeRoads, final @Nullable Runnable whenDone 1079 ) { 1080 if (plotIds.size() < 2) { 1081 return false; 1082 } 1083 1084 final PlotId pos1 = plotIds.get(0); 1085 final PlotId pos2 = plotIds.get(plotIds.size() - 1); 1086 final PlotManager manager = getPlotManager(); 1087 1088 QueueCoordinator queue = getQueue(); 1089 manager.startPlotMerge(plotIds, queue); 1090 final Set<UUID> trusted = new HashSet<>(); 1091 final Set<UUID> members = new HashSet<>(); 1092 final Set<UUID> denied = new HashSet<>(); 1093 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 1094 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 1095 PlotId id = PlotId.of(x, y); 1096 Plot plot = getPlotAbs(id); 1097 trusted.addAll(plot.getTrusted()); 1098 members.addAll(plot.getMembers()); 1099 denied.addAll(plot.getDenied()); 1100 if (removeRoads) { 1101 plot.getPlotModificationManager().removeSign(); 1102 } 1103 } 1104 } 1105 members.removeAll(trusted); 1106 denied.removeAll(trusted); 1107 denied.removeAll(members); 1108 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 1109 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 1110 final boolean lx = x < pos2.getX(); 1111 final boolean ly = y < pos2.getY(); 1112 final PlotId id = PlotId.of(x, y); 1113 final Plot plot = getPlotAbs(id); 1114 1115 Set<UUID> currentlyTrusted = plot.getTrusted(); 1116 trusted.forEach(uuid -> { 1117 if (!currentlyTrusted.contains(uuid) && eventDispatcher.callPlayerTrust( 1118 null, 1119 plot, 1120 uuid, 1121 PlayerPlotAddRemoveEvent.Reason.MERGE 1122 ).getEventResult() != Result.DENY) { 1123 plot.addTrusted(uuid); 1124 eventDispatcher.callPostTrusted(null, plot, uuid, true, PlayerPlotAddRemoveEvent.Reason.MERGE); 1125 } 1126 }); 1127 1128 Set<UUID> currentlyAdded = plot.getMembers(); 1129 members.forEach(uuid -> { 1130 if (!currentlyAdded.contains(uuid) && eventDispatcher.callPlayerAdd( 1131 null, 1132 plot, 1133 uuid, 1134 PlayerPlotAddRemoveEvent.Reason.MERGE 1135 ).getEventResult() != Result.DENY) { 1136 plot.addMember(uuid); 1137 eventDispatcher.callPostAdded(null, plot, uuid, true, PlayerPlotAddRemoveEvent.Reason.MERGE); 1138 } 1139 }); 1140 1141 Set<UUID> currentlyDenied = plot.getDenied(); 1142 denied.forEach(uuid -> { 1143 if (!currentlyDenied.contains(uuid) && eventDispatcher.callPlayerDeny( 1144 null, 1145 plot, 1146 uuid, 1147 PlayerPlotAddRemoveEvent.Reason.MERGE 1148 ).getEventResult() != Result.DENY) { 1149 plot.addDenied(uuid); 1150 eventDispatcher.callPostDenied(null, plot, uuid, true, PlayerPlotAddRemoveEvent.Reason.MERGE); 1151 } 1152 }); 1153 1154 Plot plot2; 1155 if (lx) { 1156 if (ly) { 1157 if (!plot.isMerged(Direction.EAST) || !plot.isMerged(Direction.SOUTH)) { 1158 if (removeRoads) { 1159 plot.getPlotModificationManager().removeRoadSouthEast(queue); 1160 } 1161 } 1162 } 1163 if (!plot.isMerged(Direction.EAST)) { 1164 plot2 = plot.getRelative(1, 0); 1165 plot.mergePlot(plot2, removeRoads, queue); 1166 } 1167 } 1168 if (ly) { 1169 if (!plot.isMerged(Direction.SOUTH)) { 1170 plot2 = plot.getRelative(0, 1); 1171 plot.mergePlot(plot2, removeRoads, queue); 1172 } 1173 } 1174 } 1175 } 1176 manager.finishPlotMerge(plotIds, queue); 1177 if (whenDone != null) { 1178 queue.setCompleteTask(whenDone); 1179 } 1180 queue.enqueue(); 1181 return true; 1182 } 1183 1184 /** 1185 * Get a set of owned plots within a selection (chooses the best algorithm based on selection size. 1186 * i.e. A selection of billions of plots will work fine 1187 * 1188 * @param pos1 first corner of selection 1189 * @param pos2 second corner of selection 1190 * @return the plots in the selection which are owned 1191 */ 1192 public Set<Plot> getPlotSelectionOwned(final @NonNull PlotId pos1, final @NonNull PlotId pos2) { 1193 final int size = (1 + pos2.getX() - pos1.getX()) * (1 + pos2.getY() - pos1.getY()); 1194 final Set<Plot> result = new HashSet<>(); 1195 if (size < 16 || size < getPlotCount()) { 1196 for (final PlotId pid : Lists.newArrayList((Iterable<? extends PlotId>) 1197 PlotId.PlotRangeIterator.range(pos1, pos2))) { 1198 final Plot plot = getPlotAbs(pid); 1199 if (plot.hasOwner()) { 1200 if (plot.getId().getX() > pos1.getX() || plot.getId().getY() > pos1.getY() 1201 || plot.getId().getX() < pos2.getX() || plot.getId().getY() < pos2.getY()) { 1202 result.add(plot); 1203 } 1204 } 1205 } 1206 } else { 1207 for (final Plot plot : getPlots()) { 1208 if (plot.getId().getX() > pos1.getX() || plot.getId().getY() > pos1.getY() || plot.getId().getX() < pos2.getX() 1209 || plot.getId().getY() < pos2.getY()) { 1210 result.add(plot); 1211 } 1212 } 1213 } 1214 return result; 1215 } 1216 1217 @SuppressWarnings("WeakerAccess") 1218 public void removeCluster(final @Nullable PlotCluster plotCluster) { 1219 if (this.clusters == null) { 1220 throw new IllegalAccessError("Clusters not enabled!"); 1221 } 1222 this.clusters.remove(plotCluster); 1223 } 1224 1225 public void addCluster(final @Nullable PlotCluster plotCluster) { 1226 if (this.clusters == null) { 1227 this.clusters = new QuadMap<>(Integer.MAX_VALUE, 0, 0, 62) { 1228 @Override 1229 public CuboidRegion getRegion(PlotCluster value) { 1230 BlockVector2 pos1 = BlockVector2.at(value.getP1().getX(), value.getP1().getY()); 1231 BlockVector2 pos2 = BlockVector2.at(value.getP2().getX(), value.getP2().getY()); 1232 return new CuboidRegion( 1233 pos1.toBlockVector3(getMinGenHeight()), 1234 pos2.toBlockVector3(getMaxGenHeight()) 1235 ); 1236 } 1237 }; 1238 } 1239 this.clusters.add(plotCluster); 1240 } 1241 1242 public @Nullable PlotCluster getCluster(final String string) { 1243 for (PlotCluster cluster : getClusters()) { 1244 if (cluster.getName().equalsIgnoreCase(string)) { 1245 return cluster; 1246 } 1247 } 1248 return null; 1249 } 1250 1251 /** 1252 * Get whether a schematic with that name is available or not. 1253 * If a schematic is available, it can be used for plot claiming. 1254 * 1255 * @param schematic the schematic to look for. 1256 * @return {@code true} if the schematic exists, {@code false} otherwise. 1257 */ 1258 public boolean hasSchematic(@NonNull String schematic) { 1259 return getSchematics().contains(schematic.toLowerCase()); 1260 } 1261 1262 /** 1263 * Get whether economy is enabled and used on this plot area or not. 1264 * 1265 * @return {@code true} if this plot area uses economy, {@code false} otherwise. 1266 */ 1267 public boolean useEconomy() { 1268 return useEconomy; 1269 } 1270 1271 /** 1272 * Get whether the plot area is limited by a world border or not. 1273 * 1274 * @return {@code true} if the plot area has a world border, {@code false} otherwise. 1275 */ 1276 public boolean hasWorldBorder() { 1277 return worldBorder; 1278 } 1279 1280 /** 1281 * Get the "extra border" size of the plot area. 1282 * 1283 * @return Plot area extra border size 1284 * @since 7.2.0 1285 */ 1286 public int getBorderSize() { 1287 return borderSize; 1288 } 1289 1290 /** 1291 * Get whether plot signs are allowed or not. 1292 * 1293 * @return {@code true} if plot signs are allowed, {@code false} otherwise. 1294 */ 1295 public boolean allowSigns() { 1296 return allowSigns; 1297 } 1298 1299 /** 1300 * Get the plot sign material. 1301 * 1302 * @return the sign material. 1303 */ 1304 public String signMaterial() { 1305 return signMaterial; 1306 } 1307 1308 public String legacySignMaterial() { 1309 return legacySignMaterial; 1310 } 1311 1312 /** 1313 * Get the value associated with the specified flag. This will look at 1314 * the default values stored in {@link GlobalFlagContainer}. 1315 * 1316 * @param flagClass The flag type (Class) 1317 * @param <T> The flag value type 1318 * @return The flag value 1319 */ 1320 public <T> T getFlag(final Class<? extends PlotFlag<T, ?>> flagClass) { 1321 return this.flagContainer.getFlag(flagClass).getValue(); 1322 } 1323 1324 /** 1325 * Get the value associated with the specified flag. This will look at 1326 * the default values stored in {@link GlobalFlagContainer}. 1327 * 1328 * @param flag The flag type (Any instance of the flag) 1329 * @param <V> The flag type (Any instance of the flag) 1330 * @param <T> flag value type 1331 * @return The flag value 1332 */ 1333 public <T, V extends PlotFlag<T, ?>> T getFlag(final V flag) { 1334 final Class<?> flagClass = flag.getClass(); 1335 final PlotFlag<?, ?> flagInstance = this.flagContainer.getFlagErased(flagClass); 1336 return FlagContainer.<T, V>castUnsafe(flagInstance).getValue(); 1337 } 1338 1339 /** 1340 * Get the value associated with the specified road flag. This will look at 1341 * the default values stored in {@link GlobalFlagContainer}. 1342 * 1343 * @param flagClass The flag type (Class) 1344 * @param <T> the flag value type 1345 * @return The flag value 1346 */ 1347 public <T> T getRoadFlag(final Class<? extends PlotFlag<T, ?>> flagClass) { 1348 return this.roadFlagContainer.getFlag(flagClass).getValue(); 1349 } 1350 1351 /** 1352 * Get the value associated with the specified road flag. This will look at 1353 * the default values stored in {@link GlobalFlagContainer}. 1354 * 1355 * @param flag The flag type (Any instance of the flag) 1356 * @param <V> The flag type (Any instance of the flag) 1357 * @param <T> flag value type 1358 * @return The flag value 1359 */ 1360 public <T, V extends PlotFlag<T, ?>> T getRoadFlag(final V flag) { 1361 final Class<?> flagClass = flag.getClass(); 1362 final PlotFlag<?, ?> flagInstance = this.roadFlagContainer.getFlagErased(flagClass); 1363 return FlagContainer.<T, V>castUnsafe(flagInstance).getValue(); 1364 } 1365 1366 public @NonNull String getWorldName() { 1367 return this.worldName; 1368 } 1369 1370 public String getId() { 1371 return this.id; 1372 } 1373 1374 public @NonNull PlotManager getPlotManager() { 1375 return this.plotManager; 1376 } 1377 1378 public int getWorldHash() { 1379 return this.worldHash; 1380 } 1381 1382 public @NonNull IndependentPlotGenerator getGenerator() { 1383 return this.generator; 1384 } 1385 1386 public boolean isAutoMerge() { 1387 return this.autoMerge; 1388 } 1389 1390 public boolean isMiscSpawnUnowned() { 1391 return this.miscSpawnUnowned; 1392 } 1393 1394 public boolean isMobSpawning() { 1395 return this.mobSpawning; 1396 } 1397 1398 public boolean isMobSpawnerSpawning() { 1399 return this.mobSpawnerSpawning; 1400 } 1401 1402 public BiomeType getPlotBiome() { 1403 return this.plotBiome; 1404 } 1405 1406 public boolean isPlotChat() { 1407 return this.plotChat; 1408 } 1409 1410 public boolean isForcingPlotChat() { 1411 return this.forcingPlotChat; 1412 } 1413 1414 public boolean isSchematicClaimSpecify() { 1415 return this.schematicClaimSpecify; 1416 } 1417 1418 public boolean isSchematicOnClaim() { 1419 return this.schematicOnClaim; 1420 } 1421 1422 public String getSchematicFile() { 1423 return this.schematicFile; 1424 } 1425 1426 public boolean isSpawnEggs() { 1427 return this.spawnEggs; 1428 } 1429 1430 public String getSignMaterial() { 1431 return this.signMaterial; 1432 } 1433 1434 public boolean isSpawnCustom() { 1435 return this.spawnCustom; 1436 } 1437 1438 public boolean isSpawnBreeding() { 1439 return this.spawnBreeding; 1440 } 1441 1442 public PlotAreaType getType() { 1443 return this.type; 1444 } 1445 1446 /** 1447 * Set the type of this plot area. 1448 * 1449 * @param type the type of the plot area. 1450 */ 1451 public void setType(PlotAreaType type) { 1452 // TODO this should probably work only if type == null 1453 this.type = type; 1454 } 1455 1456 public PlotAreaTerrainType getTerrain() { 1457 return this.terrain; 1458 } 1459 1460 /** 1461 * Set the terrain generation type of this plot area. 1462 * 1463 * @param terrain the terrain type of the plot area. 1464 */ 1465 public void setTerrain(PlotAreaTerrainType terrain) { 1466 this.terrain = terrain; 1467 } 1468 1469 public boolean isHomeAllowNonmember() { 1470 return this.homeAllowNonmember; 1471 } 1472 1473 /** 1474 * Get the location for non-members to be teleported to. 1475 * 1476 * @since 6.1.4 1477 */ 1478 public BlockLoc nonmemberHome() { 1479 return this.nonmemberHome; 1480 } 1481 1482 /** 1483 * Get the default location for players to be teleported to. May be overridden by {@link #nonmemberHome} if the player is 1484 * not a member of the plot. 1485 * 1486 * @since 6.1.4 1487 */ 1488 public BlockLoc defaultHome() { 1489 return this.defaultHome; 1490 } 1491 1492 protected void setDefaultHome(BlockLoc defaultHome) { 1493 this.defaultHome = defaultHome; 1494 } 1495 1496 /** 1497 * Get the maximum height that changes to plot components (wall filling, air, all etc.) may operate to 1498 * 1499 * @since 7.3.4 1500 */ 1501 public int getMaxComponentHeight() { 1502 return this.maxBuildHeight; 1503 } 1504 1505 /** 1506 * Get the minimum height that changes to plot components (wall filling, air, all etc.) may operate to 1507 * 1508 * @since 7.3.4 1509 */ 1510 public int getMinComponentHeight() { 1511 return this.minBuildHeight; 1512 } 1513 1514 /** 1515 * Get the maximum height players may build in. Exclusive. 1516 */ 1517 public int getMaxBuildHeight() { 1518 return this.maxBuildHeight; 1519 } 1520 1521 /** 1522 * Get the minimum height players may build in. Inclusive. 1523 */ 1524 public int getMinBuildHeight() { 1525 return this.minBuildHeight; 1526 } 1527 1528 /** 1529 * Get the min height from which PlotSquared will generate blocks. Inclusive. 1530 * 1531 * @since 6.6.0 1532 */ 1533 public int getMinGenHeight() { 1534 return this.minGenHeight; 1535 } 1536 1537 /** 1538 * Get the max height to which PlotSquared will generate blocks. Inclusive. 1539 * 1540 * @since 6.6.0 1541 */ 1542 public int getMaxGenHeight() { 1543 return this.maxGenHeight; 1544 } 1545 1546 public GameMode getGameMode() { 1547 return this.gameMode; 1548 } 1549 1550 public Map<String, PlotExpression> getPrices() { 1551 return this.prices; 1552 } 1553 1554 protected List<String> getSchematics() { 1555 return this.schematics; 1556 } 1557 1558 public boolean isRoadFlags() { 1559 return this.roadFlags; 1560 } 1561 1562 public FlagContainer getFlagContainer() { 1563 return this.flagContainer; 1564 } 1565 1566 public FlagContainer getRoadFlagContainer() { 1567 return this.roadFlagContainer; 1568 } 1569 1570 public void setAllowSigns(boolean allowSigns) { 1571 this.allowSigns = allowSigns; 1572 } 1573 1574}