PageRenderTime 44ms CodeModel.GetById 29ms app.highlight 12ms RepoModel.GetById 0ms app.codeStats 0ms

/worldedit-core/src/main/java/com/sk89q/worldedit/command/SchematicCommands.java

https://gitlab.com/Skull3x/WorldEdit
Java | 353 lines | 281 code | 39 blank | 33 comment | 41 complexity | 29476bfcdc9c4d3dbf11e827819efffe MD5 | raw file
  1/*
  2 * WorldEdit, a Minecraft world manipulation toolkit
  3 * Copyright (C) sk89q <http://www.sk89q.com>
  4 * Copyright (C) WorldEdit team and contributors
  5 *
  6 * This program is free software: you can redistribute it and/or modify it
  7 * under the terms of the GNU Lesser General Public License as published by the
  8 * Free Software Foundation, either version 3 of the License, or
  9 * (at your option) any later version.
 10 *
 11 * This program is distributed in the hope that it will be useful, but WITHOUT
 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 14 * for more details.
 15 *
 16 * You should have received a copy of the GNU Lesser General Public License
 17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 18 */
 19
 20package com.sk89q.worldedit.command;
 21
 22import com.sk89q.minecraft.util.commands.Command;
 23import com.sk89q.minecraft.util.commands.CommandContext;
 24import com.sk89q.minecraft.util.commands.CommandException;
 25import com.sk89q.minecraft.util.commands.CommandPermissions;
 26import com.sk89q.worldedit.EditSession;
 27import com.sk89q.worldedit.LocalConfiguration;
 28import com.sk89q.worldedit.LocalSession;
 29import com.sk89q.worldedit.WorldEdit;
 30import com.sk89q.worldedit.WorldEditException;
 31import com.sk89q.worldedit.entity.Player;
 32import com.sk89q.worldedit.extension.platform.Actor;
 33import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
 34import com.sk89q.worldedit.extent.clipboard.Clipboard;
 35import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
 36import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
 37import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
 38import com.sk89q.worldedit.function.operation.Operations;
 39import com.sk89q.worldedit.math.transform.Transform;
 40import com.sk89q.worldedit.session.ClipboardHolder;
 41import com.sk89q.worldedit.util.command.binding.Switch;
 42import com.sk89q.worldedit.util.command.parametric.Optional;
 43import com.sk89q.worldedit.util.io.Closer;
 44import com.sk89q.worldedit.util.io.file.FilenameException;
 45import com.sk89q.worldedit.world.registry.WorldData;
 46
 47import java.io.BufferedInputStream;
 48import java.io.BufferedOutputStream;
 49import java.io.File;
 50import java.io.FileInputStream;
 51import java.io.FileOutputStream;
 52import java.io.IOException;
 53import java.util.ArrayList;
 54import java.util.Arrays;
 55import java.util.Comparator;
 56import java.util.List;
 57import java.util.logging.Level;
 58import java.util.logging.Logger;
 59import java.util.regex.Pattern;
 60
 61import static com.google.common.base.Preconditions.checkNotNull;
 62
 63/**
 64 * Commands that work with schematic files.
 65 */
 66public class SchematicCommands {
 67
 68    /**
 69     * 9 schematics per page fits in the MC chat window.
 70     */
 71    private static final int SCHEMATICS_PER_PAGE = 9;
 72    private static final Logger log = Logger.getLogger(SchematicCommands.class.getCanonicalName());
 73    private final WorldEdit worldEdit;
 74
 75    /**
 76     * Create a new instance.
 77     *
 78     * @param worldEdit reference to WorldEdit
 79     */
 80    public SchematicCommands(WorldEdit worldEdit) {
 81        checkNotNull(worldEdit);
 82        this.worldEdit = worldEdit;
 83    }
 84
 85    @Command(
 86            aliases = { "load" },
 87            usage = "[<format>] <filename>",
 88            desc = "Load a schematic into your clipboard",
 89            min = 1, max = 2
 90    )
 91    @Deprecated
 92    @CommandPermissions({ "worldedit.clipboard.load", "worldedit.schematic.load" })
 93    public void load(Player player, LocalSession session, @Optional("schematic") String formatName, String filename) throws FilenameException {
 94        LocalConfiguration config = worldEdit.getConfiguration();
 95
 96        File dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
 97        File f = worldEdit.getSafeOpenFile(player, dir, filename, "schematic", "schematic");
 98
 99        if (!f.exists()) {
100            player.printError("Schematic " + filename + " does not exist!");
101            return;
102        }
103
104        ClipboardFormat format = ClipboardFormat.findByAlias(formatName);
105        if (format == null) {
106            player.printError("Unknown schematic format: " + formatName);
107            return;
108        }
109
110        Closer closer = Closer.create();
111        try {
112            FileInputStream fis = closer.register(new FileInputStream(f));
113            BufferedInputStream bis = closer.register(new BufferedInputStream(fis));
114            ClipboardReader reader = format.getReader(bis);
115
116            WorldData worldData = player.getWorld().getWorldData();
117            Clipboard clipboard = reader.read(player.getWorld().getWorldData());
118            session.setClipboard(new ClipboardHolder(clipboard, worldData));
119
120            log.info(player.getName() + " loaded " + f.getCanonicalPath());
121            player.print(filename + " loaded. Paste it with //paste");
122        } catch (IOException e) {
123            player.printError("Schematic could not read or it does not exist: " + e.getMessage());
124            log.log(Level.WARNING, "Failed to load a saved clipboard", e);
125        } finally {
126            try {
127                closer.close();
128            } catch (IOException ignored) {
129            }
130        }
131    }
132
133    @Command(
134            aliases = { "save" },
135            usage = "[<format>] <filename>",
136            desc = "Save a schematic into your clipboard",
137            min = 1, max = 2
138    )
139    @Deprecated
140    @CommandPermissions({ "worldedit.clipboard.save", "worldedit.schematic.save" })
141    public void save(Player player, LocalSession session, @Optional("schematic") String formatName, String filename) throws CommandException, WorldEditException {
142        LocalConfiguration config = worldEdit.getConfiguration();
143
144        File dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
145        File f = worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic");
146
147        ClipboardFormat format = ClipboardFormat.findByAlias(formatName);
148        if (format == null) {
149            player.printError("Unknown schematic format: " + formatName);
150            return;
151        }
152
153        ClipboardHolder holder = session.getClipboard();
154        Clipboard clipboard = holder.getClipboard();
155        Transform transform = holder.getTransform();
156        Clipboard target;
157
158        // If we have a transform, bake it into the copy
159        if (!transform.isIdentity()) {
160            FlattenedClipboardTransform result = FlattenedClipboardTransform.transform(clipboard, transform, holder.getWorldData());
161            target = new BlockArrayClipboard(result.getTransformedRegion());
162            target.setOrigin(clipboard.getOrigin());
163            Operations.completeLegacy(result.copyTo(target));
164        } else {
165            target = clipboard;
166        }
167
168        Closer closer = Closer.create();
169        try {
170            // Create parent directories
171            File parent = f.getParentFile();
172            if (parent != null && !parent.exists()) {
173                if (!parent.mkdirs()) {
174                    throw new CommandException("Could not create folder for schematics!");
175                }
176            }
177
178            FileOutputStream fos = closer.register(new FileOutputStream(f));
179            BufferedOutputStream bos = closer.register(new BufferedOutputStream(fos));
180            ClipboardWriter writer = closer.register(format.getWriter(bos));
181            writer.write(target, holder.getWorldData());
182            log.info(player.getName() + " saved " + f.getCanonicalPath());
183            player.print(filename + " saved.");
184        } catch (IOException e) {
185            player.printError("Schematic could not written: " + e.getMessage());
186            log.log(Level.WARNING, "Failed to write a saved clipboard", e);
187        } finally {
188            try {
189                closer.close();
190            } catch (IOException ignored) {
191            }
192        }
193    }
194
195    @Command(
196            aliases = { "delete", "d" },
197            usage = "<filename>",
198            desc = "Delete a saved schematic",
199            help = "Delete a schematic from the schematic list",
200            min = 1,
201            max = 1
202    )
203    @CommandPermissions("worldedit.schematic.delete")
204    public void delete(Player player, LocalSession session, EditSession editSession, CommandContext args) throws WorldEditException {
205
206        LocalConfiguration config = worldEdit.getConfiguration();
207        String filename = args.getString(0);
208
209        File dir = worldEdit.getWorkingDirectoryFile(config.saveDir);
210        File f = worldEdit.getSafeSaveFile(player, dir, filename, "schematic", "schematic");
211
212        if (!f.exists()) {
213            player.printError("Schematic " + filename + " does not exist!");
214            return;
215        }
216
217        if (!f.delete()) {
218            player.printError("Deletion of " + filename + " failed! Maybe it is read-only.");
219            return;
220        }
221
222        player.print(filename + " has been deleted.");
223    }
224
225    @Command(
226            aliases = {"formats", "listformats", "f"},
227            desc = "List available formats",
228            max = 0
229    )
230    @CommandPermissions("worldedit.schematic.formats")
231    public void formats(Actor actor) throws WorldEditException {
232        actor.print("Available clipboard formats (Name: Lookup names)");
233        StringBuilder builder;
234        boolean first = true;
235        for (ClipboardFormat format : ClipboardFormat.values()) {
236            builder = new StringBuilder();
237            builder.append(format.name()).append(": ");
238            for (String lookupName : format.getAliases()) {
239                if (!first) {
240                    builder.append(", ");
241                }
242                builder.append(lookupName);
243                first = false;
244            }
245            first = true;
246            actor.print(builder.toString());
247        }
248    }
249
250    @Command(
251            aliases = {"list", "all", "ls"},
252            desc = "List saved schematics",
253            min = 0,
254            max = 1,
255            flags = "dnp",
256            help = "List all schematics in the schematics directory\n" +
257                    " -d sorts by date, oldest first\n" +
258                    " -n sorts by date, newest first\n" +
259                    " -p <page> prints the requested page\n"
260    )
261    @CommandPermissions("worldedit.schematic.list")
262    public void list(Actor actor, CommandContext args, @Switch('p') @Optional("1") int page) throws WorldEditException {
263        File dir = worldEdit.getWorkingDirectoryFile(worldEdit.getConfiguration().saveDir);
264        List<File> fileList = allFiles(dir);
265
266        if (fileList.isEmpty()) {
267            actor.printError("No schematics found.");
268            return;
269        }
270
271        File[] files = new File[fileList.size()];
272        fileList.toArray(files);
273
274        int pageCount = files.length / SCHEMATICS_PER_PAGE + 1;
275        if (page < 1) {
276            actor.printError("Page must be at least 1");
277            return;
278        }
279        if (page > pageCount) {
280            actor.printError("Page must be less than " + (pageCount + 1));
281            return;
282        }
283
284        final int sortType = args.hasFlag('d') ? -1 : args.hasFlag('n') ? 1 : 0;
285        // cleanup file list
286        Arrays.sort(files, new Comparator<File>(){
287            @Override
288            public int compare(File f1, File f2) {
289                // http://stackoverflow.com/questions/203030/best-way-to-list-files-in-java-sorted-by-date-modified
290                int res;
291                if (sortType == 0) { // use name by default
292                    int p = f1.getParent().compareTo(f2.getParent());
293                    if (p == 0) { // same parent, compare names
294                        res = f1.getName().compareTo(f2.getName());
295                    } else { // different parent, sort by that
296                        res = p;
297                    }
298                } else {
299                    res = Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()); // use date if there is a flag
300                    if (sortType == 1) res = -res; // flip date for newest first instead of oldest first
301                }
302                return res;
303            }
304        });
305
306        List<String> schematics = listFiles(worldEdit.getConfiguration().saveDir, files);
307        int offset = (page - 1) * SCHEMATICS_PER_PAGE;
308
309        actor.print("Available schematics (Filename: Format) [" + page + "/" + pageCount + "]:");
310        StringBuilder build = new StringBuilder();
311        int limit = Math.min(offset + SCHEMATICS_PER_PAGE, schematics.size());
312        for (int i = offset; i < limit;) {
313            build.append(schematics.get(i));
314            if (++i != limit) {
315                build.append("\n");
316            }
317        }
318
319        actor.print(build.toString());
320    }
321
322    private List<File> allFiles(File root) {
323        File[] files = root.listFiles();
324        if (files == null) return null;
325        List<File> fileList = new ArrayList<File>();
326        for (File f : files) {
327            if (f.isDirectory()) {
328                List<File> subFiles = allFiles(f);
329                if (subFiles == null) continue; // empty subdir
330                fileList.addAll(subFiles);
331            } else {
332                fileList.add(f);
333            }
334        }
335        return fileList;
336    }
337
338    private List<String> listFiles(String prefix, File[] files) {
339        if (prefix == null) prefix = "";
340        List<String> result = new ArrayList<String>();
341        for (File file : files) {
342            StringBuilder build = new StringBuilder();
343
344            build.append("\u00a72");
345            ClipboardFormat format = ClipboardFormat.findByFile(file);
346            boolean inRoot = file.getParentFile().getName().equals(prefix);
347            build.append(inRoot ? file.getName() : file.getPath().split(Pattern.quote(prefix + File.separator))[1])
348                    .append(": ").append(format == null ? "Unknown" : format.name());
349            result.add(build.toString());
350        }
351        return result;
352    }
353}