org.blockartistry.mod.Restructured.VersionCheck.java Source code

Java tutorial

Introduction

Here is the source code for org.blockartistry.mod.Restructured.VersionCheck.java

Source

/*
 * This file is part of Restructured, licensed under the MIT License (MIT).
 *
 * Copyright (c) OreCruncher
 *
 * 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 org.blockartistry.mod.Restructured;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import org.apache.commons.lang3.StringUtils;
import org.blockartistry.mod.Restructured.ModLog;
import org.blockartistry.mod.Restructured.ModOptions;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.StatCollector;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.event.FMLInterModComms;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;

// Modeled after the BuildCraft version check system.
public final class VersionCheck implements Runnable {

    private static final String REMOTE_VERSION_FILE = "https://raw.githubusercontent.com/OreCruncher/Restructured/master/versions.txt";
    private static final int VERSION_CHECK_RETRIES = 3;
    private static final int VERSION_CHECK_INTERVAL = 10000;

    public static enum UpdateStatus {
        UNKNOWN, CURRENT, OUTDATED, COMM_ERROR
    }

    public static final SoftwareVersion modVersion = new SoftwareVersion(Restructured.VERSION);
    public static SoftwareVersion currentVersion = new SoftwareVersion();
    public static UpdateStatus status = UpdateStatus.UNKNOWN;

    private static final String mcVersion = Loader.instance().getMinecraftModContainer().getVersion();

    public static class SoftwareVersion implements Comparable<SoftwareVersion> {

        public final int major;
        public final int minor;
        public final int revision;
        public final int patch;
        public final boolean isAlpha;
        public final boolean isBeta;

        public SoftwareVersion() {
            this.major = 0;
            this.minor = 0;
            this.revision = 0;
            this.patch = 0;
            this.isAlpha = false;
            this.isBeta = false;
        }

        public SoftwareVersion(String versionString) {

            // This can happen when running in debug
            if (versionString.charAt(0) == '@') {
                this.major = 0;
                this.minor = 0;
                this.revision = 0;
                this.patch = 0;
                this.isAlpha = false;
                this.isBeta = false;
                return;
            }

            assert versionString != null;
            assert versionString.length() > 0;

            isAlpha = StringUtils.containsIgnoreCase(versionString, "ALPHA");
            if (isAlpha)
                versionString = StringUtils.remove(versionString, "ALPHA");

            isBeta = StringUtils.containsIgnoreCase(versionString, "BETA");
            if (isBeta)
                versionString = StringUtils.remove(versionString, "BETA");

            final String[] parts = StringUtils.split(versionString, ".");
            final int numComponents = parts.length;

            assert numComponents >= 3;

            this.major = Integer.parseInt(parts[0]);
            this.minor = Integer.parseInt(parts[1]);
            this.revision = Integer.parseInt(parts[2]);
            if (numComponents == 4) {
                this.patch = Integer.parseInt(parts[3]);
            } else {
                this.patch = 0;
            }
        }

        @Override
        public String toString() {
            final StringBuilder builder = new StringBuilder();
            builder.append(this.major).append('.').append(this.minor).append('.').append(this.revision);
            if (this.patch != 0)
                builder.append('.').append(this.patch);
            if (this.isAlpha)
                builder.append("ALPHA");
            if (this.isBeta)
                builder.append("BETA");
            return builder.toString();
        }

        @Override
        public int compareTo(final SoftwareVersion obj) {

            if (this.major != obj.major)
                return this.major - obj.major;

            if (this.minor != obj.minor)
                return this.minor - obj.minor;

            if (this.revision != obj.revision)
                return this.revision - obj.revision;

            return this.patch - obj.patch;
        }
    }

    private VersionCheck() {
    }

    private static final String CURSE_PROJECT_NAME = "230570-restructured";
    private static final String MOD_NAME_TEMPLATE = "Restructured-1.7.10-[].jar";

    public static void register() {

        if (Loader.isModLoaded("VersionChecker")) {
            final NBTTagCompound nbt = new NBTTagCompound();
            nbt.setString("curseProjectName", CURSE_PROJECT_NAME);
            nbt.setString("curseFilenameParser", MOD_NAME_TEMPLATE);
            FMLInterModComms.sendRuntimeMessage(Restructured.MOD_ID, "VersionChecker", "addVersionCheck", nbt);
        }

        if (ModOptions.getOnlineVersionChecking()) {
            final VersionCheck test = new VersionCheck();
            MinecraftForge.EVENT_BUS.register(test);
            new Thread(test).start();
        }
    }

    @SubscribeEvent
    public void playerLogin(final PlayerLoggedInEvent event) {

        if (event.player instanceof EntityPlayer) {
            if (status == UpdateStatus.OUTDATED) {
                final String msg = StatCollector.translateToLocalFormatted("msg.NewVersionAvailable.restructured",
                        Restructured.MOD_NAME, currentVersion, CURSE_PROJECT_NAME);
                final IChatComponent component = IChatComponent.Serializer.jsonToComponent(msg);
                event.player.addChatMessage(component);
            }
        }
    }

    private static void versionCheck() {
        try {

            String location = REMOTE_VERSION_FILE;
            HttpURLConnection conn = null;
            while (location != null && !location.isEmpty()) {
                URL url = new URL(location);

                if (conn != null) {
                    conn.disconnect();
                }

                conn = (HttpURLConnection) url.openConnection();
                conn.setRequestProperty("User-Agent",
                        "Mozilla/5.0 (Windows; U; Windows NT 6.0; ru; rv:1.9.0.11) Gecko/2009060215 Firefox/3.0.11 (.NET CLR 3.5.30729)");
                conn.connect();
                location = conn.getHeaderField("Location");
            }

            if (conn == null) {
                throw new NullPointerException();
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String line;
            while ((line = reader.readLine()) != null) {
                String[] tokens = line.split(":");
                if (mcVersion.matches(tokens[0])) {
                    currentVersion = new SoftwareVersion(tokens[1]);
                    break;
                }
            }

            status = UpdateStatus.CURRENT;
            if (modVersion.compareTo(currentVersion) < 0)
                status = UpdateStatus.OUTDATED;

            conn.disconnect();
            reader.close();

        } catch (Exception e) {
            ModLog.warn("Unable to read remote version data", e);
            status = UpdateStatus.COMM_ERROR;
        }
    }

    @Override
    public void run() {

        int count = 0;

        ModLog.info("Checking for newer mod version");

        try {

            do {
                if (count > 0) {
                    ModLog.info("Awaiting attempt %d", count);
                    Thread.sleep(VERSION_CHECK_INTERVAL);
                }
                versionCheck();
                count++;
            } while (count < VERSION_CHECK_RETRIES && status == UpdateStatus.COMM_ERROR);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        switch (status) {
        case COMM_ERROR:
            ModLog.warn("Version check failed");
            break;
        case CURRENT:
            ModLog.info("Restructured version [%s] is the same or newer than the current version [%s]", modVersion,
                    currentVersion);
            break;
        case OUTDATED:
            ModLog.warn("Using outdated version [" + modVersion + "] for Minecraft " + mcVersion
                    + ". Consider updating to " + currentVersion + ".");
            break;
        case UNKNOWN:
            ModLog.warn("Unknown version check status!");
            break;
        default:
            break;

        }
    }
}