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.common.collect.Lists; 022import com.google.inject.Inject; 023import com.plotsquared.core.configuration.Settings; 024import com.plotsquared.core.configuration.caption.TranslatableCaption; 025import com.plotsquared.core.location.Location; 026import com.plotsquared.core.permissions.Permission; 027import com.plotsquared.core.player.ConsolePlayer; 028import com.plotsquared.core.player.PlotPlayer; 029import com.plotsquared.core.plot.Plot; 030import com.plotsquared.core.plot.PlotArea; 031import com.plotsquared.core.plot.schematic.Schematic; 032import com.plotsquared.core.plot.world.PlotAreaManager; 033import com.plotsquared.core.util.SchematicHandler; 034import com.plotsquared.core.util.StringMan; 035import com.plotsquared.core.util.TabCompletions; 036import com.plotsquared.core.util.task.RunnableVal; 037import com.plotsquared.core.util.task.TaskManager; 038import net.kyori.adventure.text.Component; 039import net.kyori.adventure.text.minimessage.tag.Tag; 040import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; 041import org.checkerframework.checker.nullness.qual.NonNull; 042 043import java.net.URL; 044import java.util.ArrayList; 045import java.util.Collection; 046import java.util.Collections; 047import java.util.LinkedList; 048import java.util.List; 049import java.util.UUID; 050import java.util.stream.Collectors; 051 052@CommandDeclaration(command = "schematic", 053 permission = "plots.schematic", 054 aliases = "schem", 055 category = CommandCategory.SCHEMATIC, 056 usage = "/plot schematic <save | saveall | paste | list>") 057public class SchematicCmd extends SubCommand { 058 059 private final PlotAreaManager plotAreaManager; 060 private final SchematicHandler schematicHandler; 061 private boolean running = false; 062 063 @Inject 064 public SchematicCmd( 065 final @NonNull PlotAreaManager plotAreaManager, 066 final @NonNull SchematicHandler schematicHandler 067 ) { 068 this.plotAreaManager = plotAreaManager; 069 this.schematicHandler = schematicHandler; 070 } 071 072 @Override 073 public boolean onCommand(final PlotPlayer<?> player, String[] args) { 074 if (args.length < 1) { 075 player.sendMessage( 076 TranslatableCaption.of("commandconfig.command_syntax"), 077 TagResolver.resolver("value", Tag.inserting(Component.text("Possible values: save, paste, exportall, list"))) 078 ); 079 return true; 080 } 081 String arg = args[0].toLowerCase(); 082 switch (arg) { 083 case "paste" -> { 084 if (!player.hasPermission(Permission.PERMISSION_SCHEMATIC_PASTE)) { 085 player.sendMessage( 086 TranslatableCaption.of("permission.no_permission"), 087 TagResolver.resolver( 088 "node", 089 Tag.inserting(Permission.PERMISSION_SCHEMATIC_PASTE) 090 ) 091 ); 092 return false; 093 } 094 if (args.length < 2) { 095 player.sendMessage( 096 TranslatableCaption.of("commandconfig.command_syntax"), 097 TagResolver.resolver( 098 "value", 099 Tag.inserting(Component.text("Possible values: save, paste, exportall, list")) 100 ) 101 ); 102 break; 103 } 104 Location loc = player.getLocation(); 105 final Plot plot = loc.getPlotAbs(); 106 if (plot == null) { 107 player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); 108 return false; 109 } 110 if (!plot.hasOwner()) { 111 player.sendMessage(TranslatableCaption.of("info.plot_unowned")); 112 return false; 113 } 114 if (!plot.isOwner(player.getUUID()) && !player.hasPermission("plots.admin.command.schematic.paste")) { 115 player.sendMessage(TranslatableCaption.of("permission.no_plot_perms")); 116 return false; 117 } 118 if (plot.getVolume() > Integer.MAX_VALUE) { 119 player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large")); 120 return false; 121 } 122 if (this.running) { 123 player.sendMessage(TranslatableCaption.of("error.task_in_process")); 124 return false; 125 } 126 final String location = args[1]; 127 this.running = true; 128 TaskManager.runTaskAsync(() -> { 129 Schematic schematic = null; 130 if (location.startsWith("url:")) { 131 try { 132 UUID uuid = UUID.fromString(location.substring(4)); 133 URL base = new URL(Settings.Web.URL); 134 URL url = new URL(base, "uploads/" + uuid + ".schematic"); 135 schematic = this.schematicHandler.getSchematic(url); 136 } catch (Exception e) { 137 e.printStackTrace(); 138 player.sendMessage( 139 TranslatableCaption.of("schematics.schematic_invalid"), 140 TagResolver.resolver( 141 "reason", 142 Tag.inserting(Component.text("non-existent url: " + location)) 143 ) 144 ); 145 SchematicCmd.this.running = false; 146 return; 147 } 148 } else { 149 try { 150 schematic = this.schematicHandler.getSchematic(location); 151 } catch (SchematicHandler.UnsupportedFormatException e) { 152 e.printStackTrace(); 153 } 154 } 155 if (schematic == null) { 156 SchematicCmd.this.running = false; 157 player.sendMessage( 158 TranslatableCaption.of("schematics.schematic_invalid"), 159 TagResolver.resolver( 160 "reason", 161 Tag.inserting(Component.text("non-existent or not in gzip format")) 162 ) 163 ); 164 return; 165 } 166 this.schematicHandler.paste( 167 schematic, 168 plot, 169 0, 170 plot.getArea().getMinBuildHeight(), 171 0, 172 false, 173 player, 174 new RunnableVal<>() { 175 @Override 176 public void run(Boolean value) { 177 SchematicCmd.this.running = false; 178 if (value) { 179 player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_success")); 180 } else { 181 player.sendMessage(TranslatableCaption.of("schematics.schematic_paste_failed")); 182 } 183 } 184 } 185 ); 186 }); 187 } 188 case "saveall", "exportall" -> { 189 if (!(player instanceof ConsolePlayer)) { 190 player.sendMessage(TranslatableCaption.of("console.not_console")); 191 return false; 192 } 193 if (args.length != 2) { 194 player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_world_args")); 195 player.sendMessage( 196 TranslatableCaption.of("commandconfig.command_syntax"), 197 TagResolver.resolver( 198 "value", 199 Tag.inserting(Component.text("Use /plot schematic exportall <area>")) 200 ) 201 ); 202 return false; 203 } 204 PlotArea area = this.plotAreaManager.getPlotAreaByString(args[1]); 205 if (area == null) { 206 player.sendMessage( 207 TranslatableCaption.of("errors.not_valid_plot_world"), 208 TagResolver.resolver("value", Tag.inserting(Component.text(args[1]))) 209 ); 210 return false; 211 } 212 Collection<Plot> plots = area.getPlots(); 213 if (plots.isEmpty()) { 214 player.sendMessage(TranslatableCaption.of("schematic.schematic_exportall_world")); 215 player.sendMessage( 216 TranslatableCaption.of("commandconfig.command_syntax"), 217 TagResolver.resolver("value", Tag.inserting(Component.text("Use /plot sch exportall <area>"))) 218 ); 219 return false; 220 } 221 boolean result = this.schematicHandler.exportAll(plots, null, null, 222 () -> player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_finished")) 223 ); 224 if (!result) { 225 player.sendMessage(TranslatableCaption.of("error.task_in_process")); 226 return false; 227 } else { 228 player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_started")); 229 player.sendMessage( 230 TranslatableCaption.of("schematics.plot_to_schem"), 231 TagResolver.resolver("amount", Tag.inserting(Component.text(plots.size()))) 232 ); 233 } 234 } 235 case "export", "save" -> { 236 if (!player.hasPermission(Permission.PERMISSION_SCHEMATIC_SAVE)) { 237 player.sendMessage( 238 TranslatableCaption.of("permission.no_permission"), 239 TagResolver.resolver( 240 "node", 241 Tag.inserting(Permission.PERMISSION_SCHEMATIC_SAVE) 242 ) 243 ); 244 return false; 245 } 246 if (this.running) { 247 player.sendMessage(TranslatableCaption.of("error.task_in_process")); 248 return false; 249 } 250 Location location = player.getLocation(); 251 Plot plot = location.getPlotAbs(); 252 if (plot == null) { 253 player.sendMessage(TranslatableCaption.of("errors.not_in_plot")); 254 return false; 255 } 256 if (!plot.hasOwner()) { 257 player.sendMessage(TranslatableCaption.of("info.plot_unowned")); 258 return false; 259 } 260 if (plot.getVolume() > Integer.MAX_VALUE) { 261 player.sendMessage(TranslatableCaption.of("schematics.schematic_too_large")); 262 return false; 263 } 264 if (!plot.isOwner(player.getUUID()) && !player.hasPermission("plots.admin.command.schematic.save")) { 265 player.sendMessage(TranslatableCaption.of("permission.no_plot_perms")); 266 return false; 267 } 268 ArrayList<Plot> plots = Lists.newArrayList(plot); 269 boolean result = this.schematicHandler.exportAll(plots, null, null, () -> { 270 player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_single_finished")); 271 SchematicCmd.this.running = false; 272 }); 273 if (!result) { 274 player.sendMessage(TranslatableCaption.of("error.task_in_process")); 275 return false; 276 } else { 277 player.sendMessage(TranslatableCaption.of("schematics.schematic_exportall_started")); 278 } 279 } 280 case "list" -> { 281 if (!player.hasPermission(Permission.PERMISSION_SCHEMATIC_LIST)) { 282 player.sendMessage( 283 TranslatableCaption.of("permission.no_permission"), 284 TagResolver.resolver( 285 "node", 286 Tag.inserting(Permission.PERMISSION_SCHEMATIC_LIST) 287 ) 288 ); 289 return false; 290 } 291 final String string = StringMan.join(this.schematicHandler.getSchematicNames(), "$2, $1"); 292 player.sendMessage( 293 TranslatableCaption.of("schematics.schematic_list"), 294 TagResolver.resolver("list", Tag.inserting(Component.text(string))) 295 ); 296 } 297 default -> player.sendMessage( 298 TranslatableCaption.of("commandconfig.command_syntax"), 299 TagResolver.resolver("value", Tag.inserting(Component.text("Possible values: save, paste, exportall, list"))) 300 ); 301 } 302 return true; 303 } 304 305 @Override 306 public Collection<Command> tab(final PlotPlayer<?> player, final String[] args, final boolean space) { 307 if (args.length == 1) { 308 final List<String> completions = new LinkedList<>(); 309 if (player.hasPermission(Permission.PERMISSION_SCHEMATIC_SAVE)) { 310 completions.add("save"); 311 } 312 if (player.hasPermission(Permission.PERMISSION_SCHEMATIC_LIST)) { 313 completions.add("list"); 314 } 315 if (player.hasPermission(Permission.PERMISSION_SCHEMATIC_PASTE)) { 316 completions.add("paste"); 317 } 318 final List<Command> commands = completions.stream().filter(completion -> completion 319 .toLowerCase() 320 .startsWith(args[0].toLowerCase())) 321 .map(completion -> new Command( 322 null, 323 true, 324 completion, 325 "", 326 RequiredType.NONE, 327 CommandCategory.ADMINISTRATION 328 ) { 329 }).collect(Collectors.toCollection(LinkedList::new)); 330 if (player.hasPermission(Permission.PERMISSION_SCHEMATIC) && args[0].length() > 0) { 331 commands.addAll(TabCompletions.completePlayers(player, args[0], Collections.emptyList())); 332 } 333 return commands; 334 } 335 return TabCompletions.completePlayers(player, String.join(",", args).trim(), Collections.emptyList()); 336 } 337 338}