diff --git a/src/main/java/li/cil/oc/Settings.scala b/src/main/java/li/cil/oc/Settings.scala index 566d9d485..91e49dcd7 100644 --- a/src/main/java/li/cil/oc/Settings.scala +++ b/src/main/java/li/cil/oc/Settings.scala @@ -175,6 +175,7 @@ class Settings(config: Config) { OpenComputers.log.warning("Bad number of Remote Terminal counts, ignoring.") Array(2, 4, 8) } + val updateCheck = config.getBoolean("misc.updateCheck") } diff --git a/src/main/java/li/cil/oc/UpdateCheck.scala b/src/main/java/li/cil/oc/UpdateCheck.scala index 120731c8c..bee6be867 100644 --- a/src/main/java/li/cil/oc/UpdateCheck.scala +++ b/src/main/java/li/cil/oc/UpdateCheck.scala @@ -1,66 +1,68 @@ package li.cil.oc -import argo.jdom.{JsonNode, JsonRootNode, JdomParser} +import argo.jdom.JdomParser +import cpw.mods.fml.common.Loader +import cpw.mods.fml.common.versioning.ComparableVersion +import java.io.InputStreamReader import java.net.{HttpURLConnection, URL} -import java.io.{InputStreamReader, BufferedReader} -import argo.saj.InvalidSyntaxException -import cpw.mods.fml.common.network.Player +import java.util.logging.Level import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.util.ChatMessageComponent import scala.collection.convert.WrapAsScala._ -import cpw.mods.fml.common.versioning.{VersionParser, DefaultArtifactVersion} -class UpdateCheck(var version: String, player: EntityPlayerMP) extends Thread { - start() +object UpdateCheck { + val releasesUrl = new URL("https://api.github.com/repos/MightyPirates/OpenComputers/releases") - override def run() { + val version = Loader.instance.getIndexedModList.get("OpenComputers").getVersion + val majorVersion = version.split('.')(0).toInt + + // Lazy to make initialize() execute once from the first thread that tries to + // read it. If other threads are spawned while it's running they will wait, + // because lazy initializers are synchronized. + lazy val result = initialize() + + def checkForPlayer(player: EntityPlayerMP) = if (Settings.get.updateCheck) { + new Thread() { + override def run() = result(player) + }.start() + } + + def initialize(): EntityPlayerMP => Unit = { try { - val jsonString: String = getHTML("https://api.github.com/repos/MightyPirates/OpenComputers/releases") - val parser: JdomParser = new JdomParser - val n = parser.parse(jsonString) - val currentNumbers = version.split("\\.") - val mcVersion = currentNumbers(0) - val ocVersionCurrent = currentNumbers(1).toInt - val minorVersionCurrent = currentNumbers(2).toInt - - n.getArrayNode().find(node => node.getStringValue("tag_name").split("v")(1).split("\\.")(0).equals(mcVersion) && !node.getBooleanValue("prerelease")) match { - case Some(node) => - val version = node.getStringValue("tag_name") - val versions = version.split("v")(1).split("\\.") - val ocVersion = versions(1).toInt - val minorVersion = versions(2).toInt - if (ocVersionCurrent < ocVersion || (ocVersion == ocVersionCurrent && minorVersion > minorVersionCurrent)) { - player.sendChatToPlayer(ChatMessageComponent.createFromText("[OpenComputers] A new Version " + version + " is available!")) + OpenComputers.log.info("Starting version check.") + releasesUrl.openConnection match { + case conn: HttpURLConnection => + conn.setRequestMethod("GET") + conn.setDoOutput(false) + val json = new JdomParser().parse(new InputStreamReader(conn.getInputStream)) + val candidates = json.getElements.filter(node => matchesVersion(node.getStringValue("tag_name")) && !node.getBooleanValue("prerelease")) + if (candidates.nonEmpty) { + val newest = candidates.maxBy(node => new ComparableVersion(node.getStringValue("tag_name").stripPrefix("v"))) + val tag = newest.getStringValue("tag_name") + val tagVersion = new ComparableVersion(tag.stripPrefix("v")) + val modVersion = new ComparableVersion(version) + if (tagVersion.compareTo(modVersion) > 0) { + OpenComputers.log.info(s"A newer version is available: ($tag})") + return (player: EntityPlayerMP) => + player.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addFormatted(Settings.namespace + "gui.Chat.NewVersion", tag)) + } } - case _ => + OpenComputers.log.info("Running the latest version.") + case _ => OpenComputers.log.warning("Failed to connect to Github.") } } catch { - //Ignore not connected exceptions and stuff - case e: Exception => + case t: Throwable => OpenComputers.log.log(Level.WARNING, "Update check failed.", t) } + // Nothing to do, return dummy callback. + p => } - - def getHTML(urlToRead: String): String = { - var url: URL = null - var conn: HttpURLConnection = null - var rd: BufferedReader = null - var line: String = null - val builder: StringBuilder = new StringBuilder - - url = new URL(urlToRead) - conn = url.openConnection.asInstanceOf[HttpURLConnection] - conn.setRequestMethod("GET") - rd = new BufferedReader(new InputStreamReader(conn.getInputStream)) - while ( { - line = rd.readLine - line - } != null) { - builder.append(line.trim).append("\n") - } - rd.close() - builder.toString() + def matchesVersion(tag: String) = try { + tag.stripPrefix("v").split('.')(0).toInt == majorVersion + } + catch { + case _: Throwable => false } } diff --git a/src/main/java/li/cil/oc/common/ConnectionHandler.scala b/src/main/java/li/cil/oc/common/ConnectionHandler.scala index 37456609e..83efbbca8 100644 --- a/src/main/java/li/cil/oc/common/ConnectionHandler.scala +++ b/src/main/java/li/cil/oc/common/ConnectionHandler.scala @@ -1,16 +1,15 @@ package li.cil.oc.common -import cpw.mods.fml.common.{ModContainer, Loader} +import cpw.mods.fml.common.Loader import cpw.mods.fml.common.network.{Player, IConnectionHandler} -import li.cil.oc.{UpdateCheck, Settings} import li.cil.oc.util.LuaStateFactory import li.cil.oc.util.mods.ProjectRed +import li.cil.oc.{UpdateCheck, Settings} import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.network.packet.{Packet1Login, NetHandler} import net.minecraft.network.{NetLoginHandler, INetworkManager} import net.minecraft.server.MinecraftServer import net.minecraft.util.ChatMessageComponent -import scala.collection.convert.WrapAsScala._ object ConnectionHandler extends IConnectionHandler { def playerLoggedIn(player: Player, netHandler: NetHandler, manager: INetworkManager) { @@ -25,8 +24,10 @@ object ConnectionHandler extends IConnectionHandler { if (!Settings.get.pureIgnorePower && !Loader.isModLoaded("UniversalElectricity")) { p.sendChatToPlayer(ChatMessageComponent.createFromText("§aOpenComputers§f: ").addKey(Settings.namespace + "gui.Chat.WarningPower")) } - val mod = Loader.instance.getIndexedModList.get("OpenComputers") - new UpdateCheck(mod.getVersion, p) + // Do update check in local games and for OPs. + if (!MinecraftServer.getServer.isDedicatedServer || MinecraftServer.getServer.getConfigurationManager.isPlayerOpped(p.getCommandSenderName)) { + UpdateCheck.checkForPlayer(p) + } case _ => } } diff --git a/src/main/resources/assets/opencomputers/lang/de_DE.lang b/src/main/resources/assets/opencomputers/lang/de_DE.lang index 2b7bc3535..3f73f76e3 100644 --- a/src/main/resources/assets/opencomputers/lang/de_DE.lang +++ b/src/main/resources/assets/opencomputers/lang/de_DE.lang @@ -86,6 +86,7 @@ oc:gui.Analyzer.RobotXp=§6Erfahrung§f: %s (Stufe %s) oc:gui.Analyzer.StoredEnergy=§6Gespeicherte Energie§f: %s oc:gui.Analyzer.TotalEnergy=§6Insgesamt gespeicherte Energie§f: %s oc:gui.Analyzer.Users=§6Benutzer§f: %s +oc:gui.Chat.NewVersion=Eine neue Version ist verfügbar: %s oc:gui.Chat.WarningLuaFallback=Die native Lua-Implementierung ist nicht verfügbar. Computer können ihren Ausführungszustand nicht speichern. Sie werden automatisch neu starten, sobald ein Chunk neu geladen wird. oc:gui.Chat.WarningPower=Universal Electricity 3 ist nicht verfügbar. Computer, Bildschirme und alle anderen Komponenten werden §lkeine§f Energie benötigen. UE3 wird auch benötigt, um zusätzlich BuildCraft, IndustrialCraft2 und Thermal Expansion zu unterstützen. oc:gui.Chat.WarningProjectRed=Die verwendete Version von Project: Red ist nicht mit OpenComputers kompatibel. Aktualisiere bitte deine Version von Project: Red. diff --git a/src/main/resources/assets/opencomputers/lang/en_US.lang b/src/main/resources/assets/opencomputers/lang/en_US.lang index a1d56e273..01c811145 100644 --- a/src/main/resources/assets/opencomputers/lang/en_US.lang +++ b/src/main/resources/assets/opencomputers/lang/en_US.lang @@ -86,6 +86,7 @@ oc:gui.Analyzer.RobotXp=§6Experience§f: %s (Level %s) oc:gui.Analyzer.StoredEnergy=§6Stored energy§f: %s oc:gui.Analyzer.TotalEnergy=§6Total stored energy§f: %s oc:gui.Analyzer.Users=§6Users§f: %s +oc:gui.Chat.NewVersion=A new version is available: %s oc:gui.Chat.WarningLuaFallback=Native Lua libraries are not available, computers will not be able to persist their state. They will reboot on chunk reloads. oc:gui.Chat.WarningPower=Universal Electricity 3 is not available. Computers, screens and all other components will §lnot§f require energy. Note that UE3 is also used to additionally support BuildCraft, IndustrialCraft2 and Thermal Expansion. oc:gui.Chat.WarningProjectRed=You are using a version of Project: Red that is incompatible with OpenComputers. Try updating your version of Project: Red. diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index e56652212..a7402249c 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -684,5 +684,10 @@ opencomputers { # The number of remote terminals supported by each server tier. terminalsPerTier: [2, 4, 8] + + # Whether to perform an update check and informing local players and OPs + # if a new version is available (contacts Github once the first player + # joins a server / the first map in single player is opened). + updateCheck: true } } \ No newline at end of file