diff options
author | cflip <cflip@cflip.net> | 2023-08-24 08:44:43 -0600 |
---|---|---|
committer | cflip <cflip@cflip.net> | 2023-08-24 08:44:54 -0600 |
commit | 5a7d6a1d7efb249fe3360330704e8ae647ae3a8f (patch) | |
tree | a00e1965cc5a9788e4229c9ee87f736bc5b6bc71 /src |
Import decompiled source from 2010 launcher jar
Diffstat (limited to 'src')
-rw-r--r-- | src/net/minecraft/GameUpdater.java | 707 | ||||
-rw-r--r-- | src/net/minecraft/Launcher.java | 218 | ||||
-rw-r--r-- | src/net/minecraft/LauncherFrame.java | 140 | ||||
-rw-r--r-- | src/net/minecraft/LoginForm.java | 380 | ||||
-rw-r--r-- | src/net/minecraft/MinecraftLauncher.java | 40 | ||||
-rw-r--r-- | src/net/minecraft/Util.java | 104 | ||||
-rw-r--r-- | src/net/minecraft/dirt.png | bin | 0 -> 360 bytes | |||
-rw-r--r-- | src/net/minecraft/favicon.png | bin | 0 -> 1374 bytes |
8 files changed, 1589 insertions, 0 deletions
diff --git a/src/net/minecraft/GameUpdater.java b/src/net/minecraft/GameUpdater.java new file mode 100644 index 0000000..1fb7f50 --- /dev/null +++ b/src/net/minecraft/GameUpdater.java @@ -0,0 +1,707 @@ +package net.minecraft; + +import java.applet.Applet; +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.*; +import java.security.*; +import java.security.cert.Certificate; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Pack200; + + +public class GameUpdater implements Runnable { + public static final int STATE_INIT = 1; + public static final int STATE_DETERMINING_PACKAGES = 2; + public static final int STATE_CHECKING_CACHE = 3; + public static final int STATE_DOWNLOADING = 4; + public static final int STATE_EXTRACTING_PACKAGES = 5; + public static final int STATE_UPDATING_CLASSPATH = 6; + public static final int STATE_SWITCHING_APPLET = 7; + public static final int STATE_INITIALIZE_REAL_APPLET = 8; + public static final int STATE_START_REAL_APPLET = 9; + public static final int STATE_DONE = 10; + public int percentage; + public int currentSizeDownload; + public int totalSizeDownload; + public int currentSizeExtract; + public int totalSizeExtract; + protected URL[] urlList; + private static ClassLoader classLoader; + protected Thread loaderThread; + protected Thread animationThread; + public boolean fatalError; + public String fatalErrorDescription; + protected String subtaskMessage = ""; + protected int state = 1; + + protected boolean lzmaSupported = false; + + protected boolean pack200Supported = false; + protected String[] genericErrorMessage = new String[]{"An error occured while loading the applet.", "Please contact support to resolve this issue.", "<placeholder for error message>"}; + + protected boolean certificateRefused; + + protected String[] certificateRefusedMessage = new String[]{"Permissions for Applet Refused.", "Please accept the permissions dialog to allow", "the applet to continue the loading process."}; + + protected static boolean natives_loaded = false; + private final String latestVersion; + private final String mainGameUrl; + + public GameUpdater(String latestVersion, String mainGameUrl) { + this.latestVersion = latestVersion; + this.mainGameUrl = mainGameUrl; + } + + public void init() { + this.state = 1; + + try { + Class.forName("LZMA.LzmaInputStream"); + this.lzmaSupported = true; + } catch (Throwable throwable) { + } + + + try { + Pack200.class.getSimpleName(); + this.pack200Supported = true; + } catch (Throwable throwable) { + } + } + + + private String generateStacktrace(Exception exception) { + Writer result = new StringWriter(); + PrintWriter printWriter = new PrintWriter(result); + exception.printStackTrace(printWriter); + return result.toString(); + } + + + protected String getDescriptionForState() { + switch (this.state) { + case 1: + return "Initializing loader"; + case 2: + return "Determining packages to load"; + case 3: + return "Checking cache for existing files"; + case 4: + return "Downloading packages"; + case 5: + return "Extracting downloaded packages"; + case 6: + return "Updating classpath"; + case 7: + return "Switching applet"; + case 8: + return "Initializing real applet"; + case 9: + return "Starting real applet"; + case 10: + return "Done loading"; + } + return "unknown state"; + } + + + protected String trimExtensionByCapabilities(String file) { + if (!this.pack200Supported) { + file = file.replaceAll(".pack", ""); + } + + if (!this.lzmaSupported) { + file = file.replaceAll(".lzma", ""); + } + return file; + } + + protected void loadJarURLs() throws Exception { + this.state = 2; + String jarList = "lwjgl.jar, jinput.jar, lwjgl_util.jar, " + this.mainGameUrl; + jarList = trimExtensionByCapabilities(jarList); + + StringTokenizer jar = new StringTokenizer(jarList, ", "); + int jarCount = jar.countTokens() + 1; + + this.urlList = new URL[jarCount]; + + URL path = new URL("http://s3.amazonaws.com/MinecraftDownload/"); + + for (int i = 0; i < jarCount - 1; i++) { + this.urlList[i] = new URL(path, jar.nextToken()); + } + + String osName = System.getProperty("os.name"); + String nativeJar = null; + + if (osName.startsWith("Win")) { + nativeJar = "windows_natives.jar.lzma"; + } else if (osName.startsWith("Linux")) { + nativeJar = "linux_natives.jar.lzma"; + } else if (osName.startsWith("Mac")) { + nativeJar = "macosx_natives.jar.lzma"; + } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { + nativeJar = "solaris_natives.jar.lzma"; + } else { + fatalErrorOccured("OS (" + osName + ") not supported", null); + } + + if (nativeJar == null) { + fatalErrorOccured("no lwjgl natives files found", null); + } else { + nativeJar = trimExtensionByCapabilities(nativeJar); + this.urlList[jarCount - 1] = new URL(path, nativeJar); + } + } + + + public void run() { + init(); + this.state = 3; + + this.percentage = 5; + + try { + loadJarURLs(); + + String path = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() { + public Object run() throws Exception { + return Util.getWorkingDirectory() + File.separator + "bin" + File.separator; + } + }); + + File dir = new File(path); + + if (!dir.exists()) { + dir.mkdirs(); + } + + if (this.latestVersion != null) { + File versionFile = new File(dir, "version"); + + boolean cacheAvailable = false; + if (versionFile.exists() && ( + this.latestVersion.equals("-1") || this.latestVersion.equals(readVersionFile(versionFile)))) { + cacheAvailable = true; + this.percentage = 90; + } + + + if (!cacheAvailable) { + downloadJars(path); + extractJars(path); + extractNatives(path); + + if (this.latestVersion != null) { + this.percentage = 90; + writeVersionFile(versionFile, this.latestVersion); + } + } + } + + updateClassPath(dir); + this.state = 10; + } catch (AccessControlException ace) { + fatalErrorOccured(ace.getMessage(), ace); + this.certificateRefused = true; + } catch (Exception e) { + fatalErrorOccured(e.getMessage(), e); + } finally { + this.loaderThread = null; + } + } + + protected String readVersionFile(File file) throws Exception { + DataInputStream dis = new DataInputStream(new FileInputStream(file)); + String version = dis.readUTF(); + dis.close(); + return version; + } + + protected void writeVersionFile(File file, String version) throws Exception { + DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); + dos.writeUTF(version); + dos.close(); + } + + + protected void updateClassPath(File dir) throws Exception { + this.state = 6; + + this.percentage = 95; + + URL[] urls = new URL[this.urlList.length]; + for (int i = 0; i < this.urlList.length; i++) { + urls[i] = (new File(dir, getJarName(this.urlList[i]))).toURI().toURL(); + } + + if (classLoader == null) { + classLoader = new URLClassLoader(urls) { + protected PermissionCollection getPermissions(CodeSource codesource) { + PermissionCollection perms = null; + + + try { + Method method = SecureClassLoader.class.getDeclaredMethod("getPermissions", CodeSource.class); + method.setAccessible(true); + perms = (PermissionCollection) method.invoke(getClass().getClassLoader(), new Object[]{codesource}); + + String host = "www.minecraft.net"; + + if (host != null && host.length() > 0) { + perms.add(new SocketPermission(host, "connect,accept")); + } else { + codesource.getLocation().getProtocol().equals("file"); + } + + + perms.add(new FilePermission("<<ALL FILES>>", "read")); + } catch (Exception e) { + e.printStackTrace(); + } + + return perms; + } + }; + } + + String path = dir.getAbsolutePath(); + if (!path.endsWith(File.separator)) path = path + File.separator; + unloadNatives(path); + + System.setProperty("org.lwjgl.librarypath", path + "natives"); + System.setProperty("net.java.games.input.librarypath", path + "natives"); + + natives_loaded = true; + } + + + private void unloadNatives(String nativePath) { + if (!natives_loaded) { + return; + } + + try { + Field field = ClassLoader.class.getDeclaredField("loadedLibraryNames"); + field.setAccessible(true); + Vector<String> libs = (Vector<String>) field.get(getClass().getClassLoader()); + + String path = (new File(nativePath)).getCanonicalPath(); + + for (int i = 0; i < libs.size(); i++) { + String s = libs.get(i); + + if (s.startsWith(path)) { + libs.remove(i); + i--; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + public Applet createApplet() throws ClassNotFoundException, InstantiationException, IllegalAccessException { + Class<Applet> appletClass = (Class) classLoader.loadClass("net.minecraft.client.MinecraftApplet"); + return appletClass.newInstance(); + } + + + protected void downloadJars(String path) throws Exception { + this.state = 4; + + + int[] fileSizes = new int[this.urlList.length]; + + + for (int i = 0; i < this.urlList.length; i++) { + System.out.println(this.urlList[i]); + URLConnection urlconnection = this.urlList[i].openConnection(); + urlconnection.setDefaultUseCaches(false); + if (urlconnection instanceof HttpURLConnection) { + ((HttpURLConnection) urlconnection).setRequestMethod("HEAD"); + } + fileSizes[i] = urlconnection.getContentLength(); + this.totalSizeDownload += fileSizes[i]; + } + + int initialPercentage = this.percentage = 10; + + + byte[] buffer = new byte[65536]; + for (int j = 0; j < this.urlList.length; j++) { + + int unsuccessfulAttempts = 0; + int maxUnsuccessfulAttempts = 3; + boolean downloadFile = true; + + + while (downloadFile) { + downloadFile = false; + + URLConnection urlconnection = this.urlList[j].openConnection(); + + if (urlconnection instanceof HttpURLConnection) { + urlconnection.setRequestProperty("Cache-Control", "no-cache"); + urlconnection.connect(); + } + + String currentFile = getFileName(this.urlList[j]); + InputStream inputstream = getJarInputStream(currentFile, urlconnection); + FileOutputStream fos = new FileOutputStream(path + currentFile); + + + long downloadStartTime = System.currentTimeMillis(); + int downloadedAmount = 0; + int fileSize = 0; + String downloadSpeedMessage = ""; + int bufferSize; + while ((bufferSize = inputstream.read(buffer, 0, buffer.length)) != -1) { + fos.write(buffer, 0, bufferSize); + this.currentSizeDownload += bufferSize; + fileSize += bufferSize; + this.percentage = initialPercentage + this.currentSizeDownload * 45 / this.totalSizeDownload; + this.subtaskMessage = "Retrieving: " + currentFile + " " + (this.currentSizeDownload * 100 / this.totalSizeDownload) + "%"; + + downloadedAmount += bufferSize; + long timeLapse = System.currentTimeMillis() - downloadStartTime; + + if (timeLapse >= 1000L) { + + float downloadSpeed = downloadedAmount / (float) timeLapse; + + downloadSpeed = (int) (downloadSpeed * 100.0F) / 100.0F; + + downloadSpeedMessage = " @ " + downloadSpeed + " KB/sec"; + + downloadedAmount = 0; + + downloadStartTime += 1000L; + } + + this.subtaskMessage = this.subtaskMessage + downloadSpeedMessage; + } + + inputstream.close(); + fos.close(); + + + if (urlconnection instanceof HttpURLConnection && + fileSize != fileSizes[j]) { + if (fileSizes[j] > 0) { + + + unsuccessfulAttempts++; + + if (unsuccessfulAttempts < maxUnsuccessfulAttempts) { + downloadFile = true; + this.currentSizeDownload -= fileSize; + continue; + } + throw new Exception("failed to download " + currentFile); + } + } + } + } + + this.subtaskMessage = ""; + } + + + protected InputStream getJarInputStream(String currentFile, final URLConnection urlconnection) throws Exception { + final InputStream[] is = new InputStream[1]; + + + for (int j = 0; j < 3 && is[0] == null; j++) { + Thread t = new Thread() { + public void run() { + try { + is[0] = urlconnection.getInputStream(); + } catch (IOException iOException) { + } + } + }; + + + t.setName("JarInputStreamThread"); + t.start(); + + int iterationCount = 0; + while (is[0] == null && iterationCount++ < 5) { + try { + t.join(1000L); + } catch (InterruptedException interruptedException) { + } + } + + + if (is[0] == null) { + try { + t.interrupt(); + t.join(); + } catch (InterruptedException interruptedException) { + } + } + } + + + if (is[0] == null) { + if (currentFile.equals("minecraft.jar")) { + throw new Exception("Unable to download " + currentFile); + } + throw new Exception("Unable to download " + currentFile); + } + + + return is[0]; + } + + + protected void extractLZMA(String in, String out) throws Exception { + File f = new File(in); + FileInputStream fileInputHandle = new FileInputStream(f); + + + Class<?> clazz = Class.forName("LZMA.LzmaInputStream"); + Constructor<?> constructor = clazz.getDeclaredConstructor(InputStream.class); + InputStream inputHandle = (InputStream) constructor.newInstance(new Object[]{fileInputHandle}); + + + OutputStream outputHandle = new FileOutputStream(out); + + byte[] buffer = new byte[16384]; + + int ret = inputHandle.read(buffer); + while (ret >= 1) { + outputHandle.write(buffer, 0, ret); + ret = inputHandle.read(buffer); + } + + inputHandle.close(); + outputHandle.close(); + + outputHandle = null; + inputHandle = null; + + + f.delete(); + } + + + protected void extractPack(String in, String out) throws Exception { + File f = new File(in); + FileOutputStream fostream = new FileOutputStream(out); + JarOutputStream jostream = new JarOutputStream(fostream); + + Pack200.Unpacker unpacker = Pack200.newUnpacker(); + unpacker.unpack(f, jostream); + jostream.close(); + + + f.delete(); + } + + + protected void extractJars(String path) throws Exception { + this.state = 5; + + float increment = 10.0F / this.urlList.length; + + for (int i = 0; i < this.urlList.length; i++) { + this.percentage = 55 + (int) (increment * (i + 1)); + String filename = getFileName(this.urlList[i]); + + if (filename.endsWith(".pack.lzma")) { + this.subtaskMessage = "Extracting: " + filename + " to " + filename.replaceAll(".lzma", ""); + extractLZMA(path + filename, path + filename.replaceAll(".lzma", "")); + + this.subtaskMessage = "Extracting: " + filename.replaceAll(".lzma", "") + " to " + filename.replaceAll(".pack.lzma", ""); + extractPack(path + filename.replaceAll(".lzma", ""), path + filename.replaceAll(".pack.lzma", "")); + } else if (filename.endsWith(".pack")) { + this.subtaskMessage = "Extracting: " + filename + " to " + filename.replace(".pack", ""); + extractPack(path + filename, path + filename.replace(".pack", "")); + } else if (filename.endsWith(".lzma")) { + this.subtaskMessage = "Extracting: " + filename + " to " + filename.replace(".lzma", ""); + extractLZMA(path + filename, path + filename.replace(".lzma", "")); + } + } + } + + + protected void extractNatives(String path) throws Exception { + this.state = 5; + + int initialPercentage = this.percentage; + + String nativeJar = getJarName(this.urlList[this.urlList.length - 1]); + + Certificate[] certificate = Launcher.class.getProtectionDomain().getCodeSource().getCertificates(); + + if (certificate == null) { + URL location = Launcher.class.getProtectionDomain().getCodeSource().getLocation(); + + JarURLConnection jurl = (JarURLConnection) (new URL("jar:" + location.toString() + "!/net/minecraft/Launcher.class")).openConnection(); + jurl.setDefaultUseCaches(true); + try { + certificate = jurl.getCertificates(); + } catch (Exception exception) { + } + } + + + File nativeFolder = new File(path + "natives"); + if (!nativeFolder.exists()) { + nativeFolder.mkdir(); + } + + JarFile jarFile = new JarFile(path + nativeJar, true); + Enumeration<JarEntry> entities = jarFile.entries(); + + this.totalSizeExtract = 0; + + + while (entities.hasMoreElements()) { + JarEntry entry = entities.nextElement(); + + + if (entry.isDirectory() || entry.getName().indexOf('/') != -1) { + continue; + } + this.totalSizeExtract = (int) (this.totalSizeExtract + entry.getSize()); + } + + this.currentSizeExtract = 0; + + entities = jarFile.entries(); + + while (entities.hasMoreElements()) { + JarEntry entry = entities.nextElement(); + + if (entry.isDirectory() || entry.getName().indexOf('/') != -1) { + continue; + } + + File file = new File(path + "natives" + File.separator + entry.getName()); + if (file.exists() && + !file.delete()) { + continue; + } + + + InputStream in = jarFile.getInputStream(jarFile.getEntry(entry.getName())); + OutputStream out = new FileOutputStream(path + "natives" + File.separator + entry.getName()); + + + byte[] buffer = new byte[65536]; + int bufferSize; + while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) { + out.write(buffer, 0, bufferSize); + this.currentSizeExtract += bufferSize; + + this.percentage = initialPercentage + this.currentSizeExtract * 20 / this.totalSizeExtract; + this.subtaskMessage = "Extracting: " + entry.getName() + " " + (this.currentSizeExtract * 100 / this.totalSizeExtract) + "%"; + } + + validateCertificateChain(certificate, entry.getCertificates()); + + in.close(); + out.close(); + } + this.subtaskMessage = ""; + + jarFile.close(); + + File f = new File(path + nativeJar); + f.delete(); + } + + + protected static void validateCertificateChain(Certificate[] ownCerts, Certificate[] native_certs) throws Exception { + if (ownCerts == null) + return; + if (native_certs == null) + throw new Exception("Unable to validate certificate chain. Native entry did not have a certificate chain at all"); + + if (ownCerts.length != native_certs.length) + throw new Exception("Unable to validate certificate chain. Chain differs in length [" + ownCerts.length + " vs " + native_certs.length + "]"); + + for (int i = 0; i < ownCerts.length; i++) { + if (!ownCerts[i].equals(native_certs[i])) { + throw new Exception("Certificate mismatch: " + ownCerts[i] + " != " + native_certs[i]); + } + } + } + + protected String getJarName(URL url) { + String fileName = url.getFile(); + + if (fileName.contains("?")) { + fileName = fileName.substring(0, fileName.indexOf("?")); + } + if (fileName.endsWith(".pack.lzma")) { + fileName = fileName.replaceAll(".pack.lzma", ""); + } else if (fileName.endsWith(".pack")) { + fileName = fileName.replaceAll(".pack", ""); + } else if (fileName.endsWith(".lzma")) { + fileName = fileName.replaceAll(".lzma", ""); + } + + return fileName.substring(fileName.lastIndexOf('/') + 1); + } + + protected String getFileName(URL url) { + String fileName = url.getFile(); + if (fileName.contains("?")) { + fileName = fileName.substring(0, fileName.indexOf("?")); + } + return fileName.substring(fileName.lastIndexOf('/') + 1); + } + + protected void fatalErrorOccured(String error, Exception e) { + e.printStackTrace(); + this.fatalError = true; + this.fatalErrorDescription = "Fatal error occured (" + this.state + "): " + error; + System.out.println(this.fatalErrorDescription); + if (e != null) { + System.out.println(generateStacktrace(e)); + } + } + + + public boolean canPlayOffline() { + try { + String path = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() { + public Object run() throws Exception { + return Util.getWorkingDirectory() + File.separator + "bin" + File.separator; + } + }); + + File dir = new File(path); + if (!dir.exists()) return false; + + dir = new File(dir, "version"); + if (!dir.exists()) return false; + + if (dir.exists()) { + String version = readVersionFile(dir); + if (version != null && version.length() > 0) { + return true; + } + } + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return false; + } +} diff --git a/src/net/minecraft/Launcher.java b/src/net/minecraft/Launcher.java new file mode 100644 index 0000000..d2275df --- /dev/null +++ b/src/net/minecraft/Launcher.java @@ -0,0 +1,218 @@ +package net.minecraft; + +import java.applet.Applet; +import java.awt.*; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; + +public class Launcher extends Applet implements Runnable, AppletStub { + public Map<String, String> customParameters = new HashMap<String, String>(); + private static final long serialVersionUID = 1L; + private GameUpdater gameUpdater; + private boolean gameUpdaterStarted = false; + private Applet applet; + private Image bgImage; + private boolean active = false; + private int context = 0; + + private VolatileImage img; + + + public boolean isActive() { + if (this.context == 0) { + this.context = -1; + try { + if (getAppletContext() != null) this.context = 1; + } catch (Exception exception) { + } + } + + if (this.context == -1) return this.active; + return super.isActive(); + } + + + public void init(String userName, String latestVersion, String downloadTicket, String sessionId) { + try { + this.bgImage = ImageIO.read(LoginForm.class.getResource("dirt.png")).getScaledInstance(32, 32, 16); + } catch (IOException e) { + e.printStackTrace(); + } + + this.customParameters.put("username", userName); + this.customParameters.put("sessionid", sessionId); + + this.gameUpdater = new GameUpdater(latestVersion, "minecraft.jar?user=" + userName + "&ticket=" + downloadTicket); + } + + public boolean canPlayOffline() { + return this.gameUpdater.canPlayOffline(); + } + + public void init() { + if (this.applet != null) { + this.applet.init(); + return; + } + init(getParameter("userName"), getParameter("latestVersion"), getParameter("downloadTicket"), getParameter("sessionId")); + } + + public void start() { + if (this.applet != null) { + this.applet.start(); + return; + } + if (this.gameUpdaterStarted) + return; + Thread t = new Thread() { + public void run() { + Launcher.this.gameUpdater.run(); + try { + if (!Launcher.this.gameUpdater.fatalError) { + Launcher.this.replace(Launcher.this.gameUpdater.createApplet()); + } + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + }; + t.setDaemon(true); + t.start(); + + t = new Thread() { + public void run() { + while (Launcher.this.applet == null) { + Launcher.this.repaint(); + try { + Thread.sleep(10L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + }; + t.setDaemon(true); + t.start(); + + this.gameUpdaterStarted = true; + } + + public void stop() { + if (this.applet != null) { + this.active = false; + this.applet.stop(); + } + } + + public void destroy() { + if (this.applet != null) { + this.applet.destroy(); + } + } + + public void replace(Applet applet) { + this.applet = applet; + applet.setStub(this); + applet.setSize(getWidth(), getHeight()); + + setLayout(new BorderLayout()); + add(applet, "Center"); + + applet.init(); + this.active = true; + applet.start(); + validate(); + } + + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g2) { + if (this.applet != null) + return; + int w = getWidth() / 2; + int h = getHeight() / 2; + if (this.img == null || this.img.getWidth() != w || this.img.getHeight() != h) { + this.img = createVolatileImage(w, h); + } + + Graphics g = this.img.getGraphics(); + for (int x = 0; x <= w / 32; x++) { + for (int y = 0; y <= h / 32; y++) + g.drawImage(this.bgImage, x * 32, y * 32, null); + } + g.setColor(Color.LIGHT_GRAY); + + + String msg = "Updating Minecraft"; + if (this.gameUpdater.fatalError) { + msg = "Failed to launch"; + } + + g.setFont(new Font(null, 1, 20)); + FontMetrics fm = g.getFontMetrics(); + g.drawString(msg, w / 2 - fm.stringWidth(msg) / 2, h / 2 - fm.getHeight() * 2); + + g.setFont(new Font(null, 0, 12)); + fm = g.getFontMetrics(); + msg = this.gameUpdater.getDescriptionForState(); + if (this.gameUpdater.fatalError) { + msg = this.gameUpdater.fatalErrorDescription; + } + + g.drawString(msg, w / 2 - fm.stringWidth(msg) / 2, h / 2 + fm.getHeight()); + msg = this.gameUpdater.subtaskMessage; + g.drawString(msg, w / 2 - fm.stringWidth(msg) / 2, h / 2 + fm.getHeight() * 2); + + if (!this.gameUpdater.fatalError) { + g.setColor(Color.black); + g.fillRect(64, h - 64, w - 128 + 1, 5); + g.setColor(new Color(32768)); + g.fillRect(64, h - 64, this.gameUpdater.percentage * (w - 128) / 100, 4); + g.setColor(new Color(2138144)); + g.fillRect(65, h - 64 + 1, this.gameUpdater.percentage * (w - 128) / 100 - 2, 1); + } + + g.dispose(); + + + g2.drawImage(this.img, 0, 0, w * 2, h * 2, null); + } + + + public void run() { + } + + public String getParameter(String name) { + String custom = this.customParameters.get(name); + if (custom != null) return custom; + try { + return super.getParameter(name); + } catch (Exception e) { + this.customParameters.put(name, null); + return null; + } + } + + + public void appletResize(int width, int height) { + } + + public URL getDocumentBase() { + try { + return new URL("http://www.minecraft.net/game/"); + } catch (MalformedURLException e) { + e.printStackTrace(); + + return null; + } + } +} diff --git a/src/net/minecraft/LauncherFrame.java b/src/net/minecraft/LauncherFrame.java new file mode 100644 index 0000000..9d23071 --- /dev/null +++ b/src/net/minecraft/LauncherFrame.java @@ -0,0 +1,140 @@ +package net.minecraft; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.IOException; +import java.net.URLEncoder; + +public class LauncherFrame extends Frame { + public static final int VERSION = 12; + private static final long serialVersionUID = 1L; + + public LauncherFrame() { + super("Minecraft Launcher"); + + setBackground(Color.BLACK); + this.loginForm = new LoginForm(this); + setLayout(new BorderLayout()); + add(this.loginForm, "Center"); + + this.loginForm.setPreferredSize(new Dimension(854, 480)); + pack(); + setLocationRelativeTo(null); + + try { + setIconImage(ImageIO.read(LauncherFrame.class.getResource("favicon.png"))); + } catch (IOException e1) { + e1.printStackTrace(); + } + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent arg0) { + (new Thread() { + public void run() { + try { + Thread.sleep(30000L); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("FORCING EXIT!"); + System.exit(0); + } + }).start(); + if (LauncherFrame.this.launcher != null) { + LauncherFrame.this.launcher.stop(); + LauncherFrame.this.launcher.destroy(); + } + System.exit(0); + } + }); + } + + private Launcher launcher; + private LoginForm loginForm; + + public void playCached(String userName) { + try { + if (userName == null || userName.length() <= 0) { + userName = "Player"; + } + this.launcher = new Launcher(); + this.launcher.customParameters.put("userName", userName); + this.launcher.init(); + removeAll(); + add(this.launcher, "Center"); + validate(); + this.launcher.start(); + this.loginForm = null; + setTitle("Minecraft"); + } catch (Exception e) { + e.printStackTrace(); + showError(e.toString()); + } + } + + public void login(String userName, String password) { + try { + String parameters = "user=" + URLEncoder.encode(userName, "UTF-8") + "&password=" + URLEncoder.encode(password, "UTF-8") + "&version=" + '\f'; + String result = Util.excutePost("http://www.minecraft.net/game/getversion.jsp", parameters); + if (result == null) { + showError("Can't connect to minecraft.net"); + this.loginForm.setNoNetwork(); + return; + } + if (!result.contains(":")) { + if (result.trim().equals("Bad login")) { + showError("Login failed"); + } else if (result.trim().equals("Old version")) { + this.loginForm.setOutdated(); + showError("Outdated launcher"); + } else { + showError(result); + } + this.loginForm.setNoNetwork(); + return; + } + String[] values = result.split(":"); + + System.out.println("Username is '" + values[2] + "'"); + + this.launcher = new Launcher(); + this.launcher.customParameters.put("userName", values[2].trim()); + this.launcher.customParameters.put("latestVersion", values[0].trim()); + this.launcher.customParameters.put("downloadTicket", values[1].trim()); + this.launcher.customParameters.put("sessionId", values[3].trim()); + this.launcher.init(); + + removeAll(); + add(this.launcher, "Center"); + validate(); + this.launcher.start(); + this.loginForm.loginOk(); + this.loginForm = null; + setTitle("Minecraft"); + } catch (Exception e) { + e.printStackTrace(); + showError(e.toString()); + this.loginForm.setNoNetwork(); + } + } + + private void showError(String error) { + removeAll(); + add(this.loginForm); + this.loginForm.setError(error); + validate(); + } + + public boolean canPlayOffline(String userName) { + Launcher launcher = new Launcher(); + launcher.init(userName, null, null, null); + return launcher.canPlayOffline(); + } + + public static void main(String[] args) { + LauncherFrame launcherFrame = new LauncherFrame(); + launcherFrame.setVisible(true); + } +} diff --git a/src/net/minecraft/LoginForm.java b/src/net/minecraft/LoginForm.java new file mode 100644 index 0000000..d8f6873 --- /dev/null +++ b/src/net/minecraft/LoginForm.java @@ -0,0 +1,380 @@ +package net.minecraft; + +import javax.crypto.Cipher; +import java.awt.*; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; + +public class LoginForm extends Panel { + private static final long serialVersionUID = 1L; + private final TextField userName = new TextField(20); + private final Image bgImage; + private final TextField password = new TextField(20); + private final Checkbox rememberBox = new Checkbox("Remember password"); + private final Button launchButton = new Button("Login"); + private final Button retryButton = new Button("Try again"); + private final Button offlineButton = new Button("Play offline"); + private final Label errorLabel = new Label("", 1); + private final LauncherFrame launcherFrame; + private boolean outdated = false; + private VolatileImage img; + + public LoginForm(final LauncherFrame launcherFrame) { + this.launcherFrame = launcherFrame; + + GridBagLayout gbl = new GridBagLayout(); + setLayout(gbl); + + add(buildLoginPanel()); + + try { + this.bgImage = ImageIO.read(LoginForm.class.getResource("dirt.png")).getScaledInstance(32, 32, 16); + } catch (IOException e) { + e.printStackTrace(); + } + + readUsername(); + + this.retryButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + LoginForm.this.errorLabel.setText(""); + LoginForm.this.removeAll(); + LoginForm.this.add(LoginForm.this.buildLoginPanel()); + LoginForm.this.validate(); + } + }); + + this.offlineButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + launcherFrame.playCached(LoginForm.this.userName.getText()); + } + }); + + this.launchButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent ae) { + launcherFrame.login(LoginForm.this.userName.getText(), LoginForm.this.password.getText()); + } + }); + } + + private void readUsername() { + try { + DataInputStream dis; + File lastLogin = new File(Util.getWorkingDirectory(), "lastlogin"); + + Cipher cipher = getCipher(2, "passwordfile"); + if (cipher != null) { + dis = new DataInputStream(new CipherInputStream(new FileInputStream(lastLogin), cipher)); + } else { + dis = new DataInputStream(new FileInputStream(lastLogin)); + } + this.userName.setText(dis.readUTF()); + this.password.setText(dis.readUTF()); + this.rememberBox.setState((this.password.getText().length() > 0)); + dis.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void writeUsername() { + try { + DataOutputStream dos; + File lastLogin = new File(Util.getWorkingDirectory(), "lastlogin"); + + Cipher cipher = getCipher(1, "passwordfile"); + if (cipher != null) { + dos = new DataOutputStream(new CipherOutputStream(new FileOutputStream(lastLogin), cipher)); + } else { + dos = new DataOutputStream(new FileOutputStream(lastLogin)); + } + dos.writeUTF(this.userName.getText()); + dos.writeUTF(this.rememberBox.getState() ? this.password.getText() : ""); + dos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private Cipher getCipher(int mode, String password) throws Exception { + Random random = new Random(43287234L); + byte[] salt = new byte[8]; + random.nextBytes(salt); + PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 5); + + SecretKey pbeKey = SecretKeyFactory.getInstance("PBEWithMD5AndDES").generateSecret(new PBEKeySpec(password.toCharArray())); + Cipher cipher = Cipher.getInstance("PBEWithMD5AndDES"); + cipher.init(mode, pbeKey, pbeParamSpec); + return cipher; + } + + public void update(Graphics g) { + paint(g); + } + + + public void paint(Graphics g2) { + int w = getWidth() / 2; + int h = getHeight() / 2; + if (this.img == null || this.img.getWidth() != w || this.img.getHeight() != h) { + this.img = createVolatileImage(w, h); + } + + Graphics g = this.img.getGraphics(); + for (int x = 0; x <= w / 32; x++) { + for (int y = 0; y <= h / 32; y++) + g.drawImage(this.bgImage, x * 32, y * 32, null); + } + g.setColor(Color.LIGHT_GRAY); + + + String msg = "Minecraft Launcher"; + g.setFont(new Font(null, 1, 20)); + FontMetrics fm = g.getFontMetrics(); + g.drawString(msg, w / 2 - fm.stringWidth(msg) / 2, h / 2 - fm.getHeight() * 2); + + g.dispose(); + g2.drawImage(this.img, 0, 0, w * 2, h * 2, null); + } + + private Panel buildLoginPanel() { + Panel panel = new Panel() { + private static final long serialVersionUID = 1L; + private final Insets insets = new Insets(12, 24, 16, 32); + + public Insets getInsets() { + return this.insets; + } + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g) { + super.paint(g); + int hOffs = 0; + + g.setColor(Color.BLACK); + g.drawRect(0, hOffs, getWidth() - 1, getHeight() - 1 - hOffs); + g.drawRect(1, 1 + hOffs, getWidth() - 3, getHeight() - 3 - hOffs); + g.setColor(Color.WHITE); + + g.drawRect(2, 2 + hOffs, getWidth() - 5, getHeight() - 5 - hOffs); + } + }; + + panel.setBackground(Color.GRAY); + BorderLayout layout = new BorderLayout(); + layout.setHgap(0); + layout.setVgap(8); + panel.setLayout(layout); + + + GridLayout gl1 = new GridLayout(0, 1); + GridLayout gl2 = new GridLayout(0, 1); + gl1.setVgap(2); + gl2.setVgap(2); + Panel titles = new Panel(gl1); + Panel values = new Panel(gl2); + + titles.add(new Label("Username:", 2)); + titles.add(new Label("Password:", 2)); + titles.add(new Label("", 2)); + + this.password.setEchoChar('*'); + values.add(this.userName); + values.add(this.password); + values.add(this.rememberBox); + + panel.add(titles, "West"); + panel.add(values, "Center"); + + Panel loginPanel = new Panel(new BorderLayout()); + + Panel registerPanel = new Panel(new BorderLayout()); + try { + if (this.outdated) { + Label accountLink = new Label("You need to update the launcher!") { + private static final long serialVersionUID = 0L; + + public void paint(Graphics g) { + super.paint(g); + + int x = 0; + int y = 0; + + + FontMetrics fm = g.getFontMetrics(); + int width = fm.stringWidth(getText()); + int height = fm.getHeight(); + + if (getAlignment() == 0) { + x = 0; + } else if (getAlignment() == 1) { + x = (getBounds()).width / 2 - width / 2; + } else if (getAlignment() == 2) { + x = (getBounds()).width - width; + } + y = (getBounds()).height / 2 + height / 2 - 1; + + g.drawLine(x + 2, y, x + width - 2, y); + } + + public void update(Graphics g) { + paint(g); + } + }; + + accountLink.setCursor(Cursor.getPredefinedCursor(12)); + accountLink.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent arg0) { + try { + Desktop.getDesktop().browse((new URL("http://www.minecraft.net/download.jsp")).toURI()); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + accountLink.setForeground(Color.BLUE); + registerPanel.add(accountLink, "West"); + registerPanel.add(new Panel(), "Center"); + } else { + Label accountLink = new Label("Need account?") { + private static final long serialVersionUID = 0L; + + public void paint(Graphics g) { + super.paint(g); + + int x = 0; + int y = 0; + + + FontMetrics fm = g.getFontMetrics(); + int width = fm.stringWidth(getText()); + int height = fm.getHeight(); + + if (getAlignment() == 0) { + x = 0; + } else if (getAlignment() == 1) { + x = (getBounds()).width / 2 - width / 2; + } else if (getAlignment() == 2) { + x = (getBounds()).width - width; + } + y = (getBounds()).height / 2 + height / 2 - 1; + + g.drawLine(x + 2, y, x + width - 2, y); + } + + public void update(Graphics g) { + paint(g); + } + }; + + accountLink.setCursor(Cursor.getPredefinedCursor(12)); + accountLink.addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent arg0) { + try { + Desktop.getDesktop().browse((new URL("http://www.minecraft.net/register.jsp")).toURI()); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + accountLink.setForeground(Color.BLUE); + registerPanel.add(accountLink, "West"); + registerPanel.add(new Panel(), "Center"); + } + } catch (Error error) { + } + + + loginPanel.add(registerPanel, "Center"); + loginPanel.add(this.launchButton, "East"); + panel.add(loginPanel, "South"); + + this.errorLabel.setFont(new Font(null, 2, 16)); + this.errorLabel.setForeground(new Color(8388608)); + panel.add(this.errorLabel, "North"); + + + return panel; + } + + private Panel buildOfflinePanel() { + Panel panel = new Panel() { + private static final long serialVersionUID = 1L; + private final Insets insets = new Insets(12, 24, 16, 32); + + public Insets getInsets() { + return this.insets; + } + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g) { + super.paint(g); + int hOffs = 0; + g.setColor(Color.BLACK); + g.drawRect(0, hOffs, getWidth() - 1, getHeight() - 1 - hOffs); + g.drawRect(1, 1 + hOffs, getWidth() - 3, getHeight() - 3 - hOffs); + g.setColor(Color.WHITE); + + g.drawRect(2, 2 + hOffs, getWidth() - 5, getHeight() - 5 - hOffs); + } + }; + + panel.setBackground(Color.GRAY); + BorderLayout layout = new BorderLayout(); + panel.setLayout(layout); + + Panel loginPanel = new Panel(new BorderLayout()); + loginPanel.add(new Panel(), "Center"); + panel.add(new Panel(), "Center"); + loginPanel.add(this.retryButton, "East"); + loginPanel.add(this.offlineButton, "West"); + + boolean canPlayOffline = this.launcherFrame.canPlayOffline(this.userName.getText()); + this.offlineButton.setEnabled(canPlayOffline); + if (!canPlayOffline) { + panel.add(new Label("Play online once to enable offline"), "Center"); + } + panel.add(loginPanel, "South"); + + this.errorLabel.setFont(new Font(null, 2, 16)); + this.errorLabel.setForeground(new Color(8388608)); + panel.add(this.errorLabel, "North"); + + + return panel; + } + + public void setError(String errorMessage) { + removeAll(); + add(buildLoginPanel()); + this.errorLabel.setText(errorMessage); + validate(); + } + + public void loginOk() { + writeUsername(); + } + + public void setNoNetwork() { + removeAll(); + add(buildOfflinePanel()); + validate(); + } + + public void checkAutologin() { + if (this.password.getText().length() > 0) { + this.launcherFrame.login(this.userName.getText(), this.password.getText()); + } + } + + public void setOutdated() { + this.outdated = true; + } +} diff --git a/src/net/minecraft/MinecraftLauncher.java b/src/net/minecraft/MinecraftLauncher.java new file mode 100644 index 0000000..14d2e5f --- /dev/null +++ b/src/net/minecraft/MinecraftLauncher.java @@ -0,0 +1,40 @@ +package net.minecraft; + +import java.util.ArrayList; + +public class MinecraftLauncher { + private static final int MIN_HEAP = 511; + private static final int RECOMMENDED_HEAP = 1024; + + public static void main(String[] args) throws Exception { + float heapSizeMegs = (float) (Runtime.getRuntime().maxMemory() / 1024L / 1024L); + + if (heapSizeMegs > 511.0F) { + LauncherFrame.main(args); + } else { + try { + String pathToJar = MinecraftLauncher.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + + ArrayList<String> params = new ArrayList<String>(); + + params.add("javaw"); + params.add("-Xmx1024m"); + params.add("-Dsun.java2d.noddraw=true"); + params.add("-Dsun.java2d.d3d=false"); + params.add("-Dsun.java2d.opengl=false"); + params.add("-Dsun.java2d.pmoffscreen=false"); + + params.add("-classpath"); + params.add(pathToJar); + params.add("net.minecraft.LauncherFrame"); + ProcessBuilder pb = new ProcessBuilder(params); + Process process = pb.start(); + if (process == null) throw new Exception("!"); + System.exit(0); + } catch (Exception e) { + e.printStackTrace(); + LauncherFrame.main(args); + } + } + } +} diff --git a/src/net/minecraft/Util.java b/src/net/minecraft/Util.java new file mode 100644 index 0000000..9b21f9c --- /dev/null +++ b/src/net/minecraft/Util.java @@ -0,0 +1,104 @@ +package net.minecraft; + +import java.io.DataOutputStream; +import java.io.File; +import java.net.HttpURLConnection; + +public class Util { + private enum OS { + linux, solaris, windows, macos, unknown + } + + private static File workDir = null; + + public static File getWorkingDirectory() { + if (workDir == null) workDir = getWorkingDirectory("minecraft"); + return workDir; + } + + public static File getWorkingDirectory(String applicationName) { + File workingDirectory; + String applicationData, userHome = System.getProperty("user.home", "."); + + switch (getPlatform()) { + case null: + case solaris: + workingDirectory = new File(userHome, '.' + applicationName + '/'); + break; + case windows: + applicationData = System.getenv("APPDATA"); + if (applicationData != null) { + workingDirectory = new File(applicationData, "." + applicationName + '/'); + break; + } + workingDirectory = new File(userHome, '.' + applicationName + '/'); + break; + case macos: + workingDirectory = new File(userHome, "Library/Application Support/" + applicationName); + break; + default: + workingDirectory = new File(userHome, String.valueOf(applicationName) + '/'); + break; + } + if (!workingDirectory.exists() && !workingDirectory.mkdirs()) + throw new RuntimeException("The working directory could not be created: " + workingDirectory); + return workingDirectory; + } + + private static OS getPlatform() { + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.contains("win")) return OS.windows; + if (osName.contains("mac")) return OS.macos; + if (osName.contains("solaris")) return OS.solaris; + if (osName.contains("sunos")) return OS.solaris; + if (osName.contains("linux")) return OS.linux; + if (osName.contains("unix")) return OS.linux; + return OS.unknown; + } + + + public static String excutePost(String targetURL, String urlParameters) { + HttpURLConnection connection = null; + + try { + URL url = new URL(targetURL); + connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + + connection.setRequestProperty("Content-Length", Integer.toString((urlParameters.getBytes()).length)); + connection.setRequestProperty("Content-Language", "en-US"); + + connection.setUseCaches(false); + connection.setDoInput(true); + connection.setDoOutput(true); + + + DataOutputStream wr = new DataOutputStream(connection.getOutputStream()); + wr.writeBytes(urlParameters); + wr.flush(); + wr.close(); + + + InputStream is = connection.getInputStream(); + BufferedReader rd = new BufferedReader(new InputStreamReader(is)); + + StringBuffer response = new StringBuffer(); + String line; + while ((line = rd.readLine()) != null) { + response.append(line); + response.append('\r'); + } + rd.close(); + return response.toString(); + } catch (Exception e) { + + e.printStackTrace(); + return null; + } finally { + + if (connection != null) + connection.disconnect(); + } + } +} diff --git a/src/net/minecraft/dirt.png b/src/net/minecraft/dirt.png Binary files differnew file mode 100644 index 0000000..ef5288f --- /dev/null +++ b/src/net/minecraft/dirt.png diff --git a/src/net/minecraft/favicon.png b/src/net/minecraft/favicon.png Binary files differnew file mode 100644 index 0000000..727eb76 --- /dev/null +++ b/src/net/minecraft/favicon.png |