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