Java tutorial
/***************************************************************************\ * Copyright 2016 [Lyonlancer5] * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, software * * distributed under the License is distributed on an "AS IS" BASIS, * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * * limitations under the License. * \***************************************************************************/ package net.lyonlancer5.mcmp.karasu.util; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.google.common.collect.Maps; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Loader; import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.relauncher.Side; import net.minecraft.event.ClickEvent; import net.minecraft.util.ChatComponentText; public class VersionIdentifier extends Thread { private static final VersionIdentifier instance = new VersionIdentifier(); private static final Logger LOGGER = LogManager.getLogger("Project Karasu ~ Version Check"); //Update stuff private final String remoteUpdateLoc; private final File localUpdateLoc; private boolean isLatest = false; private boolean hasChecked = false; private boolean isPre = false; private String latestVersion = ""; private String updateLink = ""; private long date = 0L; //Compatibility stuff private final String remoteCompatLoc; private final File localCompatLoc; private VersionIdentifier() { setName("Project Karasu - Version Check"); remoteUpdateLoc = "https://raw.githubusercontent.com/Lyonlancer5/Project-Karasu/master/update"; localUpdateLoc = new File(Constants.MAIN_DIR, "update"); remoteCompatLoc = "https://raw.githubusercontent.com/Lyonlancer5/Project-Karasu/master/compat"; localCompatLoc = new File(Constants.MAIN_DIR, "compat"); } public static VersionIdentifier instance() { return instance; } /** * Checks if the given version is compatible with the current version running * @param remoteVersion The version we are connecting to * @param remoteSide The side of the target connection */ public boolean isCompatibleVersion(String remoteVersion, Side remoteSide) { Constants.LOGGER.info("Remote version: " + remoteVersion + " @ Side." + remoteSide.name()); Constants.LOGGER.info( "Local version: " + Constants.VERSION + " @ Side." + FMLCommonHandler.instance().getSide().name()); try { if (!localCompatLoc.exists() || (System.currentTimeMillis() - localCompatLoc.lastModified()) > 604800000000L) { ModFileUtils.download(remoteCompatLoc, localCompatLoc); } List<String> lines = IOUtils.readLines(new FileInputStream(localCompatLoc)); for (String s : lines) { String[] pars = s.split(":"); if (pars[0].equalsIgnoreCase(Constants.VERSION)) { for (String oneVer : pars[1].split(",")) { if (oneVer.equals(remoteVersion)) return true; else continue; } } } } catch (Exception e) { //NOISE } Map<String, Integer> localv = identify(Constants.VERSION), remotev = identify(remoteVersion); if (remotev.get("major") != localv.get("major")) return false; if (remotev.get("minor") != localv.get("minor")) return false; if (remotev.get("revision") != localv.get("revision")) return false; if (remotev.get("is-pre-release") == localv.get("is-pre-release")) { return true; } else { return false; } } /** * Identifies the version via the given {@code format} * and returns a map instance with the following keys: * * "major": the major version * "minor": the minor version * "revision": the revision no. * "build": the build no. * "pre-release-id": the pre-release ID, can be negative if it is a stable version * "is-pre-release": determines whether the format given is a pre-release or not (0 for false, 1 for true) */ public static Map<String, Integer> identify(String format) { HashMap<String, Integer> vars = Maps.newHashMap(); String[] var0 = format.split("\\."); if (var0.length < 4) return null; int major = Integer.parseInt(var0[0]), minor = Integer.parseInt(var0[1]), revision = Integer.parseInt(var0[2]); int build, preId = 0; if (var0[3].contains("-pre")) { String[] var1 = var0[3].split("-"); build = Integer.parseInt(var1[0]); preId = Integer.parseInt(var1[1].substring(3)); vars.put("pre-release-id", Integer.valueOf(preId)); vars.put("is-pre-release", Integer.valueOf(1)); } else { build = Integer.parseInt(var0[3]); vars.put("pre-release-id", Integer.valueOf(-1)); vars.put("is-pre-release", Integer.valueOf(0)); } vars.put("major", major); vars.put("minor", minor); vars.put("revision", revision); vars.put("build", build); return vars; } public void run() { if (!ModFileUtils.isDevEnv) { //MARKER: Do check whether the thread is restarted, just in case... if (!hasChecked) { //MARKER: First off, download a local copy of the cache try { LOGGER.info("Querying master repository"); ModFileUtils.download(remoteUpdateLoc, localUpdateLoc); } catch (IOException e) { LOGGER.warn("Failed to query remote repository"); isLatest = true; hasChecked = true; return; } //MARKER: Load the cache and get the latest version try { LOGGER.info("Loading update cache"); //mcversion:<stable>:version:date_in_long:url //mcversion:<pre>:version:date_in_long:url List<String> lines = IOUtils.readLines(new FileInputStream(localUpdateLoc)); for (String s : lines) { String[] params = s.split(":"); if (params[0].equals(Loader.MC_VERSION)) { if (isPre && params[1].equals("pre")) { latestVersion = params[2]; date = Long.parseLong(params[3]); updateLink = params[4]; } else if (!isPre && params[1].equals("stable")) { latestVersion = params[2]; date = Long.parseLong(params[3]); updateLink = params[4]; } break; } } } catch (IOException e) { LOGGER.warn("Failed to load local cache", e); isLatest = true; hasChecked = true; return; } //MARKER: Then compare EACH Map<String, Integer> local = identify(Constants.VERSION), remote = identify(latestVersion); if (!isPre) { if (remote.get("major") > local.get("major") || remote.get("minor") > local.get("minor") || remote.get("revision") > local.get("revision") || remote.get("build") > local.get("build")) { LOGGER.info("A new update for Project Karasu is available!"); LOGGER.info("v" + latestVersion + " - released on " + ((new SimpleDateFormat("MM/dd/yyyy")).format(new Date(date)))); hasChecked = true; isLatest = false; return; } } else { if (remote.get("pre-release-id") > local.get("pre-release-id")) { LOGGER.info("A pre-release update has been released"); LOGGER.info("v" + latestVersion + " - released on " + ((new SimpleDateFormat("MM/dd/yyyy")).format(new Date(date)))); hasChecked = true; isLatest = false; return; } } isLatest = true; hasChecked = true; LOGGER.info("No updates found"); } } } public static class UpdateNotifier { private boolean hasNotified = false; public void notify(TickEvent.PlayerTickEvent event) { if (!hasNotified && event.player.worldObj.isRemote && !VersionIdentifier.instance().isLatest) { ChatComponentText cct = new ChatComponentText("\u00a76Project Karasu has an update - Get it here!"); cct.getChatStyle().setChatClickEvent( new ClickEvent(ClickEvent.Action.OPEN_URL, VersionIdentifier.instance().updateLink)); event.player.addChatMessage(cct); hasNotified = true; } if (!hasNotified && event.player.worldObj.isRemote && VersionIdentifier.instance().isLatest) { ChatComponentText chat = new ChatComponentText( "\u00a76Project Karasu - The Backporting Initiative is loaded - v" + Constants.VERSION); event.player.addChatMessage(chat); hasNotified = true; } } public static void register() { if (!ModFileUtils.isDevEnv) { Constants.LOGGER.info("Running update thread"); FMLCommonHandler.instance().bus().register(new UpdateNotifier()); } } } }