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