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