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