/* * Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine. * * Copyright (c) 2011 - 2014 Michael Andonie and contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ea; import ea.internal.util.Logger; import javax.imageio.ImageIO; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; import java.io.BufferedInputStream; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; /** * Diese Klasse definiert Versions-Konstanten und sorgt für eine About-Box beim Ausführen der * .jar-Datei. * <p/> * TODO: Commit-ID in Jar packen und mit aktueller ID auf GitHub vergleichen * * @author Niklas Keller <me@kelunik.com> */ public class EngineAlpha extends Frame { /** * Der Versionscode des aktuellen Release.<br /> * Rechnung:<br/> * <code> * 10000 * major + 100 * minor + 1 * bugfix * </code> */ public static final int VERSION_CODE = 30200; /** * Format: v(major).(minor).(bugfix) * Beispiel: v3.1.2 */ public static final String VERSION_STRING = "v3.2.0"; /** * Gibt an, ob dieser Release in .jar - Form vorliegt. Ist das der Fall, * ist dieser Wert <code>true</code>, sonst ist er <code>false</code>. */ public static final boolean IS_JAR; /** * Zeitpunkt, an dem diese Jar-Datei erzeugt wurde, falls als Jar-Datei ausgeführt, sonst die * aktuelle Zeit in Sekunden seit dem 01.01.1970 (Unix Timestamp) */ public static final long BUILD_TIME; /** * Statischer Konstruktor. * Ermittelt <code>IS_JAR</code> und <code>BUILD_TIME</code>. */ static { IS_JAR = isJar(); BUILD_TIME = IS_JAR ? getBuildTime() / 1000 : System.currentTimeMillis() / 1000; } /** * Wird debug auf <code>true</code> gesetzt, so werden ausführliche Informationen zu Tickern im * Logger ausgegeben. */ private static boolean debug; /** * Panel, das den Fensterinhalt zeichnet. */ private EngineAlphaPromotion promo; public EngineAlpha () { super("Engine Alpha " + VERSION_STRING); try { setIconImage(ImageIO.read(getClass().getResourceAsStream("/assets/favicon.png"))); } catch (IOException e) { Logger.error(e.getLocalizedMessage()); } this.addWindowListener(new WindowAdapter() { public void windowClosing (WindowEvent e) { promo.shutdown(); setVisible(false); dispose(); System.exit(0); } }); promo = new EngineAlphaPromotion(); } /** * Main-Methode der Engine Alpha. Diese öffnet ein Fenster, das einen Versionsabgleich der * aktuellen Version mit der aktuell verfügbaren Version macht. */ public static void main (String[] args) { new EngineAlpha(); } /** * Gibt an, ob das Programm gerade aus einer Jar heraus gestartet wurde. * @return <code>true</code>, falls ja, sonst <code>false</code>. */ public static boolean isJar () { String className = EngineAlpha.class.getName().replace('.', '/'); String classJar = EngineAlpha.class.getResource("/" + className + ".class").toString(); return classJar.startsWith("jar:"); } /** * Gibt den Namen der Jar-Datei zurück, die gerade ausgeführt wird. * * @return Dateiname der Jar-Datei oder <code>null</code>, falls das Programm nicht über eine * Jar-Datei ausgeführt wird. */ @SuppressWarnings ( "unused" ) public static String getJarName () { String className = EngineAlpha.class.getName().replace('.', '/'); String classJar = EngineAlpha.class.getResource("/" + className + ".class").toString(); if (classJar.startsWith("jar:")) { String vals[] = classJar.split("/"); for (String val : vals) { if (val.contains("!")) { try { return java.net.URLDecoder.decode(val.substring(0, val.length() - 1), "UTF-8"); } catch (Exception e) { return null; } } } } return null; } /** * Gibt an, wann die Jar-Datei erzeugt wurde. * * @return Erzeugungsdatum der Jar-Datei in Sekunden seit dem 01.01.1970 (Unix Timestamp) oder * den aktuellen Timestamp, falls nicht von einer Jar-Datei ausgeführt. */ public static long getBuildTime () { try { String uri = EngineAlpha.class.getName().replace('.', '/') + ".class"; JarURLConnection j = (JarURLConnection) ClassLoader.getSystemResource(uri).openConnection(); long time = j.getJarFile().getEntry("META-INF/MANIFEST.MF").getTime(); return time > 0 ? time : System.currentTimeMillis() / 1000; } catch (Exception e) { return System.currentTimeMillis() / 1000; } } /** * Holt den gesamten Inhalt einer einzelnen Webseite und gibt diesen zurück. * * @param uri * URL, welche geholt werden soll * * @return Response-Body */ private static String getUrlBody (String uri) { // workaround, make sure this is set to false // see http://stackoverflow.com/a/14884941/2373138 System.setProperty("jsse.enableSNIExtension", "false"); BufferedInputStream bis = null; URL url; try { url = new URL(uri); bis = new BufferedInputStream(url.openStream()); StringBuilder builder = new StringBuilder(); byte[] data = new byte[1024]; int read; while ((read = bis.read(data)) != -1) { builder.append(new String(data, 0, read)); } return builder.toString(); } catch (Exception e) { // client may have no internet connection } finally { if (bis != null) { try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } /** * Gibt an, ob sich die Engine im Debug-Modus befindet. * * @return {@code true} falls die Engine im Debug-Modus läuft, sonst {@code false} */ public static boolean isDebug () { return debug; } /** * Ändert den Debug-Status der Engine. * * @param value {@code true}, falls die Engine im Debug-Modus arbeiten soll, sonst {@code false}. */ public static void setDebug (boolean value) { debug = value; } /** * Zeichenebene für den Versionschecker */ private class EngineAlphaPromotion extends Canvas implements Runnable { private Thread thread; private BufferedImage logo; private double alpha = 0; private boolean loading = true; private boolean alive = true; private int version_stable = -1; public EngineAlphaPromotion () { EngineAlpha parent = EngineAlpha.this; try { logo = ImageIO.read(getClass().getResource("/assets/logo.png")); } catch (IOException e) { e.printStackTrace(); } setSize(400, 300); setPreferredSize(getSize()); parent.add(this); parent.pack(); Dimension screen = getToolkit().getScreenSize(); parent.setLocation((screen.width - parent.getWidth()) / 2, (screen.height - parent.getHeight()) / 2); parent.setVisible(true); thread = new Thread(this) {{ setDaemon(true); }}; thread.start(); new Thread() { { setDaemon(true); } public void run () { try { String body = getUrlBody("https://raw.githubusercontent.com/engine-alpha/engine-alpha/master/VERSION_STABLE").trim(); version_stable = Integer.parseInt(body); } catch (Exception e) { version_stable = -1; } loading = false; } }.start(); } public void run () { createBufferStrategy(2); BufferStrategy bs = getBufferStrategy(); Graphics2D g = (Graphics2D) bs.getDrawGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); long lastTime, currTime = System.currentTimeMillis(); while (alive) { lastTime = currTime; currTime = System.currentTimeMillis(); update(currTime - lastTime); render(g); bs.show(); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } } private void update (long passedTime) { alpha += passedTime * .01; alpha %= 360; } public void render (Graphics2D g) { g.setFont(new Font("SansSerif", Font.ITALIC, 14)); FontMetrics fm = g.getFontMetrics(); g.setColor(new Color(250, 250, 250)); g.fillRect(0, 0, getWidth(), getHeight()); g.drawImage(logo, (getWidth() - logo.getWidth()) / 2, 45, null); if (loading) { g.setColor(new Color(0, 0, 0, 150)); g.fillOval((int) (getWidth() / 2 + 8 * Math.cos(alpha)) - 2, (int) (getHeight() - 80 + 8 * Math.sin(alpha)) - 2, 4, 4); g.fillOval((int) (getWidth() / 2 + 8 * Math.cos(180 + alpha)) - 2, (int) (getHeight() - 80 + 8 * Math.sin(180 + alpha)) - 2, 4, 4); g.drawLine((int) (getWidth() / 2 + 8 * Math.cos(alpha)), (int) (getHeight() - 80 + 8 * Math.sin(alpha)), (int) (getWidth() / 2 + 8 * Math.cos(180 + alpha)), (int) (getHeight() - 80 + 8 * Math.sin(180 + alpha))); } else { String message = ""; Color color = new Color(30, 30, 30); if (version_stable == -1) { message = "Server für Versionsabgleich nicht erreichbar."; } else if (version_stable == VERSION_CODE) { message = "Dies ist die aktuelle Stable-Version."; color = new Color(50, 200, 25); } else if (VERSION_CODE < version_stable) { message = "Es ist eine neue Stable-Version verfügbar!"; color = new Color(200, 50, 0); } g.setColor(color); g.drawString(message, (getWidth() - fm.stringWidth(message)) / 2, getHeight() - 70); } Date date = new Date(BUILD_TIME * 1000); SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss z"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); g.setColor(new Color(100, 100, 100)); String str = "Build #" + VERSION_CODE + " " + sdf.format(date); g.drawString(str, (getWidth() - fm.stringWidth(str)) / 2, getHeight() - 40); } public void shutdown () { this.alive = false; try { thread.join(); } catch (Exception e) { e.printStackTrace(); } } } }