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.Settings; 024import com.plotsquared.core.configuration.caption.CaptionUtility; 025import com.plotsquared.core.configuration.caption.StaticCaption; 026import com.plotsquared.core.configuration.caption.TranslatableCaption; 027import com.plotsquared.core.events.PlotFlagAddEvent; 028import com.plotsquared.core.events.PlotFlagRemoveEvent; 029import com.plotsquared.core.events.Result; 030import com.plotsquared.core.location.Location; 031import com.plotsquared.core.permissions.Permission; 032import com.plotsquared.core.player.PlotPlayer; 033import com.plotsquared.core.plot.Plot; 034import com.plotsquared.core.plot.flag.FlagParseException; 035import com.plotsquared.core.plot.flag.GlobalFlagContainer; 036import com.plotsquared.core.plot.flag.InternalFlag; 037import com.plotsquared.core.plot.flag.PlotFlag; 038import com.plotsquared.core.plot.flag.types.IntegerFlag; 039import com.plotsquared.core.plot.flag.types.ListFlag; 040import com.plotsquared.core.util.EventDispatcher; 041import com.plotsquared.core.util.MathMan; 042import com.plotsquared.core.util.StringComparison; 043import com.plotsquared.core.util.StringMan; 044import com.plotsquared.core.util.helpmenu.HelpMenu; 045import com.plotsquared.core.util.task.RunnableVal2; 046import com.plotsquared.core.util.task.RunnableVal3; 047import net.kyori.adventure.text.Component; 048import net.kyori.adventure.text.TextComponent; 049import net.kyori.adventure.text.format.Style; 050import net.kyori.adventure.text.minimessage.tag.Tag; 051import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 052import org.checkerframework.checker.nullness.qual.NonNull; 053import org.checkerframework.checker.nullness.qual.Nullable; 054 055import java.util.ArrayList; 056import java.util.Arrays; 057import java.util.Collection; 058import java.util.Collections; 059import java.util.HashMap; 060import java.util.Iterator; 061import java.util.List; 062import java.util.Locale; 063import java.util.Map; 064import java.util.concurrent.CompletableFuture; 065import java.util.stream.Collectors; 066import java.util.stream.Stream; 067 068@CommandDeclaration(command = "flag", 069 aliases = {"f", "flag"}, 070 usage = "/plot flag <set | remove | add | list | info> <flag> <value>", 071 category = CommandCategory.SETTINGS, 072 requiredType = RequiredType.NONE, 073 permission = "plots.flag") 074@SuppressWarnings("unused") 075public final class FlagCommand extends Command { 076 077 private final EventDispatcher eventDispatcher; 078 079 @Inject 080 public FlagCommand(final @NonNull EventDispatcher eventDispatcher) { 081 super(MainCommand.getInstance(), true); 082 this.eventDispatcher = eventDispatcher; 083 } 084 085 private static boolean sendMessage(PlotPlayer<?> player) { 086 player.sendMessage( 087 TranslatableCaption.of("commandconfig.command_syntax"), 088 TagResolver.resolver( 089 "value", 090 Tag.inserting(Component.text("/plot flag <set | remove | add | list | info> <flag> <value>")) 091 ) 092 ); 093 return true; 094 } 095 096 private static boolean checkPermValue( 097 final @NonNull PlotPlayer<?> player, 098 final @NonNull PlotFlag<?, ?> flag, @NonNull String key, @NonNull String value 099 ) { 100 key = key.toLowerCase(); 101 value = value.toLowerCase(); 102 String perm = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format(key.toLowerCase(), value.toLowerCase()); 103 if (flag instanceof IntegerFlag && MathMan.isInteger(value)) { 104 try { 105 int numeric = Integer.parseInt(value); 106 perm = perm.substring(0, perm.length() - value.length() - 1); 107 boolean result = false; 108 if (numeric > 0) { 109 int checkRange = PlotSquared.get().getPlatform().equalsIgnoreCase("bukkit") ? 110 numeric : 111 Settings.Limit.MAX_PLOTS; 112 result = player.hasPermissionRange(perm, checkRange) >= numeric; 113 } 114 if (!result) { 115 player.sendMessage( 116 TranslatableCaption.of("permission.no_permission"), 117 TagResolver.resolver( 118 "node", 119 Tag.inserting(Component.text(perm + "." + numeric)) 120 ) 121 ); 122 } 123 return result; 124 } catch (NumberFormatException ignore) { 125 } 126 } else if (flag instanceof final ListFlag<?, ?> listFlag) { 127 try { 128 PlotFlag<? extends List<?>, ?> parsedFlag = listFlag.parse(value); 129 for (final Object entry : parsedFlag.getValue()) { 130 final String permission = Permission.PERMISSION_SET_FLAG_KEY_VALUE.format( 131 key.toLowerCase(), 132 entry.toString().toLowerCase() 133 ); 134 final boolean result = player.hasPermission(permission); 135 if (!result) { 136 player.sendMessage( 137 TranslatableCaption.of("permission.no_permission"), 138 TagResolver.resolver("node", Tag.inserting(Component.text(permission))) 139 ); 140 return false; 141 } 142 } 143 } catch (final FlagParseException e) { 144 player.sendMessage( 145 TranslatableCaption.of("flag.flag_parse_error"), 146 TagResolver.builder() 147 .tag("flag_name", Tag.inserting(Component.text(flag.getName()))) 148 .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) 149 .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) 150 .build() 151 ); 152 return false; 153 } catch (final Exception e) { 154 return false; 155 } 156 return true; 157 } 158 boolean result; 159 String basePerm = Permission.PERMISSION_SET_FLAG_KEY.format(key.toLowerCase()); 160 if (flag.isValuedPermission()) { 161 result = player.hasKeyedPermission(basePerm, value); 162 } else { 163 result = player.hasPermission(basePerm); 164 perm = basePerm; 165 } 166 if (!result) { 167 player.sendMessage( 168 TranslatableCaption.of("permission.no_permission"), 169 TagResolver.resolver("node", Tag.inserting(Component.text(perm))) 170 ); 171 } 172 return result; 173 } 174 175 /** 176 * Checks if the player is allowed to modify the flags at their current location 177 * 178 * @return {@code true} if the player is allowed to modify the flags at their current location 179 */ 180 private static boolean checkRequirements(final @NonNull PlotPlayer<?> player) { 181 final Location location = player.getLocation(); 182 final Plot plot = location.getPlotAbs(); 183 if (plot == null) { 184 player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); 185 return false; 186 } 187 if (!plot.hasOwner()) { 188 player.sendMessage(TranslatableCaption.of("working.plot_not_claimed")); 189 return false; 190 } 191 if (!plot.isOwner(player.getUUID()) && !player.hasPermission(Permission.PERMISSION_SET_FLAG_OTHER)) { 192 player.sendMessage( 193 TranslatableCaption.of("permission.no_permission"), 194 TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_SET_FLAG_OTHER)) 195 ); 196 return false; 197 } 198 return true; 199 } 200 201 /** 202 * Attempt to extract the plot flag from the command arguments. If the flag cannot 203 * be found, a flag suggestion may be sent to the player. 204 * 205 * @param player Player executing the command 206 * @param arg String to extract flag from 207 * @return The flag, if found, else null 208 */ 209 @Nullable 210 private static PlotFlag<?, ?> getFlag( 211 final @NonNull PlotPlayer<?> player, 212 final @NonNull String arg 213 ) { 214 if (arg.length() > 0) { 215 final PlotFlag<?, ?> flag = GlobalFlagContainer.getInstance().getFlagFromString(arg); 216 if (flag instanceof InternalFlag || flag == null) { 217 boolean suggested = false; 218 try { 219 final StringComparison<PlotFlag<?, ?>> stringComparison = 220 new StringComparison<>( 221 arg, 222 GlobalFlagContainer.getInstance().getFlagMap().values(), 223 PlotFlag::getName 224 ); 225 final String best = stringComparison.getBestMatch(); 226 if (best != null) { 227 player.sendMessage( 228 TranslatableCaption.of("flag.not_valid_flag_suggested"), 229 TagResolver.resolver("value", Tag.inserting(Component.text(best))) 230 ); 231 suggested = true; 232 } 233 } catch (final Exception ignored) { /* Happens sometimes because of mean code */ } 234 if (!suggested) { 235 player.sendMessage(TranslatableCaption.of("flag.not_valid_flag")); 236 } 237 return null; 238 } 239 return flag; 240 } 241 return null; 242 } 243 244 @Override 245 public CompletableFuture<Boolean> execute( 246 PlotPlayer<?> player, String[] args, 247 RunnableVal3<Command, Runnable, Runnable> confirm, 248 RunnableVal2<Command, CommandResult> whenDone 249 ) throws CommandException { 250 if (args.length == 0 || !Arrays 251 .asList("set", "s", "list", "l", "delete", "remove", "r", "add", "a", "info", "i") 252 .contains(args[0].toLowerCase(Locale.ENGLISH))) { 253 new HelpMenu(player).setCategory(CommandCategory.SETTINGS) 254 .setCommands(this.getCommands()).generateMaxPages() 255 .generatePage(0, getParent().toString(), player).render(); 256 return CompletableFuture.completedFuture(true); 257 } 258 return super.execute(player, args, confirm, whenDone); 259 } 260 261 @Override 262 public Collection<Command> tab( 263 final PlotPlayer<?> player, final String[] args, 264 final boolean space 265 ) { 266 if (args.length == 1) { 267 return Stream 268 .of("set", "add", "remove", "delete", "info", "list") 269 .filter(value -> value.startsWith(args[0].toLowerCase(Locale.ENGLISH))) 270 .map(value -> new Command(null, false, value, "", RequiredType.NONE, null) { 271 }).collect(Collectors.toList()); 272 } else if (Arrays.asList("set", "add", "remove", "delete", "info") 273 .contains(args[0].toLowerCase(Locale.ENGLISH)) && args.length == 2) { 274 return GlobalFlagContainer.getInstance().getRecognizedPlotFlags().stream() 275 .filter(flag -> !(flag instanceof InternalFlag)) 276 .filter(flag -> flag.getName().startsWith(args[1].toLowerCase(Locale.ENGLISH))) 277 .map(flag -> new Command(null, false, flag.getName(), "", RequiredType.NONE, null) { 278 }).collect(Collectors.toList()); 279 } else if (Arrays.asList("set", "add", "remove", "delete") 280 .contains(args[0].toLowerCase(Locale.ENGLISH)) && args.length == 3) { 281 try { 282 final PlotFlag<?, ?> flag = 283 GlobalFlagContainer.getInstance().getFlagFromString(args[1]); 284 if (flag != null) { 285 Stream<String> stream = flag.getTabCompletions().stream(); 286 if (flag instanceof ListFlag && args[2].contains(",")) { 287 final String[] split = args[2].split(","); 288 // Prefix earlier values onto all suggestions 289 StringBuilder prefix = new StringBuilder(); 290 for (int i = 0; i < split.length - 1; i++) { 291 prefix.append(split[i]).append(","); 292 } 293 final String cmp; 294 if (!args[2].endsWith(",")) { 295 cmp = split[split.length - 1]; 296 } else { 297 prefix.append(split[split.length - 1]).append(","); 298 cmp = ""; 299 } 300 return stream 301 .filter(value -> value.startsWith(cmp.toLowerCase(Locale.ENGLISH))).map( 302 value -> new Command(null, false, prefix + value, "", 303 RequiredType.NONE, null 304 ) { 305 }).collect(Collectors.toList()); 306 } else { 307 return stream 308 .filter(value -> value.startsWith(args[2].toLowerCase(Locale.ENGLISH))) 309 .map(value -> new Command(null, false, value, "", RequiredType.NONE, 310 null 311 ) { 312 }).collect(Collectors.toList()); 313 } 314 } 315 } catch (final Exception ignored) { 316 } 317 } 318 return tabOf(player, args, space); 319 } 320 321 @CommandDeclaration(command = "set", 322 aliases = {"s", "set"}, 323 usage = "/plot flag set <flag> <value>", 324 category = CommandCategory.SETTINGS, 325 requiredType = RequiredType.NONE, 326 permission = "plots.set.flag") 327 public void set( 328 final Command command, final PlotPlayer<?> player, final String[] args, 329 final RunnableVal3<Command, Runnable, Runnable> confirm, 330 final RunnableVal2<Command, CommandResult> whenDone 331 ) { 332 if (!checkRequirements(player)) { 333 return; 334 } 335 if (args.length < 2) { 336 player.sendMessage( 337 TranslatableCaption.of("commandconfig.command_syntax"), 338 TagResolver.resolver("value", Tag.inserting(Component.text("/plot flag set <flag> <value>"))) 339 ); 340 return; 341 } 342 final PlotFlag<?, ?> plotFlag = getFlag(player, args[0]); 343 if (plotFlag == null) { 344 return; 345 } 346 Plot plot = player.getLocation().getPlotAbs(); 347 PlotFlagAddEvent event = eventDispatcher.callFlagAdd(plotFlag, plot); 348 if (event.getEventResult() == Result.DENY) { 349 player.sendMessage( 350 TranslatableCaption.of("events.event_denied"), 351 TagResolver.resolver("value", Tag.inserting(Component.text("Flag set"))) 352 ); 353 return; 354 } 355 boolean force = event.getEventResult() == Result.FORCE; 356 String value = StringMan.join(Arrays.copyOfRange(args, 1, args.length), " "); 357 if (!force && !checkPermValue(player, plotFlag, args[0], value)) { 358 return; 359 } 360 value = CaptionUtility.stripClickEvents(plotFlag, value); 361 final PlotFlag<?, ?> parsed; 362 try { 363 parsed = plotFlag.parse(value); 364 } catch (final FlagParseException e) { 365 player.sendMessage( 366 TranslatableCaption.of("flag.flag_parse_error"), 367 TagResolver.builder() 368 .tag("flag_name", Tag.inserting(Component.text(plotFlag.getName()))) 369 .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) 370 .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) 371 .build() 372 ); 373 return; 374 } 375 plot.setFlag(parsed); 376 player.sendMessage( 377 TranslatableCaption.of("flag.flag_added"), 378 TagResolver.builder() 379 .tag("flag", Tag.inserting(Component.text(args[0]))) 380 .tag("value", Tag.inserting(Component.text(parsed.toString()))) 381 .build() 382 ); 383 } 384 385 @SuppressWarnings({"unchecked", "rawtypes"}) 386 @CommandDeclaration(command = "add", 387 aliases = {"a", "add"}, 388 usage = "/plot flag add <flag> <value>", 389 category = CommandCategory.SETTINGS, 390 requiredType = RequiredType.NONE, 391 permission = "plots.flag.add") 392 public void add( 393 final Command command, PlotPlayer<?> player, final String[] args, 394 final RunnableVal3<Command, Runnable, Runnable> confirm, 395 final RunnableVal2<Command, CommandResult> whenDone 396 ) { 397 if (!checkRequirements(player)) { 398 return; 399 } 400 if (args.length < 2) { 401 player.sendMessage( 402 TranslatableCaption.of("commandconfig.command_syntax"), 403 TagResolver.resolver("value", Tag.inserting(Component.text("/plot flag add <flag> <values>"))) 404 ); 405 return; 406 } 407 final PlotFlag<?, ?> plotFlag = getFlag(player, args[0]); 408 if (plotFlag == null) { 409 return; 410 } 411 Plot plot = player.getLocation().getPlotAbs(); 412 PlotFlagAddEvent event = eventDispatcher.callFlagAdd(plotFlag, plot); 413 if (event.getEventResult() == Result.DENY) { 414 player.sendMessage( 415 TranslatableCaption.of("events.event_denied"), 416 TagResolver.resolver("value", Tag.inserting(Component.text("Flag add"))) 417 ); 418 return; 419 } 420 boolean force = event.getEventResult() == Result.FORCE; 421 final PlotFlag localFlag = player.getLocation().getPlotAbs().getFlagContainer() 422 .getFlag(event.getFlag().getClass()); 423 if (!force) { 424 for (String entry : args[1].split(",")) { 425 if (!checkPermValue(player, event.getFlag(), args[0], entry)) { 426 return; 427 } 428 } 429 } 430 final String value = StringMan.join(Arrays.copyOfRange(args, 1, args.length), " "); 431 final PlotFlag parsed; 432 try { 433 parsed = event.getFlag().parse(value); 434 } catch (FlagParseException e) { 435 player.sendMessage( 436 TranslatableCaption.of("flag.flag_parse_error"), 437 TagResolver.builder() 438 .tag("flag_name", Tag.inserting(Component.text(plotFlag.getName()))) 439 .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) 440 .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) 441 .build() 442 ); 443 return; 444 } 445 boolean result = 446 player.getLocation().getPlotAbs().setFlag(localFlag.merge(parsed.getValue())); 447 if (!result) { 448 player.sendMessage(TranslatableCaption.of("flag.flag_not_added")); 449 return; 450 } 451 player.sendMessage( 452 TranslatableCaption.of("flag.flag_added"), 453 TagResolver.builder() 454 .tag("flag", Tag.inserting(Component.text(args[0]))) 455 .tag("value", Tag.inserting(Component.text(parsed.toString()))) 456 .build() 457 ); 458 } 459 460 @SuppressWarnings({"unchecked", "rawtypes"}) 461 @CommandDeclaration(command = "remove", 462 aliases = {"r", "remove", "delete"}, 463 usage = "/plot flag remove <flag> [values]", 464 category = CommandCategory.SETTINGS, 465 requiredType = RequiredType.NONE, 466 permission = "plots.flag.remove") 467 public void remove( 468 final Command command, PlotPlayer<?> player, final String[] args, 469 final RunnableVal3<Command, Runnable, Runnable> confirm, 470 final RunnableVal2<Command, CommandResult> whenDone 471 ) { 472 if (!checkRequirements(player)) { 473 return; 474 } 475 if (args.length != 1 && args.length != 2) { 476 player.sendMessage( 477 TranslatableCaption.of("commandconfig.command_syntax"), 478 TagResolver.resolver("value", Tag.inserting(Component.text("/plot flag remove <flag> [values]"))) 479 ); 480 return; 481 } 482 PlotFlag<?, ?> flag = getFlag(player, args[0]); 483 if (flag == null) { 484 return; 485 } 486 final Plot plot = player.getLocation().getPlotAbs(); 487 final PlotFlag<?, ?> flagWithOldValue = plot.getFlagContainer().getFlag(flag.getClass()); 488 PlotFlagRemoveEvent event = eventDispatcher.callFlagRemove(flag, plot); 489 if (event.getEventResult() == Result.DENY) { 490 player.sendMessage( 491 TranslatableCaption.of("events.event_denied"), 492 TagResolver.resolver("value", Tag.inserting(Component.text("Flag remove"))) 493 ); 494 return; 495 } 496 boolean force = event.getEventResult() == Result.FORCE; 497 flag = event.getFlag(); 498 if (!force && !player.hasPermission(Permission.PERMISSION_SET_FLAG_KEY.format(args[0].toLowerCase()))) { 499 if (args.length != 2) { 500 player.sendMessage( 501 TranslatableCaption.of("permission.no_permission"), 502 TagResolver.resolver( 503 "node", 504 Tag.inserting(Component.text(Permission.PERMISSION_SET_FLAG_KEY.format(args[0].toLowerCase()))) 505 ) 506 ); 507 return; 508 } 509 } 510 if (args.length == 2 && flag instanceof final ListFlag<?, ?> listFlag) { 511 String value = StringMan.join(Arrays.copyOfRange(args, 1, args.length), " "); 512 final List<?> list = 513 new ArrayList<>(plot.getFlag((Class<? extends ListFlag<?, ?>>) listFlag.getClass())); 514 final PlotFlag parsedFlag; 515 try { 516 parsedFlag = listFlag.parse(value); 517 } catch (final FlagParseException e) { 518 player.sendMessage( 519 TranslatableCaption.of("flag.flag_parse_error"), 520 TagResolver.builder() 521 .tag("flag_name", Tag.inserting(Component.text(flag.getName()))) 522 .tag("flag_value", Tag.inserting(Component.text(e.getValue()))) 523 .tag("error", Tag.inserting(e.getErrorMessage().toComponent(player))) 524 .build() 525 ); 526 return; 527 } 528 if (((List<?>) parsedFlag.getValue()).isEmpty()) { 529 player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); 530 return; 531 } 532 if (list.removeAll((List) parsedFlag.getValue())) { 533 if (list.isEmpty()) { 534 if (plot.removeFlag(flag)) { 535 player.sendMessage( 536 TranslatableCaption.of("flag.flag_removed"), 537 TagResolver.builder() 538 .tag("flag", Tag.inserting(Component.text(args[0]))) 539 .tag("value", Tag.inserting(Component.text(flag.toString()))) 540 .build() 541 ); 542 return; 543 } else { 544 player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); 545 return; 546 } 547 } else { 548 PlotFlag<?, ?> plotFlag = parsedFlag.createFlagInstance(list); 549 PlotFlagAddEvent addEvent = eventDispatcher.callFlagAdd(plotFlag, plot); 550 if (addEvent.getEventResult() == Result.DENY) { 551 player.sendMessage( 552 TranslatableCaption.of("events.event_denied"), 553 TagResolver.resolver( 554 "value", 555 Tag.inserting(Component.text("Re-addition of " + plotFlag.getName())) 556 ) 557 ); 558 return; 559 } 560 if (plot.setFlag(addEvent.getFlag())) { 561 player.sendMessage(TranslatableCaption.of("flag.flag_partially_removed")); 562 return; 563 } else { 564 player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); 565 return; 566 } 567 } 568 } else { 569 player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); 570 return; 571 } 572 } else { 573 boolean result = plot.removeFlag(flag); 574 if (!result) { 575 player.sendMessage(TranslatableCaption.of("flag.flag_not_removed")); 576 return; 577 } 578 } 579 player.sendMessage( 580 TranslatableCaption.of("flag.flag_removed"), 581 TagResolver.builder() 582 .tag("flag", Tag.inserting(Component.text(args[0]))) 583 .tag("value", Tag.inserting(Component.text(flag.toString()))) 584 .build() 585 ); 586 } 587 588 @CommandDeclaration(command = "list", 589 aliases = {"l", "list", "flags"}, 590 usage = "/plot flag list", 591 category = CommandCategory.SETTINGS, 592 requiredType = RequiredType.NONE, 593 permission = "plots.flag.list") 594 public void list( 595 final Command command, final PlotPlayer<?> player, final String[] args, 596 final RunnableVal3<Command, Runnable, Runnable> confirm, 597 final RunnableVal2<Command, CommandResult> whenDone 598 ) { 599 if (!checkRequirements(player)) { 600 return; 601 } 602 603 final Map<Component, ArrayList<String>> flags = new HashMap<>(); 604 for (PlotFlag<?, ?> plotFlag : GlobalFlagContainer.getInstance().getRecognizedPlotFlags()) { 605 if (plotFlag instanceof InternalFlag) { 606 continue; 607 } 608 final Component category = plotFlag.getFlagCategory().toComponent(player); 609 final Collection<String> flagList = flags.computeIfAbsent(category, k -> new ArrayList<>()); 610 flagList.add(plotFlag.getName()); 611 } 612 613 for (final Map.Entry<Component, ArrayList<String>> entry : flags.entrySet()) { 614 Collections.sort(entry.getValue()); 615 Component category = 616 MINI_MESSAGE.deserialize( 617 TranslatableCaption.of("flag.flag_list_categories").getComponent(player), 618 TagResolver.resolver("category", Tag.inserting(entry.getKey().style(Style.empty()))) 619 ); 620 TextComponent.Builder builder = Component.text().append(category); 621 final Iterator<String> flagIterator = entry.getValue().iterator(); 622 while (flagIterator.hasNext()) { 623 final String flag = flagIterator.next(); 624 builder.append(MINI_MESSAGE 625 .deserialize( 626 TranslatableCaption.of("flag.flag_list_flag").getComponent(player), 627 TagResolver.builder() 628 .tag("command", Tag.preProcessParsed("/plot flag info " + flag)) 629 .tag("flag", Tag.inserting(Component.text(flag))) 630 .tag("suffix", Tag.inserting(Component.text(flagIterator.hasNext() ? ", " : ""))) 631 .build() 632 )); 633 } 634 player.sendMessage(StaticCaption.of(MINI_MESSAGE.serialize(builder.build()))); 635 } 636 } 637 638 @CommandDeclaration(command = "info", 639 aliases = {"i", "info"}, 640 usage = "/plot flag info <flag>", 641 category = CommandCategory.SETTINGS, 642 requiredType = RequiredType.NONE, 643 permission = "plots.flag.info") 644 public void info( 645 final Command command, final PlotPlayer<?> player, final String[] args, 646 final RunnableVal3<Command, Runnable, Runnable> confirm, 647 final RunnableVal2<Command, CommandResult> whenDone 648 ) { 649 if (!checkRequirements(player)) { 650 return; 651 } 652 if (args.length < 1) { 653 player.sendMessage( 654 TranslatableCaption.of("commandconfig.command_syntax"), 655 TagResolver.resolver("value", Tag.inserting(Component.text("/plot flag info <flag>"))) 656 ); 657 return; 658 } 659 final PlotFlag<?, ?> plotFlag = getFlag(player, args[0]); 660 if (plotFlag != null) { 661 player.sendMessage(TranslatableCaption.of("flag.flag_info_header")); 662 // Flag name 663 player.sendMessage( 664 TranslatableCaption.of("flag.flag_info_name"), 665 TagResolver.resolver("flag", Tag.inserting(Component.text(plotFlag.getName()))) 666 ); 667 // Flag category 668 player.sendMessage( 669 TranslatableCaption.of("flag.flag_info_category"), 670 TagResolver.resolver( 671 "value", 672 Tag.inserting(plotFlag.getFlagCategory().toComponent(player)) 673 ) 674 ); 675 // Flag description 676 // TODO maybe merge and \n instead? 677 player.sendMessage(TranslatableCaption.of("flag.flag_info_description")); 678 player.sendMessage(plotFlag.getFlagDescription()); 679 // Flag example 680 player.sendMessage( 681 TranslatableCaption.of("flag.flag_info_example"), 682 TagResolver.builder() 683 .tag("command", Tag.preProcessParsed("/plot flag set")) 684 .tag("flag", Tag.preProcessParsed(plotFlag.getName())) 685 .tag("value", Tag.preProcessParsed(plotFlag.getExample())) 686 .build() 687 ); 688 // Default value 689 final String defaultValue = player.getLocation().getPlotArea().getFlagContainer() 690 .getFlagErased(plotFlag.getClass()).toString(); 691 player.sendMessage( 692 TranslatableCaption.of("flag.flag_info_default_value"), 693 TagResolver.resolver("value", Tag.inserting(Component.text(defaultValue))) 694 ); 695 // Footer. Done this way to prevent the duplicate-message-thingy from catching it 696 player.sendMessage(TranslatableCaption.of("flag.flag_info_footer")); 697 } 698 } 699 700}