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