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