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