net.morematerials.manager.UpdateManager.java Source code

Java tutorial

Introduction

Here is the source code for net.morematerials.manager.UpdateManager.java

Source

/*
 * This file is part of MoreMaterials, licensed under the MIT License (MIT).
 *
 * Copyright (c) 2013 AlmuraDev <http://www.almuradev.com/>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package net.morematerials.manager;

import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.bukkit.configuration.file.YamlConfiguration;
import org.getspout.spoutapi.SpoutManager;

import net.morematerials.MoreMaterials;

public class UpdateManager {

    private MoreMaterials plugin;
    private File tempDir;
    private ArrayList<String> itemMap = new ArrayList<String>();

    public UpdateManager(MoreMaterials plugin) {
        this.plugin = plugin;
        this.tempDir = new File(this.plugin.getDataFolder().getPath(), "updater");

        // Get all .smp files.
        File dir = new File(this.plugin.getDataFolder().getPath(), "materials");
        for (File file : dir.listFiles()) {
            if (file.getName().endsWith(".smp")) {
                try {
                    this.updateSmp(file);
                } catch (Exception exception) {
                }
            }
        }

        // Cleanups
        this.itemMap.clear();
    }

    private void updateSmp(File file) throws Exception {
        ZipFile smpFile = new ZipFile(file);
        Enumeration<? extends ZipEntry> entries = smpFile.entries();
        String smpName = file.getName().substring(0, file.getName().lastIndexOf("."));

        // First we need to know what files are in this .smp file, because the old format uses magic filename matching.
        ArrayList<String> containedFiles = new ArrayList<String>();
        HashMap<String, YamlConfiguration> materials = new HashMap<String, YamlConfiguration>();

        // Now we walk through the file once and store every file.
        ZipEntry entry;
        YamlConfiguration yaml;
        while (entries.hasMoreElements()) {
            entry = entries.nextElement();

            // Only if its a .yml file
            if (entry.getName().endsWith(".yml")) {
                // Load the .yml file
                yaml = new YamlConfiguration();
                yaml.load(smpFile.getInputStream(entry));

                // Texture is required for new package format.
                if (!yaml.contains("Texture")) {
                    materials.put(entry.getName().substring(0, entry.getName().lastIndexOf(".")), yaml);
                } else {
                    containedFiles.add(entry.getName());
                }
            } else {
                containedFiles.add(entry.getName());
            }
        }

        // If this map contains any entry, we need to convert something.
        if (materials.size() > 0) {
            this.plugin.getUtilsManager().log("Deprecated .smp found: " + file.getName() + ". Updating...");

            // We need a temporary directory to update the .smp in.
            this.tempDir.mkdir();

            // First extract all untouched assets:
            for (String filename : containedFiles) {
                InputStream in = smpFile.getInputStream(smpFile.getEntry(filename));
                OutputStream out = new FileOutputStream(new File(this.tempDir, filename));
                int read;
                byte[] bytes = new byte[1024];
                while ((read = in.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }
                out.flush();
                out.close();
                in.close();
            }

            // Now convert each .yml file in this archive.
            YamlConfiguration oldYaml;
            YamlConfiguration newYaml;
            for (String materialName : materials.keySet()) {
                oldYaml = materials.get(materialName);
                newYaml = new YamlConfiguration();

                // Required "Type" which is Block or Item. Old format didnt support Tools anyway.
                newYaml.set("Type", oldYaml.getString("Type"));
                // Title is now required and falls back to filename.
                newYaml.set("Title", oldYaml.getString("Title", materialName));

                // Now call the converter methods.
                if (newYaml.getString("Type").equals("Block")) {
                    this.convertBlock(oldYaml, newYaml, materialName, containedFiles);
                    this.convertBlockHandlers(oldYaml, newYaml);
                } else if (newYaml.getString("Type").equals("Item")) {
                    this.convertItem(oldYaml, newYaml, materialName, containedFiles);
                    this.convertItemHandlers(oldYaml, newYaml);
                }

                // Copy over recipes - nothing changed here!
                if (oldYaml.contains("Recipes")) {
                    newYaml.set("Recipes", oldYaml.getList("Recipes"));
                }

                // Finally store the new .yml file.
                String yamlString = newYaml.saveToString();
                BufferedWriter out = new BufferedWriter(
                        new FileWriter(new File(this.tempDir, materialName + ".yml")));
                out.write(this.fixYamlProblems(yamlString));
                out.close();

                // Also update itemmap entry!
                for (Integer i = 0; i < this.itemMap.size(); i++) {
                    String oldMaterial = this.itemMap.get(i).replaceAll("^[0-9]+:MoreMaterials.", "");
                    if (oldMaterial.equals(newYaml.getString("Title"))) {
                        this.itemMap.set(i, this.itemMap.get(i).replaceAll("^([0-9]+:MoreMaterials.).+$",
                                "$1" + smpName + "." + materialName));
                        break;
                    }
                }

                // And we need to tell SpoutPlugin that this material must be renamed!
                SpoutManager.getMaterialManager().renameMaterialKey(this.plugin, newYaml.getString("Title"),
                        smpName + "." + materialName);
            }

            // First remove old .smp file
            smpFile.close();
            file.delete();

            // Then repack the new .smp file
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(file));
            for (File entryFile : this.tempDir.listFiles()) {
                FileInputStream in = new FileInputStream(entryFile);
                out.putNextEntry(new ZipEntry(entryFile.getName()));
                Integer len;
                byte[] buf = new byte[1024];
                while ((len = in.read(buf)) > 0) {
                    out.write(buf, 0, len);
                }
                out.closeEntry();
                in.close();
            }
            out.close();

            // At last remove the temp directory.
            FileUtils.deleteDirectory(this.tempDir);
        } else {
            // At last, close the file handle.
            smpFile.close();
        }
    }

    private void convertBlock(YamlConfiguration oldYaml, YamlConfiguration newYaml, String materialName,
            ArrayList<String> containedFiles) throws Exception {
        // This one is renamed because in the future items can fork items.
        if (oldYaml.contains("BlockID")) {
            newYaml.set("BaseId", oldYaml.getInt("BlockID"));
        }

        // Rotation was never officially, but we will support 1.7.1 changes.
        if (oldYaml.contains("Rotate")) {
            newYaml.set("Rotation", oldYaml.getBoolean("Rotate"));
        }

        // Texture was a magic-function before. Replacing it now by correct one.
        newYaml.set("Texture", materialName + ".png");

        // Also there were no coords before, so we need to automatically calculate them for now.
        ArrayList<String> coords = new ArrayList<String>();
        BufferedImage bufferedImage = ImageIO.read(new File(this.tempDir, materialName + ".png"));
        Integer textureCount = bufferedImage.getWidth() / bufferedImage.getHeight();
        for (Integer i = 0; i < textureCount; i++) {
            coords.add("" + (i * bufferedImage.getHeight()) + " 0 " + bufferedImage.getHeight() + " "
                    + bufferedImage.getHeight());
        }
        newYaml.set("Coords", coords);

        // Shape was also a magic function before, replacing that now.
        if (containedFiles.contains(materialName + ".shape")) {
            newYaml.set("Shape", materialName + ".shape");
        }

        // We can simply copy hardness...
        if (oldYaml.contains("Hardness")) {
            newYaml.set("Hardness", oldYaml.getDouble("Hardness"));
        }

        // Also the friction...
        if (oldYaml.contains("Friction")) {
            newYaml.set("Friction", oldYaml.getDouble("Friction"));
        }

        // Also the drop stuff...
        if (oldYaml.contains("ItemDrop")) {
            newYaml.set("ItemDrop", oldYaml.getString("ItemDrop"));
        }
        if (oldYaml.contains("ItemDropAmount")) {
            newYaml.set("ItemDropAmount", oldYaml.getInt("ItemDropAmount"));
        }

        // And at last the lightlevel!
        if (oldYaml.contains("LightLevel")) {
            newYaml.set("LightLevel", oldYaml.getDouble("LightLevel"));
        }

        // Cannot convert handlers! (They are coded, so we really can't!)
        if (oldYaml.contains("WalkAction.Handler") || oldYaml.contains("Rclick.Handler")
                || oldYaml.contains("Lclick.Handler") || oldYaml.contains("RedstonePowered.Handler")) {
            this.plugin.getUtilsManager()
                    .log(" - " + materialName + " uses handlers, they cannot be converted automatically.");
        }
    }

    private void convertItem(YamlConfiguration oldYaml, YamlConfiguration newYaml, String materialName,
            ArrayList<String> containedFiles) throws Exception {
        // Texture was a magic-function before. Replacing it now by correct one.
        newYaml.set("Texture", materialName + ".png");

        // Also there were no coords before, so we need to automatically calculate them for now.
        ArrayList<String> coords = new ArrayList<String>();
        BufferedImage bufferedImage = ImageIO.read(new File(this.tempDir, materialName + ".png"));
        coords.add("0 0 " + bufferedImage.getWidth() + " " + bufferedImage.getHeight());
        newYaml.set("Coords", coords);

        // Cannot convert handlers! (They are coded, so we really can't!)
        if (oldYaml.contains("Rclick.Handler") || oldYaml.contains("Lclick.Handler")) {
            this.plugin.getUtilsManager()
                    .log(" - " + materialName + " uses handlers, they cannot be converted automatically.");
        }
    }

    private void convertBlockHandlers(YamlConfiguration oldYaml, YamlConfiguration newYaml) {
        //TODO (all handlers)
        // StepSound
        // WalkSpeed
        // JumpHeight
        // FallDamage
        // WalkAction
        // Rclick
        // Lclick
        // RedstonePowered
    }

    private void convertItemHandlers(YamlConfiguration oldYaml, YamlConfiguration newYaml) {
        //TODO (all handlers)
        // Damage
        // KeepEnchanting
        // Rclick
        // Lclick
    }

    private String fixYamlProblems(String yamlString) {
        // Fixes for crafting recipes, which may be lower case, because we simply copied them above.
        yamlString = yamlString.replaceAll("([\r\n][\\- ]+)ingredients:", "$1Ingredients:");
        yamlString = yamlString.replaceAll("([\r\n][\\- ]+)type: shaped", "$1Type: Shaped");
        yamlString = yamlString.replaceAll("([\r\n][\\- ]+)type: shapeless", "$1Type: Shapeless");
        yamlString = yamlString.replaceAll("([\r\n][\\- ]+)type: furnace", "$1Type: Furnace");
        yamlString = yamlString.replaceAll("([\r\n][\\- ]+)amount:", "$1Amount:");

        // This fixes the invalid recipes format.
        yamlString = yamlString.replaceAll("(Ingredients: )'([0-9 ]+)\\s+([0-9 ]+)\\s+([0-9 ]+)\\s+'",
                "$1|\n    $2\n    $3\n    $4");
        return yamlString;
    }

}