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.backup.BackupManager; 024import com.plotsquared.core.configuration.Settings; 025import com.plotsquared.core.configuration.caption.StaticCaption; 026import com.plotsquared.core.configuration.caption.TranslatableCaption; 027import com.plotsquared.core.inject.factory.ProgressSubscriberFactory; 028import com.plotsquared.core.permissions.Permission; 029import com.plotsquared.core.player.PlotPlayer; 030import com.plotsquared.core.plot.Plot; 031import com.plotsquared.core.plot.PlotArea; 032import com.plotsquared.core.plot.PlotManager; 033import com.plotsquared.core.queue.QueueCoordinator; 034import com.plotsquared.core.util.PatternUtil; 035import com.plotsquared.core.util.StringMan; 036import com.plotsquared.core.util.TabCompletions; 037import com.plotsquared.core.util.WorldUtil; 038import com.sk89q.worldedit.function.pattern.Pattern; 039import com.sk89q.worldedit.world.block.BlockCategory; 040import com.sk89q.worldedit.world.block.BlockType; 041import com.sk89q.worldedit.world.block.BlockTypes; 042import net.kyori.adventure.text.Component; 043import net.kyori.adventure.text.minimessage.tag.Tag; 044import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 045import org.checkerframework.checker.nullness.qual.NonNull; 046 047import java.util.ArrayList; 048import java.util.Arrays; 049import java.util.Collection; 050import java.util.Collections; 051import java.util.HashSet; 052import java.util.LinkedList; 053import java.util.List; 054import java.util.Locale; 055import java.util.stream.Collectors; 056 057@CommandDeclaration(command = "set", 058 aliases = {"s"}, 059 usage = "/plot set <biome | alias | home | flag> <value...>", 060 permission = "plots.set", 061 category = CommandCategory.APPEARANCE, 062 requiredType = RequiredType.NONE) 063public class Set extends SubCommand { 064 065 public static final String[] values = new String[]{"biome", "alias", "home"}; 066 public static final String[] aliases = new String[]{"b", "w", "wf", "a", "h"}; 067 068 private final SetCommand component; 069 070 @Inject 071 public Set(final @NonNull WorldUtil worldUtil) { 072 this.component = new SetCommand() { 073 074 @Override 075 public String getId() { 076 return "set.component"; 077 } 078 079 @Override 080 public boolean set(PlotPlayer<?> player, final Plot plot, String value) { 081 final PlotArea plotArea = player.getLocation().getPlotArea(); 082 if (plotArea == null) { 083 return false; 084 } 085 final PlotManager manager = plotArea.getPlotManager(); 086 087 String[] components = manager.getPlotComponents(plot.getId()); 088 089 String[] args = value.split(" "); 090 String material = 091 StringMan.join(Arrays.copyOfRange(args, 1, args.length), ",").trim(); 092 093 final List<String> forbiddenTypes = new ArrayList<>(Settings.General.INVALID_BLOCKS); 094 095 if (Settings.Enabled_Components.CHUNK_PROCESSOR) { 096 forbiddenTypes.addAll(worldUtil.getTileEntityTypes().stream().map( 097 BlockType::getName).toList()); 098 } 099 100 if (!player.hasPermission(Permission.PERMISSION_ADMIN_ALLOW_UNSAFE) && 101 !forbiddenTypes.isEmpty()) { 102 for (String forbiddenType : forbiddenTypes) { 103 forbiddenType = forbiddenType.toLowerCase(Locale.ENGLISH); 104 if (forbiddenType.startsWith("minecraft:")) { 105 forbiddenType = forbiddenType.substring(10); 106 } 107 for (String blockType : material.split(",")) { 108 blockType = blockType.toLowerCase(Locale.ENGLISH); 109 if (blockType.startsWith("minecraft:")) { 110 blockType = blockType.substring(10); 111 } 112 113 if (blockType.startsWith("##")) { 114 try { 115 final BlockCategory category = BlockCategory.REGISTRY.get(blockType.substring(2) 116 .replaceAll("[*^|]+", "").toLowerCase(Locale.ENGLISH)); 117 if (category == null || !category.contains(BlockTypes.get(forbiddenType))) { 118 continue; 119 } 120 } catch (final Throwable ignored) { 121 } 122 } else if (!blockType.contains(forbiddenType)) { 123 continue; 124 } 125 player.sendMessage( 126 TranslatableCaption.of("invalid.component_illegal_block"), 127 TagResolver.resolver("value", Tag.inserting(Component.text(forbiddenType))) 128 ); 129 return true; 130 } 131 } 132 } 133 134 for (String component : components) { 135 if (component.equalsIgnoreCase(args[0])) { 136 if (!player.hasPermission(Permission.PERMISSION_SET_COMPONENT.format(component))) { 137 player.sendMessage( 138 TranslatableCaption.of("permission.no_permission"), 139 TagResolver.resolver( 140 "node", 141 Tag.inserting(Component.text(Permission.PERMISSION_SET_COMPONENT.format(component))) 142 ) 143 ); 144 return false; 145 } 146 if (args.length < 2) { 147 player.sendMessage(TranslatableCaption.of("need.need_block")); 148 return true; 149 } 150 151 Pattern pattern = PatternUtil.parse(player, material, false); 152 153 if (plot.getRunning() > 0) { 154 player.sendMessage(TranslatableCaption.of("errors.wait_for_timer")); 155 return false; 156 } 157 158 BackupManager.backup(player, plot, () -> { 159 plot.addRunning(); 160 QueueCoordinator queue = plotArea.getQueue(); 161 queue.setCompleteTask(() -> { 162 plot.removeRunning(); 163 player.sendMessage( 164 TranslatableCaption.of("working.component_complete"), 165 TagResolver.resolver("plot", Tag.inserting(Component.text(plot.getId().toString()))) 166 ); 167 }); 168 if (Settings.QUEUE.NOTIFY_PROGRESS) { 169 queue.addProgressSubscriber( 170 PlotSquared 171 .platform() 172 .injector() 173 .getInstance(ProgressSubscriberFactory.class) 174 .createWithActor(player)); 175 } 176 for (final Plot current : plot.getConnectedPlots()) { 177 current.getPlotModificationManager().setComponent(component, pattern, player, queue); 178 } 179 queue.enqueue(); 180 player.sendMessage(TranslatableCaption.of("working.generating_component")); 181 }); 182 return true; 183 } 184 } 185 return false; 186 } 187 188 @Override 189 public Collection<Command> tab( 190 final PlotPlayer<?> player, final String[] args, 191 final boolean space 192 ) { 193 return TabCompletions.completePatterns(StringMan.join(args, ",")); 194 } 195 }; 196 } 197 198 public boolean noArgs(PlotPlayer<?> player) { 199 ArrayList<String> newValues = new ArrayList<>(Arrays.asList("biome", "alias", "home")); 200 Plot plot = player.getCurrentPlot(); 201 if (plot != null) { 202 newValues.addAll(Arrays.asList(plot.getManager().getPlotComponents(plot.getId()))); 203 } 204 player.sendMessage(StaticCaption.of(TranslatableCaption 205 .of("commandconfig.subcommand_set_options_header_only") 206 .getComponent(player) + StringMan 207 .join(newValues, TranslatableCaption.of("blocklist.block_list_separator").getComponent(player)))); 208 return false; 209 } 210 211 @Override 212 public boolean onCommand(PlotPlayer<?> player, String[] args) { 213 if (args.length == 0) { 214 return noArgs(player); 215 } 216 Command cmd = MainCommand.getInstance().getCommand("set" + args[0]); 217 if (cmd != null) { 218 if (!player.hasPermission(cmd.getPermission(), true)) { 219 return false; 220 } 221 cmd.execute(player, Arrays.copyOfRange(args, 1, args.length), null, null); 222 return true; 223 } 224 // Additional checks 225 Plot plot = player.getCurrentPlot(); 226 if (plot == null) { 227 player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); 228 return false; 229 } 230 if (plot.getVolume() > Integer.MAX_VALUE) { 231 player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large")); 232 return false; 233 } 234 // components 235 HashSet<String> components = 236 new HashSet<>(Arrays.asList(plot.getManager().getPlotComponents(plot.getId()))); 237 if (components.contains(args[0].toLowerCase())) { 238 return this.component.onCommand(player, Arrays.copyOfRange(args, 0, args.length)); 239 } 240 return noArgs(player); 241 } 242 243 @Override 244 public Collection<Command> tab(final PlotPlayer<?> player, String[] args, boolean space) { 245 if (args.length == 1) { 246 final List<String> completions = new LinkedList<>(); 247 248 if (player.hasPermission(Permission.PERMISSION_SET_BIOME)) { 249 completions.add("biome"); 250 } 251 if (player.hasPermission(Permission.PERMISSION_SET_ALIAS)) { 252 completions.add("alias"); 253 } 254 if (player.hasPermission(Permission.PERMISSION_SET_HOME)) { 255 completions.add("home"); 256 } 257 if (player.hasPermission(Permission.PERMISSION_SET_MAIN)) { 258 completions.add("main"); 259 } 260 if (player.hasPermission(Permission.PERMISSION_SET_FLOOR)) { 261 completions.add("floor"); 262 } 263 if (player.hasPermission(Permission.PERMISSION_SET_AIR)) { 264 completions.add("air"); 265 } 266 if (player.hasPermission(Permission.PERMISSION_SET_ALL)) { 267 completions.add("all"); 268 } 269 if (player.hasPermission(Permission.PERMISSION_SET_BORDER)) { 270 completions.add("border"); 271 } 272 if (player.hasPermission(Permission.PERMISSION_SET_WALL)) { 273 completions.add("wall"); 274 } 275 if (player.hasPermission(Permission.PERMISSION_SET_OUTLINE)) { 276 completions.add("outline"); 277 } 278 if (player.hasPermission(Permission.PERMISSION_SET_MIDDLE)) { 279 completions.add("middle"); 280 } 281 final List<Command> commands = completions.stream().filter(completion -> completion 282 .toLowerCase() 283 .startsWith(args[0].toLowerCase())) 284 .map(completion -> new Command(null, true, completion, "", RequiredType.NONE, CommandCategory.APPEARANCE) { 285 }).collect(Collectors.toCollection(LinkedList::new)); 286 287 if (player.hasPermission(Permission.PERMISSION_SET) && args[0].length() > 0) { 288 commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList())); 289 } 290 return commands; 291 } else if (args.length > 1) { 292 // Additional checks 293 Plot plot = player.getCurrentPlot(); 294 if (plot == null) { 295 return new ArrayList<>(); 296 } 297 298 final String[] newArgs = new String[args.length - 1]; 299 System.arraycopy(args, 1, newArgs, 0, newArgs.length); 300 301 final Command cmd = MainCommand.getInstance().getCommand("set" + args[0]); 302 if (cmd != null) { 303 if (!player.hasPermission(cmd.getPermission(), true)) { 304 return new ArrayList<>(); 305 } 306 return cmd.tab(player, newArgs, space); 307 } 308 309 // components 310 HashSet<String> components = 311 new HashSet<>(Arrays.asList(plot.getManager().getPlotComponents(plot.getId()))); 312 if (components.contains(args[0].toLowerCase())) { 313 return this.component.tab(player, newArgs, space); 314 } 315 } 316 return tabOf(player, args, space); 317 } 318 319}