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.command; 020 021import com.google.inject.Inject; 022import com.plotsquared.core.PlotSquared; 023import com.plotsquared.core.configuration.ConfigurationSection; 024import com.plotsquared.core.configuration.ConfigurationUtil; 025import com.plotsquared.core.configuration.Settings; 026import com.plotsquared.core.configuration.caption.CaptionHolder; 027import com.plotsquared.core.configuration.caption.TranslatableCaption; 028import com.plotsquared.core.configuration.file.YamlConfiguration; 029import com.plotsquared.core.events.TeleportCause; 030import com.plotsquared.core.generator.AugmentedUtils; 031import com.plotsquared.core.generator.HybridPlotWorld; 032import com.plotsquared.core.inject.annotations.WorldConfig; 033import com.plotsquared.core.inject.annotations.WorldFile; 034import com.plotsquared.core.inject.factory.HybridPlotWorldFactory; 035import com.plotsquared.core.location.Location; 036import com.plotsquared.core.permissions.Permission; 037import com.plotsquared.core.player.ConsolePlayer; 038import com.plotsquared.core.player.PlotPlayer; 039import com.plotsquared.core.plot.PlotArea; 040import com.plotsquared.core.plot.PlotAreaTerrainType; 041import com.plotsquared.core.plot.PlotAreaType; 042import com.plotsquared.core.plot.PlotId; 043import com.plotsquared.core.plot.world.PlotAreaManager; 044import com.plotsquared.core.plot.world.SinglePlotArea; 045import com.plotsquared.core.queue.GlobalBlockQueue; 046import com.plotsquared.core.queue.QueueCoordinator; 047import com.plotsquared.core.setup.PlotAreaBuilder; 048import com.plotsquared.core.util.FileUtils; 049import com.plotsquared.core.util.MathMan; 050import com.plotsquared.core.util.RegionUtil; 051import com.plotsquared.core.util.SchematicHandler; 052import com.plotsquared.core.util.SetupUtils; 053import com.plotsquared.core.util.StringMan; 054import com.plotsquared.core.util.TabCompletions; 055import com.plotsquared.core.util.WorldUtil; 056import com.plotsquared.core.util.task.RunnableVal3; 057import com.sk89q.worldedit.EditSession; 058import com.sk89q.worldedit.EditSessionBuilder; 059import com.sk89q.worldedit.LocalSession; 060import com.sk89q.worldedit.WorldEdit; 061import com.sk89q.worldedit.entity.Player; 062import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; 063import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat; 064import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; 065import com.sk89q.worldedit.function.operation.ForwardExtentCopy; 066import com.sk89q.worldedit.function.operation.Operations; 067import com.sk89q.worldedit.math.BlockVector3; 068import com.sk89q.worldedit.regions.CuboidRegion; 069import com.sk89q.worldedit.regions.Region; 070import com.sk89q.worldedit.world.World; 071import net.kyori.adventure.text.Component; 072import net.kyori.adventure.text.minimessage.tag.Tag; 073import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 074import org.checkerframework.checker.nullness.qual.NonNull; 075 076import java.io.File; 077import java.io.FileOutputStream; 078import java.io.IOException; 079import java.util.ArrayList; 080import java.util.Arrays; 081import java.util.Collection; 082import java.util.Collections; 083import java.util.HashMap; 084import java.util.LinkedList; 085import java.util.List; 086import java.util.Map; 087import java.util.Objects; 088import java.util.Set; 089import java.util.UUID; 090import java.util.stream.Collectors; 091 092@CommandDeclaration(command = "area", 093 permission = "plots.area", 094 category = CommandCategory.ADMINISTRATION, 095 requiredType = RequiredType.NONE, 096 aliases = "world", 097 usage = "/plot area <create | info | list | tp | regen>", 098 confirmation = true) 099public class Area extends SubCommand { 100 101 private final PlotAreaManager plotAreaManager; 102 private final YamlConfiguration worldConfiguration; 103 private final File worldFile; 104 private final HybridPlotWorldFactory hybridPlotWorldFactory; 105 private final SetupUtils setupUtils; 106 private final WorldUtil worldUtil; 107 private final GlobalBlockQueue blockQueue; 108 109 private final Map<UUID, Map<String, Object>> metaData = new HashMap<>(); 110 111 @Inject 112 public Area( 113 final @NonNull PlotAreaManager plotAreaManager, 114 @WorldConfig final @NonNull YamlConfiguration worldConfiguration, 115 @WorldFile final @NonNull File worldFile, 116 final @NonNull HybridPlotWorldFactory hybridPlotWorldFactory, 117 final @NonNull SetupUtils setupUtils, 118 final @NonNull WorldUtil worldUtil, 119 final @NonNull GlobalBlockQueue blockQueue 120 ) { 121 this.plotAreaManager = plotAreaManager; 122 this.worldConfiguration = worldConfiguration; 123 this.worldFile = worldFile; 124 this.hybridPlotWorldFactory = hybridPlotWorldFactory; 125 this.setupUtils = setupUtils; 126 this.worldUtil = worldUtil; 127 this.blockQueue = blockQueue; 128 } 129 130 @Override 131 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 132 if (args.length == 0) { 133 sendUsage(player); 134 return false; 135 } 136 switch (args[0].toLowerCase()) { 137 case "single" -> { 138 if (player instanceof ConsolePlayer) { 139 player.sendMessage(RequiredType.CONSOLE.getErrorMessage()); 140 return false; 141 } 142 if (!player.hasPermission(Permission.PERMISSION_AREA_CREATE)) { 143 player.sendMessage( 144 TranslatableCaption.of("permission.no_permission"), 145 TagResolver.resolver( 146 "node", 147 Tag.inserting(Permission.PERMISSION_AREA_CREATE) 148 ) 149 ); 150 return false; 151 } 152 if (args.length < 2) { 153 player.sendMessage( 154 TranslatableCaption.of("single.single_area_needs_name"), 155 TagResolver.resolver("command", Tag.inserting(Component.text("/plot area single <name>"))) 156 ); 157 return false; 158 } 159 final PlotArea existingArea = this.plotAreaManager.getPlotArea(player.getLocation().getWorldName(), args[1]); 160 if (existingArea != null && existingArea.getId().equalsIgnoreCase(args[1])) { 161 player.sendMessage(TranslatableCaption.of("single.single_area_name_taken")); 162 return false; 163 } 164 final LocalSession localSession = WorldEdit.getInstance().getSessionManager().getIfPresent(player.toActor()); 165 if (localSession == null) { 166 player.sendMessage(TranslatableCaption.of("single.single_area_missing_selection")); 167 return false; 168 } 169 Region playerSelectedRegion = null; 170 try { 171 playerSelectedRegion = localSession.getSelection(((Player) player.toActor()).getWorld()); 172 } catch (final Exception ignored) { 173 } 174 if (playerSelectedRegion == null) { 175 player.sendMessage(TranslatableCaption.of("single.single_area_missing_selection")); 176 return false; 177 } 178 if (playerSelectedRegion.getWidth() != playerSelectedRegion.getLength()) { 179 player.sendMessage(TranslatableCaption.of("single.single_area_not_square")); 180 return false; 181 } 182 if (this.plotAreaManager.getPlotAreas( 183 Objects.requireNonNull(playerSelectedRegion.getWorld()).getName(), 184 CuboidRegion.makeCuboid(playerSelectedRegion) 185 ).length != 0) { 186 player.sendMessage(TranslatableCaption.of("single.single_area_overlapping")); 187 } 188 // Alter the region 189 final BlockVector3 playerSelectionMin = playerSelectedRegion.getMinimumPoint(); 190 final BlockVector3 playerSelectionMax = playerSelectedRegion.getMaximumPoint(); 191 // Create a new selection that spans the entire vertical range of the world 192 World world = playerSelectedRegion.getWorld(); 193 final CuboidRegion selectedRegion = 194 new CuboidRegion( 195 playerSelectedRegion.getWorld(), 196 BlockVector3.at(playerSelectionMin.getX(), world.getMinY(), playerSelectionMin.getZ()), 197 BlockVector3.at(playerSelectionMax.getX(), world.getMaxY(), playerSelectionMax.getZ()) 198 ); 199 // There's only one plot in the area... 200 final PlotId plotId = PlotId.of(1, 1); 201 final HybridPlotWorld hybridPlotWorld = this.hybridPlotWorldFactory 202 .create( 203 player.getLocation().getWorldName(), 204 args[1], 205 Objects.requireNonNull(PlotSquared.platform()).defaultGenerator(), 206 plotId, 207 plotId 208 ); 209 // Plot size is the same as the region width 210 hybridPlotWorld.PLOT_WIDTH = hybridPlotWorld.SIZE = (short) selectedRegion.getWidth(); 211 // We use a schematic generator 212 hybridPlotWorld.setTerrain(PlotAreaTerrainType.NONE); 213 // It is always a partial plot world 214 hybridPlotWorld.setType(PlotAreaType.PARTIAL); 215 // We save the schematic :D 216 hybridPlotWorld.PLOT_SCHEMATIC = true; 217 // Set the road width to 0 218 hybridPlotWorld.ROAD_WIDTH = hybridPlotWorld.ROAD_OFFSET_X = hybridPlotWorld.ROAD_OFFSET_Z = 0; 219 // Set the plot height to the selection height 220 hybridPlotWorld.PLOT_HEIGHT = hybridPlotWorld.ROAD_HEIGHT = hybridPlotWorld.WALL_HEIGHT = playerSelectionMin.getBlockY(); 221 // No sign plz 222 hybridPlotWorld.setAllowSigns(false); 223 final File parentFile = FileUtils.getFile( 224 PlotSquared.platform().getDirectory(), 225 Settings.Paths.SCHEMATICS + File.separator + "GEN_ROAD_SCHEMATIC" + File.separator + hybridPlotWorld.getWorldName() + File.separator 226 + hybridPlotWorld.getId() 227 ); 228 if (!parentFile.exists() && !parentFile.mkdirs()) { 229 player.sendMessage(TranslatableCaption.of("single.single_area_could_not_make_directories")); 230 return false; 231 } 232 final File file = new File(parentFile, "plot.schem"); 233 try (final ClipboardWriter clipboardWriter = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream( 234 file))) { 235 final BlockArrayClipboard clipboard = new BlockArrayClipboard(selectedRegion); 236 EditSessionBuilder editSessionBuilder = WorldEdit.getInstance().newEditSessionBuilder(); 237 editSessionBuilder.world(selectedRegion.getWorld()); 238 final EditSession editSession = editSessionBuilder.build(); 239 final ForwardExtentCopy forwardExtentCopy = 240 new ForwardExtentCopy(editSession, selectedRegion, clipboard, selectedRegion.getMinimumPoint()); 241 forwardExtentCopy.setCopyingBiomes(true); 242 forwardExtentCopy.setCopyingEntities(true); 243 Operations.complete(forwardExtentCopy); 244 clipboardWriter.write(clipboard); 245 } catch (final Exception e) { 246 player.sendMessage(TranslatableCaption.of("single.single_area_failed_to_save")); 247 e.printStackTrace(); 248 return false; 249 } 250 251 // Setup schematic 252 try { 253 hybridPlotWorld.setupSchematics(); 254 } catch (final SchematicHandler.UnsupportedFormatException e) { 255 e.printStackTrace(); 256 } 257 258 // Calculate the offset 259 final BlockVector3 singlePos1 = selectedRegion.getMinimumPoint(); 260 261 // Now the schematic is saved, which is wonderful! 262 PlotAreaBuilder singleBuilder = PlotAreaBuilder.ofPlotArea(hybridPlotWorld).plotManager(PlotSquared 263 .platform() 264 .pluginName()) 265 .generatorName(PlotSquared.platform().pluginName()).maximumId(plotId).minimumId(plotId); 266 Runnable singleRun = () -> { 267 final String path = 268 "worlds." + hybridPlotWorld.getWorldName() + ".areas." + hybridPlotWorld.getId() + '-' + singleBuilder 269 .minimumId() + '-' 270 + singleBuilder.maximumId(); 271 final int offsetX = singlePos1.getX(); 272 final int offsetZ = singlePos1.getZ(); 273 if (offsetX != 0) { 274 this.worldConfiguration.set(path + ".road.offset.x", offsetX); 275 } 276 if (offsetZ != 0) { 277 this.worldConfiguration.set(path + ".road.offset.z", offsetZ); 278 } 279 final String worldName = this.setupUtils.setupWorld(singleBuilder); 280 if (this.worldUtil.isWorld(worldName)) { 281 PlotSquared.get().loadWorld(worldName, null); 282 player.sendMessage(TranslatableCaption.of("single.single_area_created")); 283 } else { 284 player.sendMessage( 285 TranslatableCaption.of("errors.error_create"), 286 TagResolver.resolver("world", Tag.inserting(Component.text(hybridPlotWorld.getWorldName()))) 287 ); 288 } 289 }; 290 singleRun.run(); 291 return true; 292 } 293 case "c", "setup", "create" -> { 294 if (!player.hasPermission(Permission.PERMISSION_AREA_CREATE)) { 295 player.sendMessage( 296 TranslatableCaption.of("permission.no_permission"), 297 TagResolver.resolver( 298 "node", 299 Tag.inserting(Permission.PERMISSION_AREA_CREATE) 300 ) 301 ); 302 return false; 303 } 304 switch (args.length) { 305 case 1: 306 player.sendMessage( 307 TranslatableCaption.of("commandconfig.command_syntax"), 308 TagResolver.resolver( 309 "value", 310 Tag.inserting(Component.text("/plot area create [world[:id]] [<modifier>=<value>]...")) 311 ) 312 ); 313 return false; 314 case 2: 315 switch (args[1].toLowerCase()) { 316 case "pos1" -> { // Set position 1 317 HybridPlotWorld area = (HybridPlotWorld) metaData.computeIfAbsent( 318 player.getUUID(), 319 missingUUID -> new HashMap<>() 320 ) 321 .get("area_create_area"); 322 if (area == null) { 323 player.sendMessage( 324 TranslatableCaption.of("commandconfig.command_syntax"), 325 TagResolver.resolver( 326 "value", 327 Tag.inserting(Component.text( 328 "/plot area create [world[:id]] [<modifier>=<value>]...")) 329 ) 330 ); 331 return false; 332 } 333 Location location = player.getLocation(); 334 metaData.computeIfAbsent(player.getUUID(), missingUUID -> new HashMap<>()).put( 335 "area_pos1", 336 location 337 ); 338 player.sendMessage( 339 TranslatableCaption.of("set.set_attribute"), 340 TagResolver.builder() 341 .tag("attribute", Tag.inserting(Component.text("area_pos1"))) 342 .tag("value", Tag.inserting( 343 Component.text(location.getX()) 344 .append(Component.text(",")) 345 .append(Component.text(location.getZ())) 346 )) 347 .build() 348 ); 349 player.sendMessage( 350 TranslatableCaption.of("area.set_pos2"), 351 TagResolver.resolver("command", Tag.inserting(Component.text("/plot area create pos2"))) 352 ); 353 return true; 354 } 355 case "pos2" -> { // Set position 2 and finish creation for type=2 (partial) 356 final HybridPlotWorld area = 357 (HybridPlotWorld) metaData.computeIfAbsent( 358 player.getUUID(), 359 missingUUID -> new HashMap<>() 360 ) 361 .get("area_create_area"); 362 if (area == null) { 363 player.sendMessage( 364 TranslatableCaption.of("commandconfig.command_syntax"), 365 TagResolver.resolver( 366 "value", 367 Tag.inserting(Component.text( 368 "/plot area create [world[:id]] [<modifier>=<value>]...")) 369 ) 370 ); 371 return false; 372 } 373 Location pos1 = player.getLocation(); 374 Location pos2 = 375 (Location) metaData.computeIfAbsent(player.getUUID(), missingUUID -> new HashMap<>()).get( 376 "area_pos1"); 377 int dx = Math.abs(pos1.getX() - pos2.getX()); 378 int dz = Math.abs(pos1.getZ() - pos2.getZ()); 379 int numX = Math.max(1, (dx + 1 + area.ROAD_WIDTH + area.SIZE / 2) / area.SIZE); 380 int numZ = Math.max(1, (dz + 1 + area.ROAD_WIDTH + area.SIZE / 2) / area.SIZE); 381 int ddx = dx - (numX * area.SIZE - area.ROAD_WIDTH); 382 int ddz = dz - (numZ * area.SIZE - area.ROAD_WIDTH); 383 int bx = Math.min(pos1.getX(), pos2.getX()) + ddx; 384 int bz = Math.min(pos1.getZ(), pos2.getZ()) + ddz; 385 int tx = Math.max(pos1.getX(), pos2.getX()) - ddx; 386 int tz = Math.max(pos1.getZ(), pos2.getZ()) - ddz; 387 int lower = (area.ROAD_WIDTH & 1) == 0 ? area.ROAD_WIDTH / 2 - 1 : area.ROAD_WIDTH / 2; 388 final int offsetX = bx - (area.ROAD_WIDTH == 0 ? 0 : lower); 389 final int offsetZ = bz - (area.ROAD_WIDTH == 0 ? 0 : lower); 390 // Height doesn't matter for this region 391 final CuboidRegion region = RegionUtil.createRegion(bx, tx, 0, 0, bz, tz); 392 final Set<PlotArea> areas = this.plotAreaManager.getPlotAreasSet(area.getWorldName(), region); 393 if (!areas.isEmpty()) { 394 player.sendMessage( 395 TranslatableCaption.of("cluster.cluster_intersection"), 396 TagResolver.resolver( 397 "cluster", 398 Tag.inserting(areas.iterator().next()) 399 ) 400 ); 401 return false; 402 } 403 PlotAreaBuilder builder = PlotAreaBuilder.ofPlotArea(area).plotManager(PlotSquared 404 .platform() 405 .pluginName()) 406 .generatorName(PlotSquared.platform().pluginName()).minimumId(PlotId.of(1, 1)) 407 .maximumId(PlotId.of(numX, numZ)); 408 final String path = 409 "worlds." + area.getWorldName() + ".areas." + area.getId() + '-' + builder.minimumId() + '-' + builder 410 .maximumId(); 411 Runnable run = () -> { 412 if (offsetX != 0) { 413 this.worldConfiguration.set(path + ".road.offset.x", offsetX); 414 } 415 if (offsetZ != 0) { 416 this.worldConfiguration.set(path + ".road.offset.z", offsetZ); 417 } 418 final String world = this.setupUtils.setupWorld(builder); 419 if (this.worldUtil.isWorld(world)) { 420 PlotSquared.get().loadWorld(world, null); 421 player.teleport(this.worldUtil.getSpawn(world), TeleportCause.COMMAND_AREA_CREATE); 422 player.sendMessage(TranslatableCaption.of("setup.setup_finished")); 423 if (area.getTerrain() != PlotAreaTerrainType.ALL) { 424 QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(world)); 425 queue.setChunkConsumer(chunk -> AugmentedUtils.generateChunk( 426 world, 427 chunk.getX(), 428 chunk.getZ(), 429 null 430 )); 431 queue.addReadChunks(region.getChunks()); 432 queue.enqueue(); 433 } 434 } else { 435 player.sendMessage( 436 TranslatableCaption.of("errors.error_create"), 437 TagResolver.resolver("world", Tag.inserting(Component.text(area.getWorldName()))) 438 ); 439 } 440 }; 441 if (hasConfirmation(player)) { 442 CmdConfirm.addPending(player, getCommandString() + " create pos2 (Creates world)", run); 443 } else { 444 run.run(); 445 } 446 return true; 447 } 448 } 449 default: // Start creation 450 String[] split = args[1].split(":"); 451 String id; 452 if (split.length == 2) { 453 id = split[1]; 454 } else { 455 id = null; 456 } 457 PlotAreaBuilder builder = PlotAreaBuilder.newBuilder(); 458 builder.worldName(split[0]); 459 final HybridPlotWorld pa = 460 this.hybridPlotWorldFactory.create( 461 builder.worldName(), 462 id, 463 PlotSquared.platform().defaultGenerator(), 464 null, 465 null 466 ); 467 PlotArea other = this.plotAreaManager.getPlotArea(pa.getWorldName(), id); 468 if (other != null && Objects.equals(pa.getId(), other.getId())) { 469 player.sendMessage( 470 TranslatableCaption.of("setup.setup_world_taken"), 471 TagResolver.resolver("value", Tag.inserting(Component.text(pa.getId()))) 472 ); 473 return false; 474 } 475 Set<PlotArea> areas = this.plotAreaManager.getPlotAreasSet(pa.getWorldName()); 476 if (!areas.isEmpty()) { 477 PlotArea area = areas.iterator().next(); 478 pa.setType(area.getType()); 479 } 480 pa.SIZE = (short) (pa.PLOT_WIDTH + pa.ROAD_WIDTH); 481 for (int i = 2; i < args.length; i++) { 482 String[] pair = args[i].split("="); 483 if (pair.length != 2) { 484 player.sendMessage( 485 TranslatableCaption.of("commandconfig.command_syntax_extended"), 486 TagResolver.builder() 487 .tag("value1", Tag.inserting(Component.text(getCommandString()))) 488 .tag( 489 "value2", 490 Tag.inserting(Component.text("create [world[:id]] [<modifier>=<value>]...")) 491 ) 492 .build() 493 ); 494 return false; 495 } 496 switch (pair[0].toLowerCase()) { 497 case "s", "size" -> { 498 pa.PLOT_WIDTH = Integer.parseInt(pair[1]); 499 pa.SIZE = (short) (pa.PLOT_WIDTH + pa.ROAD_WIDTH); 500 } 501 case "g", "gap" -> { 502 pa.ROAD_WIDTH = Integer.parseInt(pair[1]); 503 pa.SIZE = (short) (pa.PLOT_WIDTH + pa.ROAD_WIDTH); 504 } 505 case "h", "height" -> { 506 int value = Integer.parseInt(pair[1]); 507 pa.PLOT_HEIGHT = value; 508 pa.ROAD_HEIGHT = value; 509 pa.WALL_HEIGHT = value; 510 } 511 case "f", "floor" -> pa.TOP_BLOCK = ConfigurationUtil.BLOCK_BUCKET.parseString(pair[1]); 512 case "m", "main" -> pa.MAIN_BLOCK = ConfigurationUtil.BLOCK_BUCKET.parseString(pair[1]); 513 case "w", "wall" -> pa.WALL_FILLING = ConfigurationUtil.BLOCK_BUCKET.parseString(pair[1]); 514 case "b", "border" -> pa.WALL_BLOCK = ConfigurationUtil.BLOCK_BUCKET.parseString(pair[1]); 515 case "terrain" -> { 516 pa.setTerrain(PlotAreaTerrainType.fromString(pair[1]) 517 .orElseThrow(() -> new IllegalArgumentException(pair[1] + " is not a valid terrain."))); 518 builder.terrainType(pa.getTerrain()); 519 } 520 case "type" -> { 521 pa.setType(PlotAreaType.fromString(pair[1]) 522 .orElseThrow(() -> new IllegalArgumentException(pair[1] + " is not a valid type."))); 523 builder.plotAreaType(pa.getType()); 524 } 525 default -> { 526 player.sendMessage( 527 TranslatableCaption.of("commandconfig.command_syntax_extended"), 528 TagResolver.builder() 529 .tag("value1", Tag.inserting(Component.text(getCommandString()))) 530 .tag( 531 "value2", 532 Tag.inserting(Component.text( 533 " create [world[:id]] [<modifier>=<value>]...")) 534 ) 535 .build() 536 ); 537 return false; 538 } 539 } 540 } 541 if (pa.getType() != PlotAreaType.PARTIAL) { 542 if (this.worldUtil.isWorld(pa.getWorldName())) { 543 player.sendMessage( 544 TranslatableCaption.of("setup.setup_world_taken"), 545 TagResolver.resolver("value", Tag.inserting(Component.text(pa.getWorldName()))) 546 ); 547 return false; 548 } 549 Runnable run = () -> { 550 String path = "worlds." + pa.getWorldName(); 551 if (!this.worldConfiguration.contains(path)) { 552 this.worldConfiguration.createSection(path); 553 } 554 ConfigurationSection section = this.worldConfiguration.getConfigurationSection(path); 555 pa.saveConfiguration(section); 556 pa.loadConfiguration(section); 557 builder.plotManager(PlotSquared.platform().pluginName()); 558 builder.generatorName(PlotSquared.platform().pluginName()); 559 String world = this.setupUtils.setupWorld(builder); 560 if (this.worldUtil.isWorld(world)) { 561 player.teleport(this.worldUtil.getSpawn(world), TeleportCause.COMMAND_AREA_CREATE); 562 player.sendMessage(TranslatableCaption.of("setup.setup_finished")); 563 } else { 564 player.sendMessage( 565 TranslatableCaption.of("errors.error_create"), 566 TagResolver.resolver("world", Tag.inserting(Component.text(pa.getWorldName()))) 567 ); 568 } 569 try { 570 this.worldConfiguration.save(this.worldFile); 571 } catch (IOException e) { 572 e.printStackTrace(); 573 } 574 }; 575 if (hasConfirmation(player)) { 576 CmdConfirm.addPending(player, getCommandString() + ' ' + StringMan.join(args, " "), run); 577 } else { 578 run.run(); 579 } 580 return true; 581 } 582 if (pa.getId() == null) { 583 player.sendMessage( 584 TranslatableCaption.of("commandconfig.command_syntax"), 585 TagResolver.resolver("value", Tag.inserting(Component.text(getUsage()))) 586 ); 587 player.sendMessage( 588 TranslatableCaption.of("commandconfig.command_syntax_extended"), 589 TagResolver.builder() 590 .tag("value1", Tag.inserting(Component.text(getCommandString()))) 591 .tag( 592 "value2", 593 Tag.inserting(Component.text( 594 " create [world[:id]] [<modifier>=<value>]...")) 595 ) 596 .build() 597 ); 598 return false; 599 } 600 if (this.worldUtil.isWorld(pa.getWorldName())) { 601 if (!player.getLocation().getWorldName().equals(pa.getWorldName())) { 602 player.teleport(this.worldUtil.getSpawn(pa.getWorldName()), TeleportCause.COMMAND_AREA_CREATE); 603 } 604 } else { 605 builder.terrainType(PlotAreaTerrainType.NONE); 606 builder.plotAreaType(PlotAreaType.NORMAL); 607 this.setupUtils.setupWorld(builder); 608 player.teleport(this.worldUtil.getSpawn(pa.getWorldName()), TeleportCause.COMMAND_AREA_CREATE); 609 } 610 metaData.computeIfAbsent(player.getUUID(), missingUUID -> new HashMap<>()).put("area_create_area", pa); 611 player.sendMessage( 612 TranslatableCaption.of("single.get_position"), 613 TagResolver.resolver("command", Tag.inserting(Component.text(getCommandString()))) 614 ); 615 break; 616 } 617 return true; 618 } 619 case "i", "info" -> { 620 if (!player.hasPermission(Permission.PERMISSION_AREA_INFO)) { 621 player.sendMessage( 622 TranslatableCaption.of("permission.no_permission"), 623 TagResolver.resolver( 624 "node", 625 Tag.inserting(Permission.PERMISSION_AREA_INFO) 626 ) 627 ); 628 return false; 629 } 630 PlotArea area; 631 switch (args.length) { 632 case 1 -> area = player.getApplicablePlotArea(); 633 case 2 -> area = this.plotAreaManager.getPlotAreaByString(args[1]); 634 default -> { 635 player.sendMessage( 636 TranslatableCaption.of("commandconfig.command_syntax_extended"), 637 TagResolver.builder() 638 .tag("value1", Tag.inserting(Component.text(getCommandString()))) 639 .tag("value2", Tag.inserting(Component.text(" info [area]"))) 640 .build() 641 ); 642 return false; 643 } 644 } 645 if (area == null) { 646 if (args.length == 2) { 647 player.sendMessage( 648 TranslatableCaption.of("errors.not_valid_plot_world"), 649 TagResolver.resolver("value", Tag.inserting(Component.text(args[1]))) 650 ); 651 } else { 652 player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world")); 653 } 654 return false; 655 } 656 String name; 657 double percent; 658 int claimed = area.getPlotCount(); 659 int clusters = area.getClusters().size(); 660 String region; 661 String generator = String.valueOf(area.getGenerator()); 662 if (area.getType() == PlotAreaType.PARTIAL) { 663 PlotId min = area.getMin(); 664 PlotId max = area.getMax(); 665 name = area.getWorldName() + ';' + area.getId() + ';' + min + ';' + max; 666 int size = (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1); 667 percent = claimed == 0 ? 0 : size / (double) claimed; 668 region = area.getRegion().toString(); 669 } else { 670 name = area.getWorldName(); 671 percent = claimed == 0 ? 0 : 100d * claimed / Integer.MAX_VALUE; 672 region = "N/A"; 673 } 674 TagResolver resolver = TagResolver.builder() 675 .tag( 676 "header", 677 Tag.inserting(TranslatableCaption.of("info.plot_info_header").toComponent(player)) 678 ) 679 .tag("name", Tag.inserting(Component.text(name))) 680 .tag("type", Tag.inserting(Component.text(area.getType().name()))) 681 .tag("terrain", Tag.inserting(Component.text(area.getTerrain().name()))) 682 .tag("usage", Tag.inserting(Component.text(String.format("%.2f", percent)))) 683 .tag("claimed", Tag.inserting(Component.text(claimed))) 684 .tag("clusters", Tag.inserting(Component.text(clusters))) 685 .tag("region", Tag.inserting(Component.text(region))) 686 .tag("generator", Tag.inserting(Component.text(generator))) 687 .tag( 688 "footer", 689 Tag.inserting(TranslatableCaption.of("info.plot_info_footer").toComponent(player)) 690 ) 691 .build(); 692 player.sendMessage(TranslatableCaption.of("info.area_info_format"), resolver); 693 return true; 694 } 695 case "l", "list" -> { 696 if (!player.hasPermission(Permission.PERMISSION_AREA_LIST)) { 697 player.sendMessage( 698 TranslatableCaption.of("permission.no_permission"), 699 TagResolver.resolver( 700 "node", 701 Tag.inserting(Permission.PERMISSION_AREA_LIST) 702 ) 703 ); 704 return false; 705 } 706 int page; 707 switch (args.length) { 708 case 1: 709 page = 0; 710 break; 711 case 2: 712 if (MathMan.isInteger(args[1])) { 713 page = Integer.parseInt(args[1]) - 1; 714 break; 715 } 716 default: 717 player.sendMessage( 718 TranslatableCaption.of("commandconfig.command_syntax_extended"), 719 TagResolver.builder() 720 .tag("value1", Tag.inserting(Component.text(getCommandString()))) 721 .tag("value2", Tag.inserting(Component.text(" list [#]"))) 722 .build() 723 ); 724 return false; 725 } 726 final List<PlotArea> areas = new ArrayList<>(Arrays.asList(this.plotAreaManager.getAllPlotAreas())); 727 paginate(player, areas, 8, page, new RunnableVal3<Integer, PlotArea, CaptionHolder>() { 728 @Override 729 public void run(Integer i, PlotArea area, CaptionHolder caption) { 730 String name; 731 double percent; 732 int claimed = area.getPlotCount(); 733 int clusters = area.getClusters().size(); 734 String region; 735 String generator = String.valueOf(area.getGenerator()); 736 if (area.getType() == PlotAreaType.PARTIAL) { 737 PlotId min = area.getMin(); 738 PlotId max = area.getMax(); 739 name = area.getWorldName() + ';' + area.getId() + ';' + min + ';' + max; 740 int size = (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1); 741 percent = claimed == 0 ? 0 : claimed / (double) size; 742 region = area.getRegion().toString(); 743 } else { 744 name = area.getWorldName(); 745 percent = claimed == 0 ? 0 : (double) claimed / Short.MAX_VALUE * Short.MAX_VALUE; 746 region = "N/A"; 747 } 748 Component tooltip = MINI_MESSAGE.deserialize( 749 TranslatableCaption.of("info.area_list_tooltip").getComponent(player), 750 TagResolver.builder() 751 .tag("claimed", Tag.inserting(Component.text(claimed))) 752 .tag("usage", Tag.inserting(Component.text(String.format("%.2f", percent) + "%"))) 753 .tag("clusters", Tag.inserting(Component.text(clusters))) 754 .tag("region", Tag.inserting(Component.text(region))) 755 .tag("generator", Tag.inserting(Component.text(generator))) 756 .build() 757 ); 758 TagResolver resolver = TagResolver.builder() 759 .tag("hover_info", Tag.inserting(tooltip)) 760 .tag("command_tp", Tag.preProcessParsed("/plot area tp " + name)) 761 .tag("command_info", Tag.preProcessParsed("/plot area info " + name)) 762 .tag("number", Tag.inserting(Component.text(i))) 763 .tag("area_name", Tag.inserting(Component.text(name))) 764 .tag("area_type", Tag.inserting(Component.text(area.getType().name()))) 765 .tag("area_terrain", Tag.inserting(Component.text(area.getTerrain().name()))) 766 .build(); 767 caption.set(TranslatableCaption.of("info.area_list_item")); 768 caption.setTagResolvers(resolver); 769 } 770 }, "/plot area list", TranslatableCaption.of("list.area_list_header_paged")); 771 return true; 772 } 773 case "regen", "clear", "reset", "regenerate" -> { 774 if (!player.hasPermission(Permission.PERMISSION_AREA_REGEN)) { 775 player.sendMessage( 776 TranslatableCaption.of("permission.no_permission"), 777 TagResolver.resolver( 778 "node", 779 Tag.inserting(Permission.PERMISSION_AREA_REGEN) 780 ) 781 ); 782 return false; 783 } 784 final PlotArea area = player.getApplicablePlotArea(); 785 if (area == null) { 786 player.sendMessage(TranslatableCaption.of("errors.not_in_plot_world")); 787 return false; 788 } 789 if (area.getType() != PlotAreaType.PARTIAL) { 790 player.sendMessage( 791 TranslatableCaption.of("single.delete_world_region"), 792 TagResolver.resolver("world", Tag.inserting(Component.text(area.getWorldName()))) 793 ); 794 return false; 795 } 796 QueueCoordinator queue = blockQueue.getNewQueue(worldUtil.getWeWorld(area.getWorldName())); 797 queue.setChunkConsumer(chunk -> AugmentedUtils.generateChunk( 798 area.getWorldName(), 799 chunk.getX(), 800 chunk.getZ(), 801 null 802 )); 803 queue.addReadChunks(area.getRegion().getChunks()); 804 queue.setCompleteTask(() -> player.sendMessage(TranslatableCaption.of("single.regeneration_complete"))); 805 queue.enqueue(); 806 return true; 807 } 808 case "goto", "v", "teleport", "visit", "tp" -> { 809 if (!player.hasPermission(Permission.PERMISSION_AREA_TP)) { 810 player.sendMessage( 811 TranslatableCaption.of("permission.no_permission"), 812 TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_AREA_TP)) 813 ); 814 return false; 815 } 816 if (args.length != 2) { 817 player.sendMessage( 818 TranslatableCaption.of("commandconfig.command_syntax"), 819 TagResolver.resolver("value", Tag.inserting(Component.text("/plot area tp [area]"))) 820 ); 821 return false; 822 } 823 PlotArea area = this.plotAreaManager.getPlotAreaByString(args[1]); 824 if (area == null) { 825 player.sendMessage( 826 TranslatableCaption.of("errors.not_valid_plot_world"), 827 TagResolver.resolver("value", Tag.inserting(Component.text(args[1]))) 828 ); 829 return false; 830 } 831 Location center; 832 if (area instanceof SinglePlotArea) { 833 ((SinglePlotArea) area).loadWorld(PlotId.of(0, 0)); 834 center = this.worldUtil.getSpawn(PlotId.of(0, 0).toUnderscoreSeparatedString()); 835 player.teleport(center, TeleportCause.COMMAND_AREA_TELEPORT); 836 } else if (area.getType() != PlotAreaType.PARTIAL) { 837 center = this.worldUtil.getSpawn(area.getWorldName()); 838 player.teleport(center, TeleportCause.COMMAND_AREA_TELEPORT); 839 } else { 840 CuboidRegion region = area.getRegion(); 841 center = Location.at(area.getWorldName(), 842 region.getMinimumPoint().getX() + (region.getMaximumPoint().getX() - region 843 .getMinimumPoint() 844 .getX()) / 2, 0, 845 region.getMinimumPoint().getZ() + (region.getMaximumPoint().getZ() - region 846 .getMinimumPoint() 847 .getZ()) / 2 848 ); 849 this.worldUtil.getHighestBlock(area.getWorldName(), center.getX(), center.getZ(), 850 y -> player.teleport(center.withY(1 + y), TeleportCause.COMMAND_AREA_TELEPORT) 851 ); 852 } 853 return true; 854 } 855 case "delete", "remove" -> { 856 player.sendMessage(TranslatableCaption.of("single.worldcreation_location")); 857 return true; 858 } 859 } 860 sendUsage(player); 861 return false; 862 } 863 864 @Override 865 public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) { 866 if (args.length == 1) { 867 final List<String> completions = new LinkedList<>(); 868 if (player.hasPermission(Permission.PERMISSION_AREA_CREATE)) { 869 completions.add("create"); 870 } 871 if (player.hasPermission(Permission.PERMISSION_AREA_CREATE)) { 872 completions.add("single"); 873 } 874 if (player.hasPermission(Permission.PERMISSION_AREA_LIST)) { 875 completions.add("list"); 876 } 877 if (player.hasPermission(Permission.PERMISSION_AREA_INFO)) { 878 completions.add("info"); 879 } 880 if (player.hasPermission(Permission.PERMISSION_AREA_TP)) { 881 completions.add("tp"); 882 } 883 final List<Command> commands = completions.stream().filter(completion -> completion 884 .toLowerCase() 885 .startsWith(args[0].toLowerCase())) 886 .map(completion -> new Command( 887 null, 888 true, 889 completion, 890 "", 891 RequiredType.NONE, 892 CommandCategory.ADMINISTRATION 893 ) { 894 }).collect(Collectors.toCollection(LinkedList::new)); 895 if (player.hasPermission(Permission.PERMISSION_AREA) && args[0].length() > 0) { 896 commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList())); 897 } 898 return commands; 899 } 900 return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList()); 901 } 902 903}