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