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