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("maxheight", Tag.inserting(Component.text(maxBuildHeight))) 683 .build() 684 ); 685 // Return true if "failed" as the method will always be inverted otherwise 686 return true; 687 } 688 return false; 689 } 690 691 public @NonNull Set<Plot> getPlotsAbs(final UUID uuid) { 692 if (uuid == null) { 693 return Collections.emptySet(); 694 } 695 final HashSet<Plot> myPlots = new HashSet<>(); 696 forEachPlotAbs(value -> { 697 if (uuid.equals(value.getOwnerAbs())) { 698 myPlots.add(value); 699 } 700 }); 701 return myPlots; 702 } 703 704 public @NonNull Set<Plot> getPlots(final @NonNull UUID uuid) { 705 return getPlots().stream().filter(plot -> plot.isBasePlot() && plot.isOwner(uuid)) 706 .collect(ImmutableSet.toImmutableSet()); 707 } 708 709 /** 710 * A collection of the claimed plots in this {@link PlotArea}. 711 * 712 * @return a collection of claimed plots 713 */ 714 public Collection<Plot> getPlots() { 715 return this.plots.values(); 716 } 717 718 public int getPlotCount(final @NonNull UUID uuid) { 719 if (!Settings.Done.COUNTS_TOWARDS_LIMIT) { 720 return (int) getPlotsAbs(uuid).stream().filter(plot -> !DoneFlag.isDone(plot)).count(); 721 } 722 return getPlotsAbs(uuid).size(); 723 } 724 725 /** 726 * Retrieves the plots for the player in this PlotArea. 727 * 728 * @param player player to get plots of 729 * @return set of player's plots 730 * @deprecated Use {@link #getPlots(UUID)} 731 */ 732 @Deprecated 733 public Set<Plot> getPlots(final @NonNull PlotPlayer<?> player) { 734 return getPlots(player.getUUID()); 735 } 736 737 //todo check if this method is needed in this class 738 739 public boolean hasPlot(final @NonNull UUID uuid) { 740 return this.plots.entrySet().stream().anyMatch(entry -> entry.getValue().isOwner(uuid)); 741 } 742 743 public int getPlotCount(final @Nullable PlotPlayer<?> player) { 744 return player != null ? getPlotCount(player.getUUID()) : 0; 745 } 746 747 public @Nullable Plot getPlotAbs(final @NonNull PlotId id) { 748 Plot plot = getOwnedPlotAbs(id); 749 if (plot == null) { 750 if (this.min != null && (id.getX() < this.min.getX() || id.getX() > this.max.getX() || id.getY() < this.min.getY() 751 || id.getY() > this.max.getY())) { 752 return null; 753 } 754 return new Plot(this, id); 755 } 756 return plot; 757 } 758 759 public @Nullable Plot getPlot(final @NonNull PlotId id) { 760 final Plot plot = getOwnedPlotAbs(id); 761 if (plot == null) { 762 if (this.min != null && (id.getX() < this.min.getX() || id.getX() > this.max.getX() || id.getY() < this.min.getY() 763 || id.getY() > this.max.getY())) { 764 return null; 765 } 766 return new Plot(this, id); 767 } 768 return plot.getBasePlot(false); 769 } 770 771 /** 772 * Retrieves the number of claimed plot in the {@link PlotArea}. 773 * 774 * @return the number of claimed plots 775 */ 776 public int getPlotCount() { 777 return this.plots.size(); 778 } 779 780 public @Nullable PlotCluster getCluster(final @NonNull Location location) { 781 final Plot plot = getPlot(location); 782 if (plot == null) { 783 return null; 784 } 785 return this.clusters != null ? this.clusters.get(plot.getId().getX(), plot.getId().getY()) : null; 786 } 787 788 public @Nullable PlotCluster getFirstIntersectingCluster( 789 final @NonNull PlotId pos1, 790 final @NonNull PlotId pos2 791 ) { 792 if (this.clusters == null) { 793 return null; 794 } 795 for (PlotCluster cluster : this.clusters.getAll()) { 796 if (cluster.intersects(pos1, pos2)) { 797 return cluster; 798 } 799 } 800 return null; 801 } 802 803 @Nullable PlotCluster getCluster(final @NonNull PlotId id) { 804 return this.clusters != null ? this.clusters.get(id.getX(), id.getY()) : null; 805 } 806 807 /** 808 * Session only plot metadata (session is until the server stops). 809 * <br> 810 * For persistent metadata use the flag system 811 * 812 * @param key metadata key 813 * @param value metadata value 814 */ 815 public void setMeta(final @NonNull String key, final @Nullable Object value) { 816 if (this.meta == null) { 817 this.meta = new ConcurrentHashMap<>(); 818 } 819 this.meta.put(key, value); 820 } 821 822 public @NonNull <T> T getMeta(final @NonNull String key, final @NonNull T def) { 823 final Object v = getMeta(key); 824 return v == null ? def : (T) v; 825 } 826 827 /** 828 * Get the metadata for a key<br> 829 * <br> 830 * For persistent metadata use the flag system 831 * 832 * @param key metadata key to get value for 833 * @return metadata value 834 */ 835 public @Nullable Object getMeta(final @NonNull String key) { 836 if (this.meta != null) { 837 return this.meta.get(key); 838 } 839 return null; 840 } 841 842 @SuppressWarnings("unused") 843 public @NonNull Set<Plot> getBasePlots() { 844 final HashSet<Plot> myPlots = new HashSet<>(getPlots()); 845 myPlots.removeIf(plot -> !plot.isBasePlot()); 846 return myPlots; 847 } 848 849 private void forEachPlotAbs(Consumer<Plot> run) { 850 for (final Entry<PlotId, Plot> entry : this.plots.entrySet()) { 851 run.accept(entry.getValue()); 852 } 853 } 854 855 public void forEachBasePlot(Consumer<Plot> run) { 856 for (final Plot plot : getPlots()) { 857 if (plot.isBasePlot()) { 858 run.accept(plot); 859 } 860 } 861 } 862 863 /** 864 * Returns an ImmutableMap of PlotId's and Plots in this PlotArea. 865 * 866 * @return map of PlotId against Plot for all plots in this area 867 * @deprecated Poorly implemented. May be removed in future. 868 */ 869 //todo eventually remove 870 @Deprecated 871 public @NonNull Map<PlotId, Plot> getPlotsRaw() { 872 return ImmutableMap.copyOf(plots); 873 } 874 875 public @NonNull Set<Entry<PlotId, Plot>> getPlotEntries() { 876 return this.plots.entrySet(); 877 } 878 879 public boolean addPlot(final @NonNull Plot plot) { 880 for (final PlotPlayer<?> pp : plot.getPlayersInPlot()) { 881 try (final MetaDataAccess<Plot> metaDataAccess = pp.accessTemporaryMetaData( 882 PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { 883 metaDataAccess.set(plot); 884 } 885 } 886 return this.plots.put(plot.getId(), plot) == null; 887 } 888 889 public Plot getNextFreePlot(final PlotPlayer<?> player, @Nullable PlotId start) { 890 int plots; 891 PlotId center; 892 PlotId min = getMin(); 893 PlotId max = getMax(); 894 if (getType() == PlotAreaType.PARTIAL) { 895 center = PlotId.of(MathMan.average(min.getX(), max.getX()), MathMan.average(min.getY(), max.getY())); 896 plots = Math.max(max.getX() - min.getX() + 1, max.getY() - min.getY() + 1) + 1; 897 if (start != null) { 898 start = PlotId.of(start.getX() - center.getX(), start.getY() - center.getY()); 899 } 900 } else { 901 center = PlotId.of(0, 0); 902 plots = Integer.MAX_VALUE; 903 } 904 for (int i = 0; i < plots; i++) { 905 if (start == null) { 906 start = getMeta("lastPlot", PlotId.of(0, 0)); 907 } else { 908 start = start.getNextId(); 909 } 910 PlotId currentId = PlotId.of(center.getX() + start.getX(), center.getY() + start.getY()); 911 Plot plot = getPlotAbs(currentId); 912 if (plot != null && plot.canClaim(player)) { 913 setMeta("lastPlot", start); 914 return plot; 915 } 916 } 917 return null; 918 } 919 920 public boolean addPlotIfAbsent(final @NonNull Plot plot) { 921 if (this.plots.putIfAbsent(plot.getId(), plot) == null) { 922 for (PlotPlayer<?> pp : plot.getPlayersInPlot()) { 923 try (final MetaDataAccess<Plot> metaDataAccess = pp.accessTemporaryMetaData( 924 PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) { 925 metaDataAccess.set(plot); 926 } 927 } 928 return true; 929 } 930 return false; 931 } 932 933 public boolean addPlotAbs(final @NonNull Plot plot) { 934 return this.plots.put(plot.getId(), plot) == null; 935 } 936 937 /** 938 * Get the plot border distance for a world<br> 939 * 940 * @return The border distance or Integer.MAX_VALUE if no border is set 941 * @deprecated Use {@link PlotArea#getBorder(boolean)} 942 */ 943 @Deprecated(forRemoval = true, since = "7.2.0") 944 public int getBorder() { 945 final Integer meta = (Integer) getMeta("worldBorder"); 946 if (meta != null) { 947 int border = meta + 1; 948 if (border == 0) { 949 return Integer.MAX_VALUE; 950 } else { 951 return border; 952 } 953 } 954 return Integer.MAX_VALUE; 955 } 956 957 /** 958 * Get the plot border distance for a world, specifying whether the returned value should include the world.border-size 959 * value. This is a player-traversable area, where plots cannot be claimed 960 * 961 * @param getExtended If the extra border given by world.border-size should be included 962 * @return Border distance of Integer.MAX_VALUE if no border is set 963 * @since 7.2.0 964 */ 965 public int getBorder(boolean getExtended) { 966 final Integer meta = (Integer) getMeta("worldBorder"); 967 if (meta != null) { 968 int border = meta + 1; 969 if (border == 0) { 970 return Integer.MAX_VALUE; 971 } else { 972 return getExtended ? border + borderSize : border; 973 } 974 } 975 return Integer.MAX_VALUE; 976 } 977 978 /** 979 * Setup the plot border for a world (usually done when the world is created). 980 */ 981 public void setupBorder() { 982 if (!this.hasWorldBorder()) { 983 return; 984 } 985 final Integer meta = (Integer) getMeta("worldBorder"); 986 if (meta == null) { 987 setMeta("worldBorder", 1); 988 } 989 for (final Plot plot : getPlots()) { 990 plot.updateWorldBorder(); 991 } 992 } 993 994 /** 995 * Delete the metadata for a key. 996 * - metadata is session only 997 * - deleting other plugin's metadata may cause issues 998 * 999 * @param key Meta data key 1000 */ 1001 public void deleteMeta(final @NonNull String key) { 1002 if (this.meta != null) { 1003 this.meta.remove(key); 1004 } 1005 } 1006 1007 public @Nullable List<Plot> canClaim( 1008 final @Nullable PlotPlayer<?> player, final @NonNull PlotId pos1, 1009 final @NonNull PlotId pos2 1010 ) { 1011 if (pos1.getX() == pos2.getX() && pos1.getY() == pos2.getY()) { 1012 if (getOwnedPlot(pos1) != null) { 1013 return null; 1014 } 1015 final Plot plot = getPlotAbs(pos1); 1016 if (plot == null) { 1017 return null; 1018 } 1019 if (plot.canClaim(player)) { 1020 return Collections.singletonList(plot); 1021 } else { 1022 return null; 1023 } 1024 } 1025 final List<Plot> plots = new LinkedList<>(); 1026 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 1027 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 1028 final PlotId id = PlotId.of(x, y); 1029 final Plot plot = getPlotAbs(id); 1030 if (plot == null) { 1031 return null; 1032 } 1033 if (!plot.canClaim(player)) { 1034 return null; 1035 } else { 1036 plots.add(plot); 1037 } 1038 } 1039 } 1040 return plots; 1041 } 1042 1043 public boolean removePlot(final @NonNull PlotId id) { 1044 return this.plots.remove(id) != null; 1045 } 1046 1047 /** 1048 * Merge a list of plots together. This is non-blocking for the world-changes that will be made. To run a task when the 1049 * world changes are complete, use {@link PlotArea#mergePlots(List, boolean, Runnable)}; 1050 * 1051 * @param plotIds List of plot IDs to merge 1052 * @param removeRoads If the roads between plots should be removed 1053 * @return if merges were completed successfully. 1054 */ 1055 public boolean mergePlots(final @NonNull List<PlotId> plotIds, final boolean removeRoads) { 1056 return mergePlots(plotIds, removeRoads, null); 1057 } 1058 1059 /** 1060 * Merge a list of plots together. This is non-blocking for the world-changes that will be made. 1061 * 1062 * @param plotIds List of plot IDs to merge 1063 * @param removeRoads If the roads between plots should be removed 1064 * @param whenDone Task to run when any merge world changes are complete. Also runs if no changes were made. Does not 1065 * run if there was an error or if too few plots IDs were supplied. 1066 * @return if merges were completed successfully. 1067 * @since 6.9.0 1068 */ 1069 public boolean mergePlots( 1070 final @NonNull List<PlotId> plotIds, final boolean removeRoads, final @Nullable Runnable whenDone 1071 ) { 1072 if (plotIds.size() < 2) { 1073 return false; 1074 } 1075 1076 final PlotId pos1 = plotIds.get(0); 1077 final PlotId pos2 = plotIds.get(plotIds.size() - 1); 1078 final PlotManager manager = getPlotManager(); 1079 1080 QueueCoordinator queue = getQueue(); 1081 manager.startPlotMerge(plotIds, queue); 1082 final Set<UUID> trusted = new HashSet<>(); 1083 final Set<UUID> members = new HashSet<>(); 1084 final Set<UUID> denied = new HashSet<>(); 1085 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 1086 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 1087 PlotId id = PlotId.of(x, y); 1088 Plot plot = getPlotAbs(id); 1089 trusted.addAll(plot.getTrusted()); 1090 members.addAll(plot.getMembers()); 1091 denied.addAll(plot.getDenied()); 1092 if (removeRoads) { 1093 plot.getPlotModificationManager().removeSign(); 1094 } 1095 } 1096 } 1097 members.removeAll(trusted); 1098 denied.removeAll(trusted); 1099 denied.removeAll(members); 1100 for (int x = pos1.getX(); x <= pos2.getX(); x++) { 1101 for (int y = pos1.getY(); y <= pos2.getY(); y++) { 1102 final boolean lx = x < pos2.getX(); 1103 final boolean ly = y < pos2.getY(); 1104 final PlotId id = PlotId.of(x, y); 1105 final Plot plot = getPlotAbs(id); 1106 1107 plot.setTrusted(trusted); 1108 plot.setMembers(members); 1109 plot.setDenied(denied); 1110 1111 Plot plot2; 1112 if (lx) { 1113 if (ly) { 1114 if (!plot.isMerged(Direction.EAST) || !plot.isMerged(Direction.SOUTH)) { 1115 if (removeRoads) { 1116 plot.getPlotModificationManager().removeRoadSouthEast(queue); 1117 } 1118 } 1119 } 1120 if (!plot.isMerged(Direction.EAST)) { 1121 plot2 = plot.getRelative(1, 0); 1122 plot.mergePlot(plot2, removeRoads, queue); 1123 } 1124 } 1125 if (ly) { 1126 if (!plot.isMerged(Direction.SOUTH)) { 1127 plot2 = plot.getRelative(0, 1); 1128 plot.mergePlot(plot2, removeRoads, queue); 1129 } 1130 } 1131 } 1132 } 1133 manager.finishPlotMerge(plotIds, queue); 1134 if (whenDone != null) { 1135 queue.setCompleteTask(whenDone); 1136 } 1137 queue.enqueue(); 1138 return true; 1139 } 1140 1141 /** 1142 * Get a set of owned plots within a selection (chooses the best algorithm based on selection size. 1143 * i.e. A selection of billions of plots will work fine 1144 * 1145 * @param pos1 first corner of selection 1146 * @param pos2 second corner of selection 1147 * @return the plots in the selection which are owned 1148 */ 1149 public Set<Plot> getPlotSelectionOwned(final @NonNull PlotId pos1, final @NonNull PlotId pos2) { 1150 final int size = (1 + pos2.getX() - pos1.getX()) * (1 + pos2.getY() - pos1.getY()); 1151 final Set<Plot> result = new HashSet<>(); 1152 if (size < 16 || size < getPlotCount()) { 1153 for (final PlotId pid : Lists.newArrayList((Iterable<? extends PlotId>) 1154 PlotId.PlotRangeIterator.range(pos1, pos2))) { 1155 final Plot plot = getPlotAbs(pid); 1156 if (plot.hasOwner()) { 1157 if (plot.getId().getX() > pos1.getX() || plot.getId().getY() > pos1.getY() 1158 || plot.getId().getX() < pos2.getX() || plot.getId().getY() < pos2.getY()) { 1159 result.add(plot); 1160 } 1161 } 1162 } 1163 } else { 1164 for (final Plot plot : getPlots()) { 1165 if (plot.getId().getX() > pos1.getX() || plot.getId().getY() > pos1.getY() || plot.getId().getX() < pos2.getX() 1166 || plot.getId().getY() < pos2.getY()) { 1167 result.add(plot); 1168 } 1169 } 1170 } 1171 return result; 1172 } 1173 1174 @SuppressWarnings("WeakerAccess") 1175 public void removeCluster(final @Nullable PlotCluster plotCluster) { 1176 if (this.clusters == null) { 1177 throw new IllegalAccessError("Clusters not enabled!"); 1178 } 1179 this.clusters.remove(plotCluster); 1180 } 1181 1182 public void addCluster(final @Nullable PlotCluster plotCluster) { 1183 if (this.clusters == null) { 1184 this.clusters = new QuadMap<>(Integer.MAX_VALUE, 0, 0, 62) { 1185 @Override 1186 public CuboidRegion getRegion(PlotCluster value) { 1187 BlockVector2 pos1 = BlockVector2.at(value.getP1().getX(), value.getP1().getY()); 1188 BlockVector2 pos2 = BlockVector2.at(value.getP2().getX(), value.getP2().getY()); 1189 return new CuboidRegion( 1190 pos1.toBlockVector3(getMinGenHeight()), 1191 pos2.toBlockVector3(getMaxGenHeight()) 1192 ); 1193 } 1194 }; 1195 } 1196 this.clusters.add(plotCluster); 1197 } 1198 1199 public @Nullable PlotCluster getCluster(final String string) { 1200 for (PlotCluster cluster : getClusters()) { 1201 if (cluster.getName().equalsIgnoreCase(string)) { 1202 return cluster; 1203 } 1204 } 1205 return null; 1206 } 1207 1208 /** 1209 * Get whether a schematic with that name is available or not. 1210 * If a schematic is available, it can be used for plot claiming. 1211 * 1212 * @param schematic the schematic to look for. 1213 * @return {@code true} if the schematic exists, {@code false} otherwise. 1214 */ 1215 public boolean hasSchematic(@NonNull String schematic) { 1216 return getSchematics().contains(schematic.toLowerCase()); 1217 } 1218 1219 /** 1220 * Get whether economy is enabled and used on this plot area or not. 1221 * 1222 * @return {@code true} if this plot area uses economy, {@code false} otherwise. 1223 */ 1224 public boolean useEconomy() { 1225 return useEconomy; 1226 } 1227 1228 /** 1229 * Get whether the plot area is limited by a world border or not. 1230 * 1231 * @return {@code true} if the plot area has a world border, {@code false} otherwise. 1232 */ 1233 public boolean hasWorldBorder() { 1234 return worldBorder; 1235 } 1236 1237 /** 1238 * Get the "extra border" size of the plot area. 1239 * 1240 * @return Plot area extra border size 1241 * @since 7.2.0 1242 */ 1243 public int getBorderSize() { 1244 return borderSize; 1245 } 1246 1247 /** 1248 * Get whether plot signs are allowed or not. 1249 * 1250 * @return {@code true} if plot signs are allowed, {@code false} otherwise. 1251 */ 1252 public boolean allowSigns() { 1253 return allowSigns; 1254 } 1255 1256 /** 1257 * Get the plot sign material. 1258 * 1259 * @return the sign material. 1260 */ 1261 public String signMaterial() { 1262 return signMaterial; 1263 } 1264 1265 public String legacySignMaterial() { 1266 return legacySignMaterial; 1267 } 1268 1269 /** 1270 * Get the value associated with the specified flag. This will look at 1271 * the default values stored in {@link GlobalFlagContainer}. 1272 * 1273 * @param flagClass The flag type (Class) 1274 * @param <T> The flag value type 1275 * @return The flag value 1276 */ 1277 public <T> T getFlag(final Class<? extends PlotFlag<T, ?>> flagClass) { 1278 return this.flagContainer.getFlag(flagClass).getValue(); 1279 } 1280 1281 /** 1282 * Get the value associated with the specified flag. This will look at 1283 * the default values stored in {@link GlobalFlagContainer}. 1284 * 1285 * @param flag The flag type (Any instance of the flag) 1286 * @param <V> The flag type (Any instance of the flag) 1287 * @param <T> flag value type 1288 * @return The flag value 1289 */ 1290 public <T, V extends PlotFlag<T, ?>> T getFlag(final V flag) { 1291 final Class<?> flagClass = flag.getClass(); 1292 final PlotFlag<?, ?> flagInstance = this.flagContainer.getFlagErased(flagClass); 1293 return FlagContainer.<T, V>castUnsafe(flagInstance).getValue(); 1294 } 1295 1296 /** 1297 * Get the value associated with the specified road flag. This will look at 1298 * the default values stored in {@link GlobalFlagContainer}. 1299 * 1300 * @param flagClass The flag type (Class) 1301 * @param <T> the flag value type 1302 * @return The flag value 1303 */ 1304 public <T> T getRoadFlag(final Class<? extends PlotFlag<T, ?>> flagClass) { 1305 return this.roadFlagContainer.getFlag(flagClass).getValue(); 1306 } 1307 1308 /** 1309 * Get the value associated with the specified road flag. This will look at 1310 * the default values stored in {@link GlobalFlagContainer}. 1311 * 1312 * @param flag The flag type (Any instance of the flag) 1313 * @param <V> The flag type (Any instance of the flag) 1314 * @param <T> flag value type 1315 * @return The flag value 1316 */ 1317 public <T, V extends PlotFlag<T, ?>> T getRoadFlag(final V flag) { 1318 final Class<?> flagClass = flag.getClass(); 1319 final PlotFlag<?, ?> flagInstance = this.roadFlagContainer.getFlagErased(flagClass); 1320 return FlagContainer.<T, V>castUnsafe(flagInstance).getValue(); 1321 } 1322 1323 public @NonNull String getWorldName() { 1324 return this.worldName; 1325 } 1326 1327 public String getId() { 1328 return this.id; 1329 } 1330 1331 public @NonNull PlotManager getPlotManager() { 1332 return this.plotManager; 1333 } 1334 1335 public int getWorldHash() { 1336 return this.worldHash; 1337 } 1338 1339 public @NonNull IndependentPlotGenerator getGenerator() { 1340 return this.generator; 1341 } 1342 1343 public boolean isAutoMerge() { 1344 return this.autoMerge; 1345 } 1346 1347 public boolean isMiscSpawnUnowned() { 1348 return this.miscSpawnUnowned; 1349 } 1350 1351 public boolean isMobSpawning() { 1352 return this.mobSpawning; 1353 } 1354 1355 public boolean isMobSpawnerSpawning() { 1356 return this.mobSpawnerSpawning; 1357 } 1358 1359 public BiomeType getPlotBiome() { 1360 return this.plotBiome; 1361 } 1362 1363 public boolean isPlotChat() { 1364 return this.plotChat; 1365 } 1366 1367 public boolean isForcingPlotChat() { 1368 return this.forcingPlotChat; 1369 } 1370 1371 public boolean isSchematicClaimSpecify() { 1372 return this.schematicClaimSpecify; 1373 } 1374 1375 public boolean isSchematicOnClaim() { 1376 return this.schematicOnClaim; 1377 } 1378 1379 public String getSchematicFile() { 1380 return this.schematicFile; 1381 } 1382 1383 public boolean isSpawnEggs() { 1384 return this.spawnEggs; 1385 } 1386 1387 public String getSignMaterial() { 1388 return this.signMaterial; 1389 } 1390 1391 public boolean isSpawnCustom() { 1392 return this.spawnCustom; 1393 } 1394 1395 public boolean isSpawnBreeding() { 1396 return this.spawnBreeding; 1397 } 1398 1399 public PlotAreaType getType() { 1400 return this.type; 1401 } 1402 1403 /** 1404 * Set the type of this plot area. 1405 * 1406 * @param type the type of the plot area. 1407 */ 1408 public void setType(PlotAreaType type) { 1409 // TODO this should probably work only if type == null 1410 this.type = type; 1411 } 1412 1413 public PlotAreaTerrainType getTerrain() { 1414 return this.terrain; 1415 } 1416 1417 /** 1418 * Set the terrain generation type of this plot area. 1419 * 1420 * @param terrain the terrain type of the plot area. 1421 */ 1422 public void setTerrain(PlotAreaTerrainType terrain) { 1423 this.terrain = terrain; 1424 } 1425 1426 public boolean isHomeAllowNonmember() { 1427 return this.homeAllowNonmember; 1428 } 1429 1430 /** 1431 * Get the location for non-members to be teleported to. 1432 * 1433 * @since 6.1.4 1434 */ 1435 public BlockLoc nonmemberHome() { 1436 return this.nonmemberHome; 1437 } 1438 1439 /** 1440 * Get the default location for players to be teleported to. May be overridden by {@link #nonmemberHome} if the player is 1441 * not a member of the plot. 1442 * 1443 * @since 6.1.4 1444 */ 1445 public BlockLoc defaultHome() { 1446 return this.defaultHome; 1447 } 1448 1449 protected void setDefaultHome(BlockLoc defaultHome) { 1450 this.defaultHome = defaultHome; 1451 } 1452 1453 /** 1454 * Get the maximum height that changes to plot components (wall filling, air, all etc.) may operate to 1455 * 1456 * @since 7.3.4 1457 */ 1458 public int getMaxComponentHeight() { 1459 return this.maxBuildHeight; 1460 } 1461 1462 /** 1463 * Get the minimum height that changes to plot components (wall filling, air, all etc.) may operate to 1464 * 1465 * @since 7.3.4 1466 */ 1467 public int getMinComponentHeight() { 1468 return this.minBuildHeight; 1469 } 1470 1471 /** 1472 * Get the maximum height players may build in. Exclusive. 1473 */ 1474 public int getMaxBuildHeight() { 1475 return this.maxBuildHeight; 1476 } 1477 1478 /** 1479 * Get the minimum height players may build in. Inclusive. 1480 */ 1481 public int getMinBuildHeight() { 1482 return this.minBuildHeight; 1483 } 1484 1485 /** 1486 * Get the min height from which PlotSquared will generate blocks. Inclusive. 1487 * 1488 * @since 6.6.0 1489 */ 1490 public int getMinGenHeight() { 1491 return this.minGenHeight; 1492 } 1493 1494 /** 1495 * Get the max height to which PlotSquared will generate blocks. Inclusive. 1496 * 1497 * @since 6.6.0 1498 */ 1499 public int getMaxGenHeight() { 1500 return this.maxGenHeight; 1501 } 1502 1503 public GameMode getGameMode() { 1504 return this.gameMode; 1505 } 1506 1507 public Map<String, PlotExpression> getPrices() { 1508 return this.prices; 1509 } 1510 1511 protected List<String> getSchematics() { 1512 return this.schematics; 1513 } 1514 1515 public boolean isRoadFlags() { 1516 return this.roadFlags; 1517 } 1518 1519 public FlagContainer getFlagContainer() { 1520 return this.flagContainer; 1521 } 1522 1523 public FlagContainer getRoadFlagContainer() { 1524 return this.roadFlagContainer; 1525 } 1526 1527 public void setAllowSigns(boolean allowSigns) { 1528 this.allowSigns = allowSigns; 1529 } 1530 1531}