├── .gitignore ├── settings.gradle ├── glass.ico ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── net │ │ └── glasslauncher │ │ │ └── legacy │ │ │ └── assets │ │ │ ├── glass.png │ │ │ ├── logo.png │ │ │ ├── logonew.png │ │ │ ├── refresh.png │ │ │ ├── background.png │ │ │ ├── blogbackground.png │ │ │ ├── blog.css │ │ │ ├── mcversions.json │ │ │ └── blog.html │ └── log4j.xml │ └── java │ └── net │ └── glasslauncher │ ├── legacy │ ├── jsontemplate │ │ ├── MCVersion.java │ │ ├── Textures.java │ │ ├── LoginCredsAgent.java │ │ ├── LoginResponseAgent.java │ │ ├── ProfileProperties.java │ │ ├── MinecraftResources.java │ │ ├── MCVersions.java │ │ ├── MultiMCPack.java │ │ ├── TextureURLs.java │ │ ├── Profile.java │ │ ├── ServerJoin.java │ │ ├── LoginCreds.java │ │ ├── LoginResponse.java │ │ ├── MinecraftResource.java │ │ ├── MultiMCComponent.java │ │ ├── ModpackConfig.java │ │ ├── LauncherConfig.java │ │ ├── ModList.java │ │ ├── Mod.java │ │ └── InstanceConfig.java │ ├── components │ │ ├── ScalingButton.java │ │ ├── JBackgroundImagePanel.java │ │ ├── Logo.java │ │ ├── JBackgroundImageTabbedPane.java │ │ ├── DragListener.java │ │ ├── DragDropHandler.java │ │ ├── DragDropList.java │ │ ├── HintTextField.java │ │ ├── HintPasswordField.java │ │ └── DirtPanel.java │ ├── mc │ │ ├── Monitor.java │ │ ├── StreamGobbler.java │ │ ├── Wrapper.java │ │ └── LaunchArgs.java │ ├── ProxyStandalone.java │ ├── OpenLinkWindow.java │ ├── Main.java │ ├── VerifyAccountWindow.java │ ├── ConsoleWindow.java │ ├── ProgressWindow.java │ ├── util │ │ ├── TempZipFile.java │ │ └── InstanceManager.java │ ├── Config.java │ ├── InstanceManagerWindow.java │ ├── MainWindow.java │ └── OptionsWindow.java │ └── proxy │ ├── web │ ├── ProxyHttpServer.java │ ├── WebUtils.java │ ├── HttpJoinHandler.java │ └── HttpSkinHandler.java │ ├── Proxy.java │ └── ProxyFilter.java ├── README.md ├── l4jconfig.xml ├── gradlew.bat ├── CHANGELOG.md ├── gradlew └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .gradle/ 3 | .idea/ 4 | out/ 5 | build/ 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'glass-launcher-legacy' 2 | -------------------------------------------------------------------------------- /glass.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/glass.ico -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/glass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/src/main/resources/net/glasslauncher/legacy/assets/glass.png -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/src/main/resources/net/glasslauncher/legacy/assets/logo.png -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/logonew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/src/main/resources/net/glasslauncher/legacy/assets/logonew.png -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/src/main/resources/net/glasslauncher/legacy/assets/refresh.png -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/src/main/resources/net/glasslauncher/legacy/assets/background.png -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/blogbackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ModificationStation/Glass-Launcher-Legacy/HEAD/src/main/resources/net/glasslauncher/legacy/assets/blogbackground.png -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/MCVersion.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class MCVersion { 7 | private String url; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/Textures.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class Textures { 7 | private TextureURLs textures; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/LoginCredsAgent.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | public class LoginCredsAgent { 4 | private String name = "Minecraft"; 5 | private long version = 1; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/LoginResponseAgent.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class LoginResponseAgent { 7 | private String name; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/ProfileProperties.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class ProfileProperties { 7 | private String value; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/MinecraftResources.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class MinecraftResources { 7 | private MinecraftResource[] files; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/MCVersions.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.Map; 6 | 7 | @Getter 8 | public class MCVersions { 9 | private Map client; 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Oct 06 22:13:54 BST 2019 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/MultiMCPack.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class MultiMCPack { 7 | private MultiMCComponent[] components; 8 | private long formatVersion; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/TextureURLs.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.Map; 6 | 7 | public class TextureURLs { 8 | @Getter private Map SKIN; 9 | @Getter private Map CAPE; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/Profile.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class Profile { 7 | private String id; 8 | private String name; 9 | private ProfileProperties[] properties; 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/ServerJoin.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Setter; 4 | 5 | @Setter 6 | public class ServerJoin { 7 | private String accessToken; 8 | private String selectedProfile; 9 | private String serverId; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/LoginCreds.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Setter; 4 | 5 | @Setter 6 | public class LoginCreds { 7 | private String username; 8 | private String password; 9 | private LoginCredsAgent agent = new LoginCredsAgent(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/LoginResponse.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | 4 | import lombok.Getter; 5 | 6 | @Getter 7 | public class LoginResponse { 8 | private String accessToken; 9 | private String error; 10 | private LoginResponseAgent selectedProfile; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/MinecraftResource.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class MinecraftResource { 7 | private String file; 8 | private String date; 9 | private long size; 10 | private String md5; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/ScalingButton.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import javax.swing.JButton; 4 | import java.awt.Insets; 5 | 6 | public class ScalingButton extends JButton { 7 | 8 | /** 9 | * Creates a button with no set text or icon. 10 | */ 11 | public ScalingButton() { 12 | setMargin(new Insets(0, 0, 0, 0)); 13 | setOpaque(false); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/MultiMCComponent.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public class MultiMCComponent { 7 | private String cachedName; 8 | private String cachedVersion; 9 | private String uid; 10 | private boolean dependencyOnly = false; 11 | private boolean important = false; 12 | private boolean disabled = false; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/ModpackConfig.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.Getter; 6 | 7 | @Getter 8 | public class ModpackConfig { 9 | @Expose private String version = ""; 10 | @SerializedName("modpackname") 11 | @Expose private String modpackName; 12 | @SerializedName("mcver") 13 | @Expose private String mcVer = "b1.7.3"; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/LauncherConfig.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import net.glasslauncher.common.JsonConfig; 7 | 8 | @Getter @Setter 9 | public class LauncherConfig extends JsonConfig { 10 | 11 | @SerializedName("lastusedname") 12 | private String lastUsedName = ""; 13 | 14 | @SerializedName("lastusedinstance") 15 | private String lastUsedInstance = ""; 16 | 17 | public LauncherConfig(String path) { 18 | super(path); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/ModList.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import com.google.gson.annotations.SerializedName; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | import net.glasslauncher.common.JsonConfig; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @Getter @Setter 13 | public class ModList extends JsonConfig { 14 | @SerializedName("jarmods") 15 | @Expose private List jarMods = new ArrayList<>(); 16 | 17 | /** 18 | * @param path Path to the JSON file. 19 | */ 20 | public ModList(String path) { 21 | super(path); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/Mod.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class Mod { 8 | @Expose private String fileName; 9 | @Expose private long type; 10 | @Expose private String name; 11 | @Expose private boolean enabled; 12 | 13 | public Mod(String modFileName, String modName, long modType, boolean modEnabled) { 14 | this.fileName = modFileName; 15 | this.type = modType; 16 | this.name = modName; 17 | this.enabled = modEnabled; 18 | } 19 | 20 | public String toString() { 21 | return name; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Glass Launcher Legacy 2 | A much more lightweight approach to what PyMCL was trying to do. 3 | 4 | ## Features 5 | 6 | - Made in Java 7 | - Instancing that works. 8 | 9 | ## Compiling 10 | 11 | Use `gradlew shadowJar` to build. 12 | Output is in `/build/libs`. 13 | 14 | Glass Launcher will automatically download any dependencies it needs into `%appdata%/.glass-launcher/lib` if on windows, `/Library/Application Support/.glass-launcher` on osx or `~/.glass-launcher` on other OSes. 15 | 16 | ## Usage 17 | 18 | You can just double click the jar file to launch the launcher normally. 19 | Launch with `-proxy` to launch just the proxy. Optional `-doskin`, `-dosound`, `-docape` args can be used. 20 | Use `-help` for detailed help about all launch args. 21 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/jsontemplate/InstanceConfig.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.jsontemplate; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import net.glasslauncher.common.JsonConfig; 6 | 7 | @Getter @Setter 8 | public class InstanceConfig extends JsonConfig { 9 | private boolean proxySound = false; 10 | private boolean proxySkin = false; 11 | private boolean proxyCape = false; 12 | private boolean proxyLogin = false; 13 | private String maxRam = "512m"; 14 | private String minRam = "64m"; 15 | private String javaArgs = ""; 16 | private String version = "none"; 17 | 18 | /** 19 | * @param path Path to the JSON file. 20 | */ 21 | public InstanceConfig(String path) { 22 | super(path); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/resources/log4j.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/proxy/web/ProxyHttpServer.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.proxy.web; 2 | 3 | import com.sun.net.httpserver.HttpServer; 4 | import net.glasslauncher.legacy.Config; 5 | 6 | import java.io.IOException; 7 | import java.net.InetSocketAddress; 8 | 9 | public final class ProxyHttpServer { 10 | public static HttpServer start() throws IOException { 11 | HttpServer server = HttpServer.create(new InetSocketAddress(Config.PROXY_ADDRESS, Config.PROXY_WEB_PORT), 0); 12 | server.createContext("/skins/", new HttpSkinHandler(0)); 13 | server.createContext("/capes/", new HttpSkinHandler(1)); 14 | server.createContext("/join/", new HttpJoinHandler()); 15 | server.setExecutor(null); // creates a default executor 16 | return server; 17 | } 18 | } -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/blog.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | background-color: #222222; 4 | background-image: url("blogbackground.png"); 5 | color: #e0d0d0; 6 | margin: 0; 7 | } 8 | 9 | a { 10 | color: #aaaaff; 11 | cursor: pointer; 12 | } 13 | 14 | hr { 15 | border: 0; 16 | color: #111111; 17 | background-color: #111111; 18 | height: 2px; 19 | } 20 | 21 | h3 { 22 | color: #ffffff; 23 | font-size: 16px; 24 | } 25 | 26 | img { 27 | border: 0; 28 | margin: 0; 29 | } 30 | 31 | .sidebar { 32 | padding: 10px; 33 | vertical-align: top; 34 | width: 100px; 35 | } 36 | 37 | .alert { 38 | background-color: #aa0000; 39 | color: #ffffff; 40 | font-weight: bold; 41 | padding: 6px 10px; 42 | width: 500px; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/mc/Monitor.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.mc; 2 | 3 | import net.glasslauncher.legacy.Main; 4 | import net.glasslauncher.proxy.Proxy; 5 | 6 | import java.util.Timer; 7 | import java.util.TimerTask; 8 | 9 | public class Monitor { 10 | private TimerTask task; 11 | 12 | private Timer timer = new Timer(); 13 | 14 | Monitor(Process mc, Proxy proxy) { 15 | 16 | task = new TimerTask() { 17 | @Override 18 | public void run() { 19 | if (!mc.isAlive()) { 20 | if (proxy != null) { 21 | proxy.exit(); 22 | } 23 | Main.getLogger().info("Minecraft exited with exit code " + mc.exitValue()); 24 | timer.cancel(); 25 | } 26 | } 27 | }; 28 | } 29 | 30 | public void start() { 31 | timer.schedule(task, 0, 1000); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/mc/StreamGobbler.java: -------------------------------------------------------------------------------- 1 | /* 2 | Credit to: 3 | https://stackoverflow.com/a/32351355 4 | */ 5 | package net.glasslauncher.legacy.mc; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.InputStreamReader; 11 | import java.io.PrintStream; 12 | 13 | public class StreamGobbler extends Thread { 14 | private InputStream in; 15 | private PrintStream out; 16 | 17 | StreamGobbler(InputStream in, PrintStream out) { 18 | this.in = in; 19 | this.out = out; 20 | } 21 | 22 | @Override 23 | public void run() { 24 | try { 25 | BufferedReader input = new BufferedReader(new InputStreamReader(in)); 26 | String line; 27 | while ((line = input.readLine()) != null) 28 | out.println(line); 29 | } catch (IOException e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/JBackgroundImagePanel.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import javax.imageio.ImageIO; 4 | import javax.swing.JPanel; 5 | import java.awt.Graphics; 6 | import java.awt.image.BufferedImage; 7 | import java.net.URL; 8 | 9 | public class JBackgroundImagePanel extends JPanel { 10 | private BufferedImage tileImage; 11 | 12 | public JBackgroundImagePanel(URL imagePath) { 13 | try { 14 | tileImage = ImageIO.read(imagePath.openStream()); 15 | } catch (Exception e) { 16 | e.printStackTrace(); 17 | tileImage = new BufferedImage(48, 48, BufferedImage.TYPE_INT_ARGB); 18 | } 19 | } 20 | 21 | 22 | protected void paintComponent(Graphics g) { 23 | int width = getWidth(); 24 | int height = getHeight(); 25 | for (int x = 0; x < width; x += tileImage.getWidth()) { 26 | for (int y = 0; y < height; y += tileImage.getHeight()) { 27 | g.drawImage(tileImage, x, y, this); 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/Logo.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import net.glasslauncher.legacy.Main; 4 | 5 | import javax.imageio.ImageIO; 6 | import javax.swing.JPanel; 7 | import java.awt.Dimension; 8 | import java.awt.Graphics; 9 | import java.awt.Image; 10 | import java.awt.image.BufferedImage; 11 | import java.io.IOException; 12 | 13 | public class Logo extends JPanel { 14 | private Image bgimage; 15 | 16 | public Logo() { 17 | setOpaque(true); 18 | try { 19 | BufferedImage logoimg = ImageIO.read(Main.class.getResource("assets/logo.png")); 20 | int w = logoimg.getWidth(); 21 | int h = logoimg.getHeight(); 22 | bgimage = logoimg.getScaledInstance(w, h, 16); 23 | setPreferredSize(new Dimension(w + 32, h + 32)); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | public void update(Graphics g) { 30 | paint(g); 31 | } 32 | 33 | public void paintComponent(Graphics g) { 34 | g.drawImage(this.bgimage, 24, 24, null); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/JBackgroundImageTabbedPane.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import javax.imageio.ImageIO; 4 | import javax.swing.JTabbedPane; 5 | import java.awt.Graphics; 6 | import java.awt.image.BufferedImage; 7 | import java.net.URL; 8 | 9 | public class JBackgroundImageTabbedPane extends JTabbedPane { 10 | 11 | private BufferedImage tileImage; 12 | 13 | public JBackgroundImageTabbedPane(URL imagePath) { 14 | try { 15 | tileImage = ImageIO.read(imagePath.openStream()); 16 | } catch (Exception e) { 17 | e.printStackTrace(); 18 | tileImage = new BufferedImage(48, 48, BufferedImage.TYPE_INT_ARGB); 19 | } 20 | } 21 | 22 | protected void paintComponent(Graphics g) { 23 | int width = getWidth(); 24 | int height = getHeight(); 25 | for (int x = 0; x < width; x += tileImage.getWidth()) { 26 | for (int y = 0; y < height; y += tileImage.getHeight()) { 27 | g.drawImage(tileImage, x, y, this); 28 | } 29 | } 30 | super.paintComponent(g); 31 | repaint(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/DragListener.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | 4 | import java.awt.datatransfer.StringSelection; 5 | import java.awt.dnd.DnDConstants; 6 | import java.awt.dnd.DragGestureEvent; 7 | import java.awt.dnd.DragGestureListener; 8 | import java.awt.dnd.DragSource; 9 | import java.awt.dnd.DragSourceDragEvent; 10 | import java.awt.dnd.DragSourceDropEvent; 11 | import java.awt.dnd.DragSourceEvent; 12 | import java.awt.dnd.DragSourceListener; 13 | 14 | class DragListener implements DragSourceListener, DragGestureListener { 15 | DragDropList list; 16 | 17 | DragSource ds = new DragSource(); 18 | 19 | DragListener(DragDropList list) { 20 | this.list = list; 21 | ds.createDefaultDragGestureRecognizer(list, 22 | DnDConstants.ACTION_MOVE, this); 23 | 24 | } 25 | 26 | public void dragGestureRecognized(DragGestureEvent dge) { 27 | StringSelection transferable = new StringSelection(Integer.toString(list.getSelectedIndex())); 28 | ds.startDrag(dge, DragSource.DefaultCopyDrop, transferable, this); 29 | } 30 | 31 | public void dragEnter(DragSourceDragEvent dsde) { 32 | } 33 | 34 | public void dragExit(DragSourceEvent dse) { 35 | } 36 | 37 | public void dragOver(DragSourceDragEvent dsde) { 38 | } 39 | 40 | public void dragDropEnd(DragSourceDropEvent dsde) { 41 | } 42 | 43 | public void dropActionChanged(DragSourceDragEvent dsde) { 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/ProxyStandalone.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import net.glasslauncher.proxy.Proxy; 4 | 5 | import java.io.File; 6 | 7 | public class ProxyStandalone { 8 | /** 9 | * Main function of the standalone proxy 10 | * @param args 11 | */ 12 | public static void main(String[] args) { 13 | boolean doSound = false; 14 | boolean doSkin = false; 15 | boolean doCape = false; 16 | boolean doLogin = false; 17 | for (String arg : args) { 18 | if (arg.toLowerCase().equals("-dosound")) { 19 | doSound = true; 20 | } else if (arg.toLowerCase().equals("-doskin")) { 21 | doSkin = true; 22 | } else if (arg.toLowerCase().equals("-docape")) { 23 | doCape = true; 24 | } else if (arg.toLowerCase().equals("-dologin")) { 25 | doLogin = true; 26 | } 27 | } 28 | if (!doSound && !doSkin && !doCape && !doLogin) { 29 | Main.getLogger().info("No proxy arguments provided! Defaulting to all enabled."); 30 | doSound = true; 31 | doSkin = true; 32 | doCape = true; 33 | } 34 | try { 35 | new File(Config.CACHE_PATH).mkdirs(); 36 | } catch (Exception e) { 37 | e.printStackTrace(); 38 | } 39 | Proxy proxy = new Proxy(new boolean[]{doSound, doSkin, doCape, doLogin}); 40 | proxy.start(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/OpenLinkWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import javax.swing.JEditorPane; 4 | import javax.swing.JLabel; 5 | import javax.swing.JOptionPane; 6 | import javax.swing.event.HyperlinkEvent; 7 | import java.awt.Component; 8 | import java.awt.Desktop; 9 | import java.awt.Font; 10 | 11 | public class OpenLinkWindow extends JOptionPane { 12 | 13 | /** 14 | * Used to make a pop-up clickable link. 15 | * @param comp 16 | * @param url 17 | */ 18 | OpenLinkWindow(Component comp, String url) { 19 | JLabel label = new JLabel(); 20 | Font font = label.getFont(); 21 | 22 | String style = "font-family:" + font.getFamily() + ";" + "font-weight:" + (font.isBold() ? "bold" : "normal") + ";" + 23 | "font-size:" + font.getSize() + "pt;"; 24 | JEditorPane link = new JEditorPane("text/html", "" // 25 | + "" + url + "" // 26 | + ""); 27 | link.addHyperlinkListener(event -> { 28 | if (event.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { 29 | try { 30 | Desktop.getDesktop().browse(event.getURL().toURI()); 31 | } catch (Exception e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | }); 36 | link.setEditable(false); 37 | link.setBackground(label.getBackground()); 38 | showMessageDialog(comp, link, "Log Uploaded!", JOptionPane.INFORMATION_MESSAGE); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /l4jconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | gui 5 | build\libs\glass-launcher-legacy-0.4.5-all.jar 6 | build\libs\glass-launcher-legacy-0.4.5.exe 7 | 8 | 9 | 10 | normal 11 | https://www.java.com/en/download/manual.jsp 12 | 13 | false 14 | false 15 | 16 | glass.ico 17 | 18 | net.glasslauncher.legacy.Main 19 | 20 | 21 | 22 | false 23 | false 24 | 1.8.0 25 | 26 | preferJre 27 | 64/32 28 | 128 29 | 128 30 | 31 | 32 | 1.0.0.0 33 | 1.0.0.0 34 | Glass Launcher 35 | CC-0 36 | 1.0.0.0 37 | 1.0.0.0 38 | Glass Launcher 39 | glass-launcher.net 40 | glasslauncher 41 | Glass Launcher.exe 42 | 43 | ENGLISH_US 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/DragDropHandler.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import javax.swing.DefaultListModel; 4 | import javax.swing.JList; 5 | import javax.swing.TransferHandler; 6 | import java.awt.datatransfer.DataFlavor; 7 | import java.awt.datatransfer.Transferable; 8 | 9 | class DragDropHandler extends TransferHandler { 10 | DragDropList list; 11 | 12 | DragDropHandler(DragDropList list) { 13 | this.list = list; 14 | } 15 | 16 | public boolean canImport(TransferSupport support) { 17 | if (!support.isDataFlavorSupported(DataFlavor.stringFlavor)) { 18 | return false; 19 | } 20 | JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); 21 | return dl.getIndex() != -1; 22 | } 23 | 24 | public boolean importData(TransferSupport support) { 25 | if (!canImport(support)) { 26 | return false; 27 | } 28 | 29 | Transferable transferable = support.getTransferable(); 30 | try { 31 | int draggedImageIndex = Integer.parseInt((String) transferable.getTransferData(DataFlavor.stringFlavor)); 32 | 33 | JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); 34 | DefaultListModel model = list.model; 35 | Object draggedObject = model.get(draggedImageIndex); 36 | int dropIndex = dl.getIndex(); 37 | if (model.indexOf(draggedObject) < dropIndex) { 38 | dropIndex--; 39 | } 40 | model.removeElement(draggedObject); 41 | model.add(dropIndex, draggedObject); 42 | return true; 43 | } catch (Exception e) { 44 | e.printStackTrace(); 45 | return false; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/DragDropList.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import net.glasslauncher.legacy.jsontemplate.Mod; 4 | 5 | import javax.swing.DefaultListCellRenderer; 6 | import javax.swing.DefaultListModel; 7 | import javax.swing.DropMode; 8 | import javax.swing.JList; 9 | import java.awt.Color; 10 | import java.awt.Component; 11 | import java.util.ArrayList; 12 | 13 | public class DragDropList extends JList { 14 | public DefaultListModel model; 15 | 16 | public DragDropList(ArrayList list) { 17 | super(new DefaultListModel()); 18 | model = (DefaultListModel) getModel(); 19 | for (Mod element : list) { 20 | model.addElement(element); 21 | } 22 | setDragEnabled(true); 23 | setDropMode(DropMode.INSERT); 24 | setTransferHandler(new DragDropHandler(this)); 25 | new DragListener(this); 26 | 27 | this.setCellRenderer(new DefaultListCellRenderer() { 28 | @Override 29 | public Component getListCellRendererComponent(JList list, Object value, int index, 30 | boolean isSelected, boolean cellHasFocus) { 31 | Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 32 | Mod mod = (Mod) value; 33 | setText(mod.toString()); 34 | if (mod.isEnabled()) { 35 | setBackground(new Color(204, 255, 204)); 36 | } else { 37 | setBackground(new Color(255, 230, 230)); 38 | } 39 | if (isSelected) { 40 | setBackground(getBackground().darker()); 41 | } 42 | return c; 43 | } 44 | 45 | }); 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/HintTextField.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import javax.swing.JTextField; 4 | import java.awt.Color; 5 | import java.awt.event.FocusEvent; 6 | import java.awt.event.FocusListener; 7 | 8 | public class HintTextField extends JTextField { 9 | private final Color defaultColor; 10 | private final Color hintColor; 11 | private final String hint; 12 | 13 | public HintTextField(String hint) { 14 | this(hint, Color.BLACK, Color.GRAY); 15 | } 16 | 17 | public HintTextField(String hint, Color defaultColor, Color hintColor) { 18 | this.defaultColor = defaultColor; 19 | this.hintColor = hintColor; 20 | this.hint = hint; 21 | this.setText(hint); 22 | this.setToolTipText(hint); 23 | this.setForeground(hintColor); 24 | this.addFocusListener(new FocusListener() { 25 | public void focusGained(FocusEvent e) { 26 | if (getForeground() == hintColor) { 27 | HintTextField.super.setText(""); 28 | setForeground(defaultColor); 29 | } 30 | } 31 | 32 | @Override 33 | public void focusLost(FocusEvent e) { 34 | if (getText().isEmpty()) { 35 | setText(hint); 36 | setForeground(hintColor); 37 | } 38 | } 39 | }); 40 | } 41 | 42 | public void setText(String text) { 43 | if (text.isEmpty()) { 44 | setText(hint); 45 | setForeground(hintColor); 46 | } 47 | else { 48 | super.setText(text); 49 | setForeground(defaultColor); 50 | } 51 | } 52 | 53 | public String getText() { 54 | if (defaultColor != getForeground() && super.getText().equals(hint)) { 55 | return ""; 56 | } 57 | else { 58 | return super.getText(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/HintPasswordField.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | 4 | import javax.swing.JPasswordField; 5 | import java.awt.Color; 6 | import java.awt.event.FocusEvent; 7 | import java.awt.event.FocusListener; 8 | import java.util.Arrays; 9 | 10 | public class HintPasswordField extends JPasswordField { 11 | private final Color defaultColor; 12 | private final Color hintColor; 13 | private final String hint; 14 | 15 | public HintPasswordField(String hint) { 16 | this(hint, Color.black, Color.gray); 17 | } 18 | 19 | public HintPasswordField(String hint, Color defaultColor, Color hintColor) { 20 | this.defaultColor = defaultColor; 21 | this.hintColor = hintColor; 22 | this.hint = hint; 23 | this.setText(hint); 24 | this.setToolTipText(hint); 25 | this.setForeground(hintColor); 26 | this.addFocusListener(new FocusListener() { 27 | public void focusGained(FocusEvent e) { 28 | if (getForeground() == hintColor) { 29 | HintPasswordField.super.setText(""); 30 | setEchoChar('•'); 31 | setForeground(defaultColor); 32 | } 33 | } 34 | 35 | @Override 36 | public void focusLost(FocusEvent e) { 37 | if (String.valueOf(getPassword()).isEmpty()) { 38 | setText(hint); 39 | setEchoChar((char) 0); 40 | setForeground(hintColor); 41 | } 42 | } 43 | }); 44 | } 45 | 46 | public void setText(String text) { 47 | if (text.isEmpty()) { 48 | setText(hint); 49 | setForeground(hintColor); 50 | } 51 | else { 52 | super.setText(text); 53 | setForeground(defaultColor); 54 | } 55 | } 56 | 57 | public char[] getPassword() { 58 | if (defaultColor != getForeground() && super.getText().equals(hint)) { 59 | return new char[]{}; 60 | } 61 | else { 62 | return super.getPassword(); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/components/DirtPanel.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.components; 2 | 3 | import net.glasslauncher.legacy.Main; 4 | 5 | import javax.imageio.ImageIO; 6 | import javax.swing.JPanel; 7 | import java.awt.Color; 8 | import java.awt.GradientPaint; 9 | import java.awt.Graphics; 10 | import java.awt.Graphics2D; 11 | import java.awt.Image; 12 | import java.awt.geom.Point2D; 13 | import java.io.IOException; 14 | 15 | public class DirtPanel extends JPanel { 16 | private Image bgImage; 17 | private Image img; 18 | 19 | public DirtPanel() { 20 | setOpaque(true); 21 | 22 | try { 23 | this.bgImage = ImageIO.read(Main.class.getResource("assets/background.png")).getScaledInstance(32, 32, 16); 24 | } catch (IOException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | 29 | public void update(Graphics g) { 30 | paint(g); 31 | } 32 | 33 | public void paintComponent(Graphics paramGraphics) { 34 | int w = getWidth() / 2 + 1; 35 | int h = getHeight() / 2 + 1; 36 | if (this.img == null || this.img.getWidth(null) != w || this.img.getHeight(null) != h) { 37 | this.img = createImage(w, h); 38 | 39 | Graphics graphics = this.img.getGraphics(); 40 | for (byte b = 0; b <= w / 32; b++) { 41 | for (byte b1 = 0; b1 <= h / 32; b1++) 42 | graphics.drawImage(this.bgImage, b * 32, b1 * 32, null); 43 | } 44 | if (graphics instanceof Graphics2D) { 45 | Graphics2D graphics2D = (Graphics2D) graphics; 46 | int k = 1; 47 | graphics2D.setPaint(new GradientPaint(new Point2D.Float(0.0F, 0.0F), new Color(553648127, true), new Point2D.Float(0.0F, k), new Color(0, true))); 48 | graphics2D.fillRect(0, 0, w, k); 49 | 50 | k = h; 51 | graphics2D.setPaint(new GradientPaint(new Point2D.Float(0.0F, 0.0F), new Color(0, true), new Point2D.Float(0.0F, k), new Color(1610612736, true))); 52 | graphics2D.fillRect(0, 0, w, k); 53 | } 54 | graphics.dispose(); 55 | } 56 | paramGraphics.drawImage(this.img, 0, 0, w * 2, h * 2, null); 57 | } 58 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/Main.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import lombok.Getter; 4 | import net.glasslauncher.common.CommonConfig; 5 | import net.glasslauncher.common.FileUtils; 6 | import net.glasslauncher.repo.api.mod.RepoReader; 7 | 8 | import javax.swing.UIManager; 9 | import java.util.ArrayList; 10 | import java.util.logging.Logger; 11 | 12 | public class Main { 13 | @Getter private static Logger logger = CommonConfig.makeLogger("GlassLauncher", "glass-launcher"); 14 | private static ArrayList libs = new ArrayList<>(); 15 | public static MainWindow mainwin; 16 | 17 | public static void main(String[] args) { 18 | try { 19 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | 24 | ConsoleWindow console = new ConsoleWindow(); 25 | 26 | getDeps(); 27 | Config.setEasyMineLauncherFile(libs.get(0)); 28 | 29 | // TODO: fix bcp* libs breaking for no reason. 30 | /*for (Object lib : libs.toArray()) { 31 | try { 32 | Classpath.addFile(Config.GLASS_PATH + "lib/" + lib); 33 | } catch (Exception e) { 34 | logger.info("Failed to load \"" + lib + "\"."); 35 | e.printStackTrace(); 36 | } 37 | }*/ 38 | 39 | try { 40 | Config.loadConfigFiles(); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | } 44 | 45 | for (String arg : args) { 46 | if (arg.equals("-proxy")) { 47 | ProxyStandalone.main(args); 48 | return; 49 | } 50 | } 51 | /*try { 52 | Main.logger.info(RepoReader.getMods()[0].getName()); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | }*/ 56 | mainwin = new MainWindow(console); 57 | } 58 | 59 | /** 60 | * Downloads all dependencies listed in Config.Deps.cactusDeps. 61 | */ 62 | private static void getDeps() { 63 | getLogger().info("Checking dependencies..."); 64 | 65 | for (String dep : Config.getGLASS_DEPS().keySet()) { 66 | try { 67 | FileUtils.downloadFile(dep, CommonConfig.GLASS_PATH + "/lib/", Config.getGLASS_DEPS().get(dep)); 68 | libs.add(dep.substring(dep.lastIndexOf('/') + 1)); 69 | } catch (Exception e) { 70 | getLogger().info("Failed to download dependency. Invalid formatting?"); 71 | e.printStackTrace(); 72 | } 73 | } 74 | } 75 | 76 | /** 77 | * Checks if the main launcher window is active. 78 | * 79 | * @return True if active, False otherwise. 80 | */ 81 | public static boolean isLauncherActive() { 82 | return mainwin != null; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/proxy/web/WebUtils.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.proxy.web; 2 | 3 | import com.google.gson.Gson; 4 | import net.glasslauncher.legacy.jsontemplate.Profile; 5 | import net.glasslauncher.legacy.Config; 6 | 7 | import java.io.BufferedReader; 8 | import java.io.File; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.net.HttpURLConnection; 13 | import java.net.URL; 14 | import java.nio.file.Files; 15 | import java.nio.file.attribute.BasicFileAttributes; 16 | import java.util.Date; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | public class WebUtils { 20 | public static String getStringFromURL(String url) throws IOException { 21 | HttpURLConnection req = (HttpURLConnection) new URL(url).openConnection(); 22 | BufferedReader res = new BufferedReader(new InputStreamReader(req.getInputStream())); 23 | StringBuilder resj = new StringBuilder(); 24 | for (String strline = ""; strline != null; strline = res.readLine()) { 25 | resj.append(strline); 26 | } 27 | return resj.toString(); 28 | } 29 | 30 | public static String getUUID(String username) throws IOException { 31 | Profile profile = (new Gson()).fromJson(getStringFromURL("https://api.mojang.com/users/profiles/minecraft/" + username + "?at=" + (new Date()).getTime() / 1000L), Profile.class); 32 | return profile.getId(); 33 | } 34 | 35 | /** 36 | * Checks glass-launcher's cache folder for a given file/folder within age limit. 37 | * 38 | * @param path The relative path to the file/folder. 39 | * @return The file object if it was found and within age limit, else return null. 40 | */ 41 | public static File checkCache(String path) { 42 | File file = new File(Config.CACHE_PATH + "webproxy/" + path); 43 | if (file.exists()) { 44 | BasicFileAttributes fileAttributes; 45 | try { 46 | fileAttributes = Files.readAttributes(file.toPath(), BasicFileAttributes.class); 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | return null; 50 | } 51 | if (fileAttributes.lastModifiedTime().to(TimeUnit.SECONDS) < (new Date().getTime()) / 1000L - Config.CACHE_AGE_LIMIT) { 52 | return null; 53 | } 54 | return file; 55 | } 56 | return null; 57 | } 58 | 59 | public static File getCache(String path) { 60 | return new File(Config.CACHE_PATH + "webproxy/" + path); 61 | } 62 | 63 | public static void makeCacheFolders(String path) { 64 | (new File(Config.CACHE_PATH + "webproxy/" + path)).mkdirs(); 65 | } 66 | 67 | public static void putCache(File file, byte[] bytes) throws IOException { 68 | FileOutputStream out = new FileOutputStream(file); 69 | if (bytes == null) { 70 | bytes = new byte[]{}; 71 | } 72 | out.write(bytes); 73 | out.close(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/proxy/Proxy.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.proxy; 2 | 3 | import com.sun.net.httpserver.HttpServer; 4 | import io.netty.channel.ChannelHandlerContext; 5 | import io.netty.handler.codec.http.HttpRequest; 6 | import net.glasslauncher.legacy.Config; 7 | import net.glasslauncher.legacy.Main; 8 | import net.glasslauncher.proxy.web.ProxyHttpServer; 9 | import org.littleshoot.proxy.HttpFilters; 10 | import org.littleshoot.proxy.HttpFiltersSourceAdapter; 11 | import org.littleshoot.proxy.HttpProxyServer; 12 | import org.littleshoot.proxy.HttpProxyServerBootstrap; 13 | import org.littleshoot.proxy.impl.DefaultHttpProxyServer; 14 | import org.littleshoot.proxy.mitm.Authority; 15 | import org.littleshoot.proxy.mitm.CertificateSniffingMitmManager; 16 | 17 | import java.io.File; 18 | import java.net.InetSocketAddress; 19 | 20 | public class Proxy extends Thread { 21 | private HttpProxyServerBootstrap serverBoot; 22 | private HttpProxyServer httpProxyServer; 23 | private HttpServer httpServer; 24 | 25 | public Proxy(boolean[] args) { 26 | try { 27 | // 1: args[sound, skin, cape] 28 | 29 | httpServer = ProxyHttpServer.start(); 30 | 31 | this.serverBoot = 32 | DefaultHttpProxyServer.bootstrap() 33 | .withAddress(new InetSocketAddress(Config.PROXY_ADDRESS, Config.PROXY_PORT)) 34 | .withManInTheMiddle(new CertificateSniffingMitmManager(new Authority( 35 | new File(Config.CACHE_PATH), 36 | "glass-launcher-proxy-mitm", 37 | "thisisranlocallysothisdoesntmatter".toCharArray(), 38 | "Glass Launcher", 39 | "Glass Launcher", 40 | "glass-launcher-proxy, a simple proxy to fix legacy MC.", 41 | "Glass Launcher", 42 | "glass-launcher-proxy, used by Glass-launcher to fix sounds, skins and capes in legacy Minecraft." 43 | ))) 44 | .withFiltersSource(new HttpFiltersSourceAdapter() { 45 | public HttpFilters filterRequest(HttpRequest originalRequest, ChannelHandlerContext ctx) { 46 | ProxyFilter proxyFilter = new ProxyFilter(originalRequest); 47 | proxyFilter.setArgs(args); 48 | return proxyFilter; 49 | } 50 | }); 51 | } catch (Exception e) { 52 | e.printStackTrace(); 53 | } 54 | } 55 | 56 | public void start() { 57 | httpProxyServer = serverBoot.start(); 58 | httpServer.start(); 59 | Main.getLogger().info("Proxy servers started!"); 60 | } 61 | 62 | public void exit() { 63 | httpProxyServer.stop(); 64 | httpServer.stop(0); 65 | Main.getLogger().info("Proxy servers stopped!"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/VerifyAccountWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import lombok.Getter; 4 | import net.glasslauncher.legacy.components.HintPasswordField; 5 | import net.glasslauncher.legacy.components.HintTextField; 6 | import net.glasslauncher.legacy.components.ScalingButton; 7 | import net.glasslauncher.legacy.mc.LaunchArgs; 8 | 9 | import javax.swing.JDialog; 10 | import javax.swing.JOptionPane; 11 | import javax.swing.JPanel; 12 | import javax.swing.JPasswordField; 13 | import java.awt.Color; 14 | import java.awt.Dimension; 15 | import java.awt.GridLayout; 16 | import java.awt.Window; 17 | import java.awt.event.FocusEvent; 18 | import java.awt.event.FocusListener; 19 | 20 | public class VerifyAccountWindow extends JDialog { 21 | @Getter boolean loginValid = false; 22 | 23 | protected JPasswordField password; 24 | protected HintTextField username; 25 | 26 | public VerifyAccountWindow(Window frame) { 27 | super(frame); 28 | setModal(true); 29 | setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); 30 | setLayout(new GridLayout()); 31 | setResizable(false); 32 | setTitle("Verify Account"); 33 | 34 | JPanel panel = new JPanel(); 35 | panel.setLayout(null); 36 | panel.setPreferredSize(new Dimension(186, 100)); 37 | 38 | // Username field 39 | username = new HintTextField("Username or Email"); 40 | if (Config.getLauncherConfig().getLastUsedName() != null) { 41 | username.setText(Config.getLauncherConfig().getLastUsedName()); 42 | } 43 | username.setBounds(10, 14, 166, 22); 44 | username.addActionListener((e) -> { 45 | login(); 46 | }); 47 | 48 | // Password field 49 | password = new HintPasswordField("Password"); 50 | password.setBounds(10, 40, 166, 22); 51 | password.addActionListener((e) -> { 52 | login(); 53 | }); 54 | 55 | // Login button 56 | ScalingButton login = new ScalingButton(); 57 | login.setText("Login"); 58 | login.setBounds(58, 64, 70, 22); 59 | login.setOpaque(false); 60 | login.addActionListener(event -> { 61 | login(); 62 | }); 63 | 64 | panel.add(username); 65 | panel.add(password); 66 | panel.add(login); 67 | 68 | add(panel); 69 | pack(); 70 | setLocationRelativeTo(frame); 71 | 72 | setVisible(true); 73 | } 74 | 75 | private void login() { 76 | String pass = ""; 77 | if (password.getForeground() != Color.gray) { 78 | pass = String.valueOf(password.getPassword()); 79 | } 80 | if (!pass.isEmpty() && (new LaunchArgs()).login(username.getText(), pass) != null) { 81 | Config.getLauncherConfig().setLastUsedName(username.getText()); 82 | Config.getLauncherConfig().saveFile(); 83 | loginValid = true; 84 | dispose(); 85 | } else { 86 | JOptionPane.showMessageDialog(this, "Invalid username or password."); 87 | password.setText(""); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | #### v0.4.5 2 | \* Fixed the resource proxy not working on old alpha versions. 3 | 4 | #### v0.4.4 5 | \+ The last instance launched is now remembered between restarts. 6 | \+ The launcher now uses glass-commons for logging and most file related functions. 7 | \* Fixed a crash relating to the cache folder not being made when the proxy is started in standalone mode. 8 | 9 | #### v0.4.3 10 | \+ Added an "Open Instances Folder" button to the instance manager. 11 | \+ Added the ability to press enter to log in. 12 | \+ Added an exe version of glass-launcher to releases. This will only be available in "stable" releases. 13 | \+ Added a progress window when applying mods from the options window. 14 | \* Made it so file select dialogs are native. This should make file selecting a little easier. 15 | \* Potentially fixed text in buttons being cut off. 16 | \* Fixed 404 errors in the resource proxy. 17 | \* Progress windows now log their text to console. 18 | \* Fixed mods not installing when installing glass-launcher modpacks. 19 | 20 | #### v0.4.2 21 | \+ Made the FileUtils class far more useful. 22 | \+ New jar constructing system. Should be WAY faster than the old system. 23 | \* Fixed maxRam not being saved in instance config. 24 | \* Fixed path being serialised to JSON in instance config. 25 | \* Fixed instance importing. Also fixed instance names having .zip at the end. 26 | \* Fixed applying mods. 27 | \* Fixed me not knowing how to use my own JsonConfig class. 28 | \- Removed all * imports. 29 | 30 | #### v0.4.1 31 | \* Disabled automatic dependency downloads in favour of FatJar because BouncyCastle libs are broken. 32 | \* Fixed Minecraft being unable to launch. 33 | 34 | #### v0.4 35 | \+ Added some basic mod management. 36 | \+ Added a basic progress window. 37 | \+ Added MultiMC modpack support. 38 | \+ Added automatic sound downloading on creating an instance. This will preserve any custom sounds from modpacks, if there are any. 39 | \+ Added authentication support to the proxy. You now no longer need to install a login fix! 40 | \+ Added caching of versions, sounds and LWJGL. 41 | \+ Added Lombok to make things easier. 42 | \+ Added the ability to use external links in `!net/glasslauncher/legacy/assets/mcversions.json`. 43 | \+ Added the ability to delete instances. 44 | \+ Added automatic instance list refreshes to make things feel more fluent. 45 | \+ Added account verification when creating/installing instances. 46 | \- Removed certain debug logs. 47 | \- Removed some unneeded classes. 48 | \- Removed PyMCL instance support due to how the mod manager and new config works. 49 | \* Swapped Json-IO with GSON. Config files are now more robust and should produce less errors (if any). 50 | \* Config overhaul for the library swap. 51 | \* Fixed some visual bugs. 52 | \* Changed install folder from `.PyMCL` to `.glass-launcher`. 53 | 54 | #### v0.3 55 | \+ Added input hints. 56 | \+ Automatic dependency downloading to `.PyMCL/lib` instead of making a fatjar. 57 | \+ MitM proxy that redirects to a localhosted webserver ran in glass-launcher, which has a semi-configurable cache time on skins and capes. Saves cache in `.PyMCL/cache/webproxy`. 58 | \+ Last used username is now saved upon a successful LaunchArgs validation. 59 | \* Better JSON reading and writing. 60 | 61 | #### v0.2 62 | \+ Config saving and loading re-added. 63 | \- Removed all legacy and out of date test code. 64 | \* Cleaned up some unused imports. 65 | 66 | #### v0.1 67 | \+ Initial release. 68 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/ConsoleWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import net.glasslauncher.common.ConsoleStream; 4 | import net.glasslauncher.common.Pastee; 5 | import net.glasslauncher.legacy.components.ScalingButton; 6 | 7 | import javax.imageio.ImageIO; 8 | import javax.swing.JFrame; 9 | import javax.swing.JOptionPane; 10 | import javax.swing.JPanel; 11 | import javax.swing.JScrollPane; 12 | import javax.swing.JTextArea; 13 | import java.awt.BorderLayout; 14 | import java.awt.Color; 15 | import java.awt.Dialog; 16 | import java.awt.Font; 17 | import java.awt.GridLayout; 18 | import java.awt.event.WindowAdapter; 19 | import java.awt.event.WindowEvent; 20 | import java.io.IOException; 21 | import java.io.PrintStream; 22 | import java.net.URISyntaxException; 23 | 24 | public class ConsoleWindow extends JFrame { 25 | private JTextArea textArea = new JTextArea(); 26 | private final PrintStream oldout = System.out; 27 | private final PrintStream olderr = System.err; 28 | private final ConsoleStream pin = new ConsoleStream(textArea, oldout); 29 | private final ConsoleStream pin2 = new ConsoleStream(textArea, olderr); 30 | 31 | public ConsoleWindow() { 32 | // create all components and add them 33 | setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); 34 | setTitle("Glass Launcher Console"); 35 | addWindowListener(new WindowAdapter() { 36 | public void windowClosing(WindowEvent we) { 37 | if (!Main.isLauncherActive()) { 38 | Main.getLogger().info("Closing..."); 39 | System.exit(0); 40 | } 41 | Main.getLogger().info("Close the launcher using the main window!"); 42 | } 43 | } 44 | ); 45 | setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 46 | 47 | try { 48 | setIconImage(ImageIO.read(ConsoleWindow.class.getResource("assets/glass.png").toURI().toURL())); 49 | } catch (URISyntaxException | IOException e) { 50 | e.printStackTrace(); 51 | } 52 | setBounds(0, 0, 600, 300); 53 | 54 | textArea.setEditable(false); 55 | textArea.setBackground(Color.black); 56 | textArea.setForeground(Color.lightGray); 57 | textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); 58 | 59 | JScrollPane textContainer = new JScrollPane(textArea); 60 | 61 | ScalingButton uploadButton = new ScalingButton(); 62 | uploadButton.setText("Upload log to paste.ee"); 63 | uploadButton.setOpaque(false); 64 | uploadButton.addActionListener(event -> { 65 | Pastee paste = new Pastee(textArea.getText()); 66 | String url = paste.post(); 67 | if (url != null) { 68 | new OpenLinkWindow(this, url); 69 | } else { 70 | JOptionPane.showMessageDialog(null, "An error occurred while uploading. Try again later."); 71 | } 72 | }); 73 | 74 | JPanel panel = new JPanel(); 75 | panel.setLayout(new GridLayout()); 76 | panel.add(textContainer); 77 | add(panel, BorderLayout.CENTER); 78 | add(uploadButton, BorderLayout.SOUTH); 79 | 80 | setLocation(5, 10); 81 | setVisible(true); 82 | 83 | System.setOut(new PrintStream(this.pin, true)); 84 | System.setErr(new PrintStream(this.pin2, true)); 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/ProgressWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import lombok.Setter; 4 | 5 | import javax.swing.JDialog; 6 | import javax.swing.JOptionPane; 7 | import javax.swing.JPanel; 8 | import javax.swing.JProgressBar; 9 | import javax.swing.plaf.basic.BasicProgressBarUI; 10 | import java.awt.Color; 11 | import java.awt.Dimension; 12 | import java.awt.GridLayout; 13 | import java.awt.Window; 14 | import java.awt.event.WindowAdapter; 15 | import java.awt.event.WindowEvent; 16 | 17 | public class ProgressWindow extends JDialog { 18 | private JPanel panel; 19 | private JProgressBar progressBar; 20 | @Setter private Thread thread; 21 | 22 | public ProgressWindow(Window frame, String title) { 23 | super(frame); 24 | setModal(true); 25 | setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); 26 | setLayout(new GridLayout()); 27 | setResizable(false); 28 | setTitle(title); 29 | 30 | addWindowListener(new WindowAdapter() { 31 | public void windowClosing(WindowEvent windowEvent) { 32 | try { 33 | int isClosing = JOptionPane.showOptionDialog( 34 | getContentPane(), 35 | "Cancelling may corrupt any open files. Are you sure?", 36 | "Confirmation", 37 | JOptionPane.YES_NO_OPTION, 38 | JOptionPane.WARNING_MESSAGE, 39 | null, 40 | null, 41 | JOptionPane.NO_OPTION 42 | ); 43 | if (isClosing == JOptionPane.YES_OPTION) { 44 | if (thread != null) { 45 | thread.interrupt(); 46 | } 47 | dispose(); 48 | } 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | }); 54 | 55 | panel = new JPanel(null); 56 | panel.setPreferredSize(new Dimension(340, 72)); 57 | 58 | progressBar = new JProgressBar(); 59 | progressBar.setBounds(20, 20, 300, 32); 60 | progressBar.setStringPainted(true); 61 | 62 | panel.add(progressBar); 63 | add(panel); 64 | 65 | pack(); 66 | setLocationRelativeTo(frame); 67 | } 68 | 69 | public void setProgressText(String progressText) { 70 | Main.getLogger().info(progressText); 71 | progressBar.setString(progressText); 72 | progressBar.repaint(); 73 | } 74 | 75 | public void setProgressColor(Color color) { 76 | progressBar.setForeground(color); 77 | } 78 | 79 | public void setProgressTextColor(Color selected, Color unselected) { 80 | progressBar.setUI(new BasicProgressBarUI() { 81 | protected Color getSelectionBackground() {return unselected;} 82 | protected Color getSelectionForeground() {return selected;} 83 | }); 84 | progressBar.repaint(); 85 | } 86 | 87 | public void setProgress(int progress) { 88 | progressBar.setValue(progress); 89 | progressBar.repaint(); 90 | } 91 | 92 | public void setProgressMax(int max) { 93 | progressBar.setMaximum(max); 94 | progressBar.repaint(); 95 | } 96 | 97 | public void increaseProgress() { 98 | progressBar.setValue(progressBar.getValue() + 1); 99 | } 100 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/proxy/web/HttpJoinHandler.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.proxy.web; 2 | 3 | import com.google.gson.Gson; 4 | import com.sun.net.httpserver.HttpExchange; 5 | import com.sun.net.httpserver.HttpHandler; 6 | import net.glasslauncher.legacy.jsontemplate.Profile; 7 | import net.glasslauncher.legacy.jsontemplate.ServerJoin; 8 | import net.glasslauncher.legacy.Main; 9 | 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.io.OutputStream; 13 | import java.io.OutputStreamWriter; 14 | import java.net.HttpURLConnection; 15 | import java.net.URL; 16 | import java.net.URLConnection; 17 | import java.util.Scanner; 18 | 19 | public class HttpJoinHandler implements HttpHandler { 20 | 21 | public void handle(HttpExchange t) { 22 | try { 23 | // Original: http://www.minecraft.net/game/joinserver.jsp?user=calmilamsy&sessionId=-&serverId=77e59ac042d8a50e 24 | // New: http://localhost:25561/join/?user=calmilamsy&sessionId=-&serverId=77e59ac042d8a50e 25 | 26 | // req = /join/?user=calmilamsy&sessionId=-&serverId=77e59ac042d8a50e 27 | String req = t.getRequestURI().toString(); 28 | String[] reqParts = req.split("[&=]"); 29 | 30 | // SERVER 31 | if (reqParts.length == 4) { 32 | String response; 33 | // Turns to: https://sessionserver.mojang.com/session/minecraft/hasJoined?username=calmilamsy&serverId=77e59ac042d8a50e 34 | req = req.replaceFirst("user", "username"); 35 | req = req.replaceFirst("/join/", "/session/minecraft/hasJoined"); 36 | req = "https://sessionserver.mojang.com" + req; 37 | 38 | HttpURLConnection reqJoined = (HttpURLConnection) (new URL(req)).openConnection(); 39 | if (reqJoined.getResponseCode() == 200) { 40 | response = "YES"; 41 | } else { 42 | response = "NO"; 43 | } 44 | 45 | t.sendResponseHeaders(200, response.getBytes().length); 46 | OutputStream os = t.getResponseBody(); 47 | os.write(response.getBytes()); 48 | os.close(); 49 | // CLIENT 50 | } else { 51 | reqParts = new String[] {reqParts[1], reqParts[3], reqParts[5]}; 52 | URL nameURL = new URL("https://api.mojang.com/users/profiles/minecraft/" + reqParts[0]); 53 | URLConnection nameUrlConnection = nameURL.openConnection(); 54 | String response = convertStreamToString(nameUrlConnection.getInputStream()); 55 | 56 | Profile profile = (new Gson()).fromJson(response, Profile.class); 57 | String uuid = profile.getId(); 58 | 59 | ServerJoin serverJoin = new ServerJoin(); 60 | serverJoin.setAccessToken(reqParts[1]); 61 | serverJoin.setSelectedProfile(uuid); 62 | serverJoin.setServerId(reqParts[2]); 63 | 64 | 65 | HttpURLConnection reqJoin = (HttpURLConnection) (new URL("https://sessionserver.mojang.com/session/minecraft/join")).openConnection(); 66 | reqJoin.setRequestMethod("POST"); 67 | reqJoin.setRequestProperty("Content-Type", "application/json"); 68 | reqJoin.setDoOutput(true); 69 | 70 | OutputStreamWriter wr = new OutputStreamWriter(reqJoin.getOutputStream()); 71 | wr.write((new Gson()).toJson(serverJoin)); 72 | wr.flush(); 73 | 74 | if (reqJoin.getResponseCode() != 204) { 75 | throw new IOException("Got unexpeced response from join request: " + reqJoin.getResponseCode()); 76 | } else { 77 | response = "ok"; 78 | } 79 | t.sendResponseHeaders(200, response.getBytes().length); 80 | 81 | OutputStream os = t.getResponseBody(); 82 | os.write(response.getBytes()); 83 | os.close(); 84 | 85 | } 86 | t.close(); 87 | } catch (Exception e) { 88 | Main.getLogger().severe("Exception while handling join:"); 89 | try { 90 | t.sendResponseHeaders(500, 0); 91 | } catch (Exception ignored) {} 92 | e.printStackTrace(); 93 | t.close(); 94 | } 95 | 96 | } 97 | 98 | static String convertStreamToString(InputStream is) { 99 | Scanner s = new Scanner(is).useDelimiter("\\A"); 100 | return s.hasNext() ? s.next() : ""; 101 | } 102 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/proxy/ProxyFilter.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.proxy; 2 | 3 | import io.netty.handler.codec.http.HttpObject; 4 | import io.netty.handler.codec.http.HttpRequest; 5 | import io.netty.handler.codec.http.HttpResponse; 6 | import net.glasslauncher.legacy.Config; 7 | import net.glasslauncher.legacy.Main; 8 | import org.littleshoot.proxy.HttpFiltersAdapter; 9 | 10 | import java.util.List; 11 | 12 | public class ProxyFilter extends HttpFiltersAdapter { 13 | private static List ignoredHosts = Config.PROXY_IGNORED_HOSTS; 14 | private static String newHost = "localhost:" + Config.PROXY_WEB_PORT; 15 | private boolean doSoundFix; 16 | private boolean doSkinFix; 17 | private boolean doCapeFix; 18 | private boolean doLoginFix; 19 | 20 | ProxyFilter(HttpRequest originalRequest) { 21 | super(originalRequest); 22 | } 23 | 24 | public void setArgs(boolean[] args) { 25 | this.doSoundFix = args[0]; 26 | this.doSkinFix = args[1]; 27 | this.doCapeFix = args[2]; 28 | this.doLoginFix = args[3]; 29 | } 30 | 31 | @Override 32 | public HttpResponse clientToProxyRequest(HttpObject httpObject) { 33 | 34 | if (httpObject instanceof HttpRequest) { 35 | HttpRequest httpRequest = (HttpRequest) httpObject; 36 | String host = httpRequest.headers().get("Host"); 37 | String path; 38 | 39 | if (httpRequest.getUri().startsWith("http://") || httpRequest.getUri().startsWith("https://")) { 40 | String uri = httpRequest.getUri(); 41 | for (String ignoredHost : ignoredHosts) { 42 | if (uri.contains(ignoredHost)) { 43 | return null; 44 | } 45 | } 46 | 47 | try { 48 | path = httpRequest.getUri().replaceFirst("htt[ps]*://" + host, ""); 49 | } catch (Exception e) { 50 | e.printStackTrace(); 51 | return null; 52 | } 53 | 54 | String doRedirect = null; 55 | 56 | if (host.contains("amazonaws.com") || host.contains("minecraft.net")) { 57 | if (doSoundFix && (path.contains("MinecraftResources") || path.contains("resources"))) { 58 | if (path.equals("/resources/")) { 59 | path = "/MinecraftResources/indexalpha.php"; 60 | } 61 | path = path.replaceFirst("/resources/", "/MinecraftResources/"); 62 | httpRequest.setUri("http://mcresources.modification-station.net" + path); 63 | httpRequest.headers().set("Host", "mcresources.modification-station.net"); 64 | return null; 65 | } 66 | 67 | if (doSkinFix && path.contains("MinecraftSkins")) { 68 | doRedirect = "/skins/" + path.split("/")[2]; 69 | } 70 | 71 | if (doCapeFix && path.contains("MinecraftCloaks")) { 72 | doRedirect = "/capes/" + path.split("/")[2]; 73 | } 74 | } 75 | 76 | if (host.contains("minecraft.net")) { 77 | if (doSkinFix && path.contains("/skin/")) { 78 | doRedirect = "/skins/" + path.split("/")[2]; 79 | } 80 | 81 | if (doCapeFix && path.contains("/cloak/")) { 82 | doRedirect = "/capes/" + path.split("/")[2]; 83 | } 84 | 85 | if (doLoginFix && path.contains("game/joinserver")) { 86 | doRedirect = path.replaceFirst("game/joinserver.jsp?", "join/"); 87 | } 88 | 89 | if (doLoginFix && path.contains("game/checkserver")) { 90 | doRedirect = path.replaceFirst("game/checkserver.jsp?", "join/"); 91 | } 92 | } 93 | 94 | if (doRedirect == null) { 95 | return null; 96 | } 97 | 98 | httpRequest.setUri("http://" + newHost + doRedirect); 99 | httpRequest.headers().set("Host", newHost); 100 | } 101 | } 102 | return null; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/mc/Wrapper.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.mc; 2 | 3 | import net.glasslauncher.common.CommonConfig; 4 | import net.glasslauncher.common.JsonConfig; 5 | import net.glasslauncher.legacy.jsontemplate.InstanceConfig; 6 | import net.glasslauncher.legacy.Config; 7 | import net.glasslauncher.legacy.Main; 8 | import net.glasslauncher.proxy.Proxy; 9 | 10 | import java.io.File; 11 | import java.util.ArrayList; 12 | import java.util.Map; 13 | 14 | public class Wrapper { 15 | private final String instance; 16 | 17 | private ArrayList args; 18 | private InstanceConfig instJson; 19 | 20 | private Proxy proxy = null; 21 | 22 | public Wrapper(String[] launchArgs) { 23 | // 0: username, 1: session, 2: version, 3: doproxy, 4: instance 24 | if (launchArgs.length < 5) { 25 | Main.getLogger().severe("Got " + launchArgs.length + " args, expected 5."); 26 | } 27 | this.instance = launchArgs[4]; 28 | String instPath = CommonConfig.GLASS_PATH + "instances/" + instance + "/.minecraft"; 29 | 30 | this.getConfig(); 31 | 32 | this.args = new ArrayList<>(); 33 | args.add(Config.JAVA_BIN); 34 | if (launchArgs[3].equals("true")) { 35 | args.add("-Dhttp.proxyHost=127.0.0.1"); 36 | args.add("-Dhttp.proxyPort=" + Config.PROXY_PORT); 37 | boolean[] proxyArgs = new boolean[]{ 38 | instJson.isProxySound(), 39 | instJson.isProxySkin(), 40 | instJson.isProxyCape(), 41 | instJson.isProxyLogin() 42 | }; 43 | proxy = new Proxy(proxyArgs); 44 | proxy.start(); 45 | } 46 | String javaArgs = instJson.getJavaArgs(); 47 | if (!javaArgs.isEmpty()) { 48 | for (String arg : javaArgs.split("- ")) { 49 | args.add("-" + arg); 50 | } 51 | } 52 | args.add("-Xmx" + instJson.getMaxRam()); 53 | args.add("-Xms" + instJson.getMinRam()); 54 | args.add("-jar"); 55 | args.add(CommonConfig.GLASS_PATH + "lib/" + Config.getEasyMineLauncherFile()); 56 | args.add("--lwjgl-dir=" + instPath + "/bin"); 57 | args.add("--jar=" + instPath + "/bin/minecraft.jar"); 58 | args.add("--native-dir=" + instPath + "/bin/natives"); 59 | args.add("--parent-dir=" + instPath); 60 | args.add("--height=520"); 61 | args.add("--width=870"); 62 | args.add("--username=" + launchArgs[0]); 63 | args.add("--session-id=" + launchArgs[1]); 64 | args.add("--title=Minecraft " + launchArgs[2]); 65 | } 66 | 67 | private void getConfig() { 68 | String instPath = CommonConfig.GLASS_PATH + "instances/" + instance; 69 | File confFile = new File(instPath + "/instance_config.json"); 70 | 71 | if (!confFile.exists()) { 72 | Main.getLogger().info("Config file does not exist! Using defaults."); 73 | instJson = new InstanceConfig(instPath + "/instance_config.json"); 74 | } else { 75 | try { 76 | instJson = (InstanceConfig) JsonConfig.loadConfig(confFile.getPath(), InstanceConfig.class); 77 | } catch (Exception e) { 78 | Main.getLogger().info("Config file cannot be read! Using defaults."); 79 | instJson = new InstanceConfig(instPath + "/instance_config.json"); 80 | e.printStackTrace(); 81 | } 82 | } 83 | } 84 | 85 | public void startMC() { 86 | // Launched as a separate process because Minecraft directly calls exit when quit is pressed. 87 | 88 | ProcessBuilder mcInit = new ProcessBuilder(args); 89 | 90 | Map mcEnv = mcInit.environment(); 91 | String newAppData = CommonConfig.GLASS_PATH + "instances/" + instance; 92 | mcEnv.put("appdata", newAppData); 93 | mcEnv.put("home", newAppData); 94 | mcEnv.put("user.home", newAppData); 95 | 96 | Process mc; 97 | try { 98 | mc = mcInit.start(); 99 | StreamGobbler mcStdout = new StreamGobbler(mc.getInputStream(), System.out); 100 | StreamGobbler mcStderr = new StreamGobbler(mc.getErrorStream(), System.err); 101 | mcStdout.start(); 102 | mcStderr.start(); 103 | (new Monitor(mc, proxy)).start(); 104 | 105 | } catch (Exception e) { 106 | e.printStackTrace(); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/util/TempZipFile.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.util; 2 | 3 | import net.glasslauncher.common.CommonConfig; 4 | import net.glasslauncher.common.FileUtils; 5 | import net.glasslauncher.legacy.Config; 6 | import net.glasslauncher.legacy.Main; 7 | 8 | import java.io.File; 9 | import java.nio.file.StandardCopyOption; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import static net.glasslauncher.legacy.Config.destDirBypass; 15 | 16 | public class TempZipFile { 17 | private final String originalPath; 18 | private final String tempPath; 19 | 20 | public TempZipFile(String zipFilePath) { 21 | File zipFile = new File(zipFilePath); 22 | originalPath = zipFilePath; 23 | tempPath = CommonConfig.GLASS_PATH + "temp/" + zipFile.getName(); 24 | File tempFile = new File(destDirBypass + tempPath); 25 | if (tempFile.exists()) { 26 | try { 27 | FileUtils.delete(tempFile); 28 | } catch (Exception e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | tempFile.mkdirs(); 33 | 34 | FileUtils.extractZip(zipFilePath, tempPath); 35 | } 36 | 37 | public void deleteFile(String relativePath) { 38 | File fileToDelete = new File(tempPath + "/" + relativePath); 39 | try { 40 | if (fileToDelete.exists()) { 41 | if (fileToDelete.isDirectory()) { 42 | FileUtils.delete(fileToDelete); 43 | } else { 44 | fileToDelete.delete(); 45 | } 46 | } 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | public boolean fileExists(String relativePath) { 53 | return getFile(relativePath).exists(); 54 | } 55 | 56 | public File getFile(String relativePath) { 57 | return new File(tempPath + "/" + relativePath); 58 | } 59 | 60 | public void mergeZip(String zipToMergePath) { 61 | File zipToMerge = new File(zipToMergePath); 62 | if (zipToMerge.exists()) { 63 | FileUtils.extractZip(zipToMergePath, tempPath); 64 | } 65 | } 66 | 67 | public void copyContentsToDir(String relative, String target) { 68 | File targetPath = new File(target); 69 | File relativePath = new File(tempPath + "/" + relative); 70 | try { 71 | FileUtils.copyRecursive(relativePath.toPath(), targetPath.toPath(), StandardCopyOption.REPLACE_EXISTING); 72 | } 73 | catch (Exception e) { 74 | e.printStackTrace(); 75 | } 76 | } 77 | 78 | public void close() { 79 | close(true); 80 | } 81 | 82 | public void close(boolean doSave) { 83 | if (doSave) { 84 | File original = new File(originalPath); 85 | try { 86 | original.delete(); 87 | } catch (Exception e) { 88 | Main.getLogger().info("Failed to delete/check read permissions for \"" + originalPath + "\""); 89 | e.printStackTrace(); 90 | return; 91 | } 92 | try { 93 | List args = new ArrayList<>(); 94 | args.add("jar"); 95 | args.add("cMf"); 96 | args.add(originalPath); 97 | args.add("./*"); 98 | ProcessBuilder processBuilder = new ProcessBuilder(args); 99 | processBuilder.redirectInput(ProcessBuilder.Redirect.INHERIT); 100 | processBuilder.directory(new File(tempPath)); 101 | Process zipProcess = processBuilder.start(); 102 | zipProcess.waitFor(10, TimeUnit.MINUTES); 103 | if (zipProcess.isAlive()) { 104 | Main.getLogger().warning("Zip process has been running for longer than 10 minutes! Terminating process..."); 105 | Main.getLogger().warning("Target folder to zip was: \"" + tempPath + "\""); 106 | zipProcess.destroy(); 107 | zipProcess.waitFor(10, TimeUnit.SECONDS); 108 | if (zipProcess.isAlive()) { 109 | Main.getLogger().warning("Zip process has taken longer than 10 seconds to terminate! Force terminating process..."); 110 | zipProcess.destroyForcibly(); 111 | } 112 | } 113 | } catch (Exception e) { 114 | e.printStackTrace(); 115 | } 116 | } 117 | 118 | try { 119 | FileUtils.delete(new File(destDirBypass + tempPath)); 120 | } catch (Exception e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/mc/LaunchArgs.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.mc; 2 | 3 | import com.google.gson.Gson; 4 | import net.glasslauncher.common.CommonConfig; 5 | import net.glasslauncher.legacy.jsontemplate.InstanceConfig; 6 | import net.glasslauncher.legacy.Config; 7 | import net.glasslauncher.legacy.Main; 8 | import net.glasslauncher.legacy.jsontemplate.LoginCreds; 9 | import net.glasslauncher.legacy.jsontemplate.LoginResponse; 10 | import net.glasslauncher.legacy.jsontemplate.LoginResponseAgent; 11 | 12 | import javax.swing.JOptionPane; 13 | import javax.xml.ws.http.HTTPException; 14 | import java.io.BufferedReader; 15 | import java.io.File; 16 | import java.io.FileReader; 17 | import java.io.IOException; 18 | import java.io.InputStreamReader; 19 | import java.io.OutputStreamWriter; 20 | import java.net.HttpURLConnection; 21 | import java.net.URL; 22 | 23 | public class LaunchArgs { 24 | private String instpath; 25 | private HttpURLConnection req; 26 | private InstanceConfig instjson; 27 | 28 | { 29 | try { 30 | req = (HttpURLConnection) new URL("https://authserver.mojang.com/authenticate").openConnection(); 31 | } catch (IOException e) { 32 | e.printStackTrace(); 33 | } 34 | } 35 | 36 | public String[] getArgs(String[] args) { 37 | // 0: username, 1: pass, 2: instance 38 | if (args.length != 3) { 39 | Main.getLogger().severe("Got " + args.length + " args, expected 3."); 40 | return null; 41 | } 42 | String instance = args[2]; 43 | instpath = CommonConfig.GLASS_PATH + "instances/" + instance; 44 | try { 45 | if (!(new File(instpath, "instance_config.json")).exists()) { 46 | instjson = new InstanceConfig(instpath + "/instance_config.json"); 47 | } else { 48 | instjson = (new Gson()).fromJson(new FileReader(instpath + "/instance_config.json"), InstanceConfig.class); 49 | } 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | return null; 53 | } 54 | String version = getVersion(); 55 | String[] logininfo; 56 | if (args[1].isEmpty()) { 57 | logininfo = new String[]{"", args[0]}; 58 | } else { 59 | logininfo = login(args[0], args[1]); 60 | } 61 | 62 | if (logininfo == null) { 63 | Main.getLogger().severe("Aborting launch."); 64 | return null; 65 | } 66 | 67 | String session = logininfo[0]; 68 | String username = logininfo[1]; 69 | 70 | return new String[]{ 71 | username, 72 | session, 73 | version, 74 | getProxyArg(), 75 | instance 76 | }; 77 | } 78 | 79 | public String[] login(String username, String password) { 80 | Gson gson = new Gson(); 81 | try { 82 | req.setRequestMethod("POST"); 83 | req.setRequestProperty("Content-Type", "application/json"); 84 | req.setDoOutput(true); 85 | req.setDoInput(true); 86 | OutputStreamWriter wr = new OutputStreamWriter(req.getOutputStream()); 87 | LoginCreds creds = new LoginCreds(); 88 | creds.setUsername(username); 89 | creds.setPassword(password); 90 | wr.write(gson.toJson(creds)); 91 | wr.flush(); 92 | 93 | BufferedReader res = new BufferedReader(new InputStreamReader(req.getInputStream())); 94 | StringBuilder resj = new StringBuilder(); 95 | for (String strline = ""; strline != null; strline = res.readLine()) { 96 | resj.append(strline); 97 | } 98 | LoginResponse session = gson.fromJson(resj.toString(), LoginResponse.class); 99 | 100 | if (req.getResponseCode() != 200) { 101 | Main.getLogger().severe("Error sending request!"); 102 | Main.getLogger().severe("Code: " + req.getResponseCode()); 103 | Main.getLogger().severe("Error: " + session.getError()); 104 | throw new HTTPException(req.getResponseCode()); 105 | } 106 | LoginResponseAgent profile = session.getSelectedProfile(); 107 | return new String[]{session.getAccessToken(), profile.getName()}; 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | try { 111 | JOptionPane.showMessageDialog(null, "Server responded with \"HTTP " + req.getResponseCode() + "\". Make sure your username and password are correct and try again."); 112 | } catch (IOException ex) { 113 | e.printStackTrace(); 114 | JOptionPane.showMessageDialog(null, "Login failed! Make sure you have an internet connection!"); 115 | } 116 | return null; 117 | } 118 | } 119 | 120 | private String getVersion() { 121 | return instjson.getVersion(); 122 | } 123 | 124 | private String getJavaArgs() { 125 | String javaargs = ""; 126 | 127 | if (instjson.getMaxRam() != null) { 128 | javaargs += "-Xmx" + instjson.getMaxRam(); 129 | } else { 130 | javaargs += "-Xmx512m"; 131 | } 132 | if (instjson.getMinRam() != null) { 133 | javaargs += "-Xms" + instjson.getMinRam(); 134 | } else { 135 | javaargs += "-Xms64m"; 136 | } 137 | if (instjson.getJavaArgs() != null) { 138 | javaargs += instjson.getJavaArgs(); 139 | } 140 | 141 | return javaargs; 142 | } 143 | 144 | private String getProxyArg() { 145 | return String.valueOf(instjson.isProxySound() || instjson.isProxyCape() || instjson.isProxySkin() || instjson.isProxyLogin()); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS='"-Xmx64m"' 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/Config.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import com.google.gson.Gson; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import net.glasslauncher.common.CommonConfig; 7 | import net.glasslauncher.common.JsonConfig; 8 | import net.glasslauncher.legacy.jsontemplate.LauncherConfig; 9 | import net.glasslauncher.legacy.jsontemplate.MCVersions; 10 | 11 | import java.io.File; 12 | import java.io.InputStreamReader; 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class Config { 20 | public static void loadConfigFiles() { 21 | Gson gson = new Gson(); 22 | mcVersions = gson.fromJson(new InputStreamReader(Main.class.getResourceAsStream("assets/mcversions.json")), MCVersions.class); 23 | launcherConfig = (LauncherConfig) JsonConfig.loadConfig(CommonConfig.GLASS_PATH + "launcher_config.json", LauncherConfig.class); 24 | if (launcherConfig == null) { 25 | launcherConfig = new LauncherConfig(CommonConfig.GLASS_PATH + "launcher_config.json"); 26 | } 27 | } 28 | 29 | public static String destDirBypass = ""; 30 | 31 | @Getter private static MCVersions mcVersions; 32 | @Getter private static LauncherConfig launcherConfig; 33 | 34 | public static final long CACHE_AGE_LIMIT = 600L; 35 | 36 | /** 37 | * The port which the built-in proxy runs on. 38 | */ 39 | public static final int PROXY_PORT = 25560; 40 | 41 | /** 42 | * The port which the built-in webserver for the proxy runs on. 43 | */ 44 | public static final int PROXY_WEB_PORT = 25561; 45 | 46 | /** 47 | * The address which the built-in proxy and webserver runs on. 48 | */ 49 | public static final String PROXY_ADDRESS = "127.0.0.1"; 50 | 51 | /** 52 | * The hosts 53 | */ 54 | public static final List PROXY_IGNORED_HOSTS = Collections.unmodifiableList(new ArrayList() {{ 55 | add("pymcl.net"); 56 | add("localhost"); 57 | add("127.0.0.1"); 58 | add("mojang.com"); 59 | add("icebergcraft.com"); 60 | add("betacraft.ovh"); 61 | add("retrocraft.net"); 62 | add("textures.minecraft.net"); 63 | add("glass-launcher.net"); 64 | }}); 65 | 66 | /** 67 | * The current OS of the user. 68 | * @see #getOSString() 69 | */ 70 | public static final String OS = getOSString(); 71 | 72 | /** 73 | * The version of the launcher. 74 | */ 75 | public static final String VERSION = "v0.4.5"; 76 | 77 | /** 78 | * The path of the launcher's cache files. 79 | */ 80 | public static final String CACHE_PATH = CommonConfig.GLASS_PATH + "cache/"; 81 | 82 | /** 83 | * The path of the Java binary running the launcher. 84 | */ 85 | public static final String JAVA_BIN = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; 86 | 87 | /** 88 | * JSON format map that reduces clutter and makes the end JSON easy to read. 89 | */ 90 | @Getter @Setter private static String easyMineLauncherFile; 91 | 92 | /** 93 | * Gets the OS of the user. 94 | * @return "windows" | "osx" | "unix" 95 | */ 96 | private static String getOSString() { 97 | String os = (System.getProperty("os.name")).toLowerCase(); 98 | if (os.contains("win")) { 99 | return "windows"; 100 | } else if (os.contains("mac")) { 101 | return "osx"; 102 | } else { 103 | return "linux"; 104 | } 105 | } 106 | 107 | /** 108 | * Gets the %appdata% (or OS equivalent) folder of the given folder name. 109 | * @param name Data folder name. 110 | * @return A full path to the data folder. 111 | */ 112 | private static String getDataPath(String name) { 113 | if (OS.equals("windows")) { 114 | return System.getenv("AppData").replaceAll("\\\\", "/") + "/" + name + "/"; 115 | } else if (OS.equals("osx")) { 116 | return System.getProperty("user.home") + "/Library/Application Support/" + name + "/"; 117 | } else { 118 | return System.getProperty("user.home") + "/" + name + "/"; 119 | } 120 | } 121 | 122 | /** 123 | * Gets the path of the supplied instance. 124 | * @param instance Instance name. 125 | * @return Returns the full path of the instance. 126 | * @exception IllegalArgumentException Thrown when null or an empty string is supplied. 127 | */ 128 | public static String getInstancePath(String instance) throws IllegalArgumentException { 129 | if (instance == null || instance.isEmpty()) { 130 | throw new IllegalArgumentException("Instance cannot be empty or null!"); 131 | } 132 | return CommonConfig.GLASS_PATH + "instances/" + instance + "/"; 133 | } 134 | 135 | @Getter private static final Map GLASS_DEPS = Collections.unmodifiableMap(new HashMap() {{ 136 | put("http://easyminelauncher.bonsaimind.org/EasyMineLauncher_v1.0.jar", "D7873F0A7A97AD78DB711BAF7D24B795"); 137 | /*put("https://repo1.maven.org/maven2/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar", "310f5841387183aca7900fead98d4858"); 138 | put("https://repo1.maven.org/maven2/com/github/ganskef/littleproxy-mitm/1.1.0/littleproxy-mitm-1.1.0.jar", "B1FD7C2BFCD32BCF5873D298484DABBA"); 139 | put("https://github.com/adamfisk/LittleProxy/releases/download/littleproxy-1.1.2/littleproxy-1.1.2-littleproxy-shade.jar", "05613C6D1BB1A8F826711BA54569311E"); 140 | put("https://repo1.maven.org/maven2/commons-io/commons-io/2.6/commons-io-2.6.jar", "467c2a1f64319c99b5faf03fc78572af"); 141 | put("https://repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk15on/1.65/bcprov-jdk15on-1.65.jar", "299b546652c9e1903685018342da0db0"); 142 | put("https://repo1.maven.org/maven2/org/bouncycastle/bcpkix-jdk15on/1.65/bcpkix-jdk15on-1.65.jar", "3364a7fa22395551658c1da1a88945d4"); 143 | put("https://repo1.maven.org/maven2/com/atlassian/commonmark/commonmark/0.14.0/commonmark-0.14.0.jar", "c2a58cfdb4c7cfe8ce0e0a91e2ed2297");*/ 144 | }}); 145 | 146 | static { 147 | if (Config.OS.equals("windows")) { 148 | destDirBypass = "\\\\?\\"; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/proxy/web/HttpSkinHandler.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.proxy.web; 2 | 3 | import com.google.gson.Gson; 4 | import com.sun.net.httpserver.Headers; 5 | import com.sun.net.httpserver.HttpExchange; 6 | import com.sun.net.httpserver.HttpHandler; 7 | import net.glasslauncher.legacy.jsontemplate.Profile; 8 | import net.glasslauncher.legacy.jsontemplate.ProfileProperties; 9 | import net.glasslauncher.legacy.jsontemplate.TextureURLs; 10 | import net.glasslauncher.legacy.jsontemplate.Textures; 11 | 12 | import javax.imageio.ImageIO; 13 | import java.awt.image.BufferedImage; 14 | import java.io.ByteArrayOutputStream; 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.io.OutputStream; 18 | import java.net.URL; 19 | import java.nio.charset.StandardCharsets; 20 | import java.util.Base64; 21 | import java.util.Map; 22 | 23 | public class HttpSkinHandler implements HttpHandler { 24 | private int isCape; 25 | 26 | public HttpSkinHandler(int isCape) throws IllegalArgumentException { 27 | if (isCape >1 || isCape <0) { 28 | throw new IllegalArgumentException("Value must be 0 or 1."); 29 | } 30 | this.isCape = isCape; 31 | } 32 | 33 | public void handle(HttpExchange t) throws IOException { 34 | try { 35 | String path = t.getRequestURI().toString(); 36 | String username = path.substring(path.lastIndexOf('/') + 1); 37 | 38 | if (username.toLowerCase().endsWith(".png")) { 39 | username = username.substring(0, username.lastIndexOf(".png")); 40 | } 41 | 42 | BufferedImage texture; 43 | try { 44 | texture = getCapeSkin(username, isCape); 45 | } catch (Exception e) { 46 | if (e.getMessage().contains("429")) { 47 | t.sendResponseHeaders(429, 0); 48 | } else { 49 | t.sendResponseHeaders(500, 0); 50 | } 51 | t.close(); 52 | return; 53 | } 54 | 55 | if (texture == null) { 56 | t.sendResponseHeaders(404, 0); 57 | t.close(); 58 | return; 59 | } 60 | 61 | byte[] response = imageToBytes(texture); 62 | 63 | Headers headers = t.getResponseHeaders(); 64 | headers.set("Content-Type", "image/png"); 65 | t.sendResponseHeaders(200, response.length); 66 | OutputStream os = t.getResponseBody(); 67 | os.write(response); 68 | os.close(); 69 | } catch (Exception e) { 70 | System.out.println("glass-netfix: Failed to handle skin:"); 71 | e.printStackTrace(); 72 | t.sendResponseHeaders(500, 0); 73 | t.close(); 74 | } 75 | } 76 | 77 | public static BufferedImage[] getImages(String username) throws IOException { 78 | String uuid = WebUtils.getUUID(username); 79 | if (uuid == null) { 80 | return null; 81 | } 82 | Profile profile = (new Gson()).fromJson(WebUtils.getStringFromURL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid), Profile.class); 83 | 84 | // It doesnt work if I just try casting the object. Don't ask why. 85 | ProfileProperties properties = profile.getProperties()[0]; 86 | 87 | byte[] base64 = (properties.getValue()).getBytes(StandardCharsets.UTF_8); 88 | Textures textures = (new Gson()).fromJson(new String(Base64.getDecoder().decode(base64)), Textures.class); 89 | 90 | TextureURLs textureURLs = textures.getTextures(); 91 | Map skin = textureURLs.getSKIN(); 92 | Map cape = textureURLs.getCAPE(); 93 | 94 | BufferedImage[] images = new BufferedImage[2]; 95 | 96 | try { 97 | File skinCache = WebUtils.checkCache(username + "/skin.png"); 98 | URL skinUrl = new URL((String) skin.get("url")); 99 | if (skinCache != null) { 100 | skinUrl = skinCache.toURI().toURL(); 101 | } 102 | images[0] = ImageIO.read(skinUrl); 103 | 104 | } catch (Exception e) { 105 | images[0] = null; 106 | } 107 | 108 | try { 109 | File capeCache = WebUtils.checkCache(username + "/cape.png"); 110 | URL capeUrl = new URL((String) cape.get("url")); 111 | if (capeCache != null) { 112 | capeUrl = capeCache.toURI().toURL(); 113 | } 114 | images[1] = ImageIO.read(capeUrl); 115 | } catch (Exception e) { 116 | images[1] = null; 117 | } 118 | return images; 119 | } 120 | 121 | /** 122 | * Checks to see if skin needs updating and updates if necessary. 123 | * 124 | * @param username Username to check. 125 | * @param isCape 126 | */ 127 | public static BufferedImage getCapeSkin(String username, int isCape) throws IOException { 128 | String[] image = new String[] {"/skin.png", "/cape.png"}; 129 | WebUtils.makeCacheFolders(username); 130 | File metaFile = WebUtils.checkCache(username + "/meta"); 131 | if (metaFile == null) { 132 | try { 133 | BufferedImage[] images = getImages(username); 134 | 135 | if (images == null) { 136 | return null; 137 | } 138 | if (images[0] != null) { 139 | ImageIO.write(images[0], "png", WebUtils.getCache(username + "/skin.png")); 140 | } 141 | if (images[1] != null) { 142 | ImageIO.write(images[1], "png", WebUtils.getCache(username + "/cape.png")); 143 | } 144 | WebUtils.putCache(WebUtils.getCache(username + "/meta"), null); 145 | } catch (Exception e) { 146 | e.printStackTrace(); 147 | } 148 | } 149 | File imageFile = WebUtils.getCache(username + image[isCape]); 150 | BufferedImage bufferedImage; 151 | if (imageFile.exists()) { 152 | bufferedImage = ImageIO.read(imageFile); 153 | } else { 154 | bufferedImage = null; 155 | } 156 | return bufferedImage; 157 | } 158 | 159 | public static byte[] imageToBytes(BufferedImage image) throws IOException { 160 | ByteArrayOutputStream imageBytes = new ByteArrayOutputStream(); 161 | ImageIO.write(image, "png", imageBytes); 162 | imageBytes.flush(); 163 | byte[] response = imageBytes.toByteArray(); 164 | imageBytes.close(); 165 | return response; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/mcversions.json: -------------------------------------------------------------------------------- 1 | { 2 | "client": { 3 | "1.5.2": { 4 | "url": "465378c9dc2f779ae1d6e8046ebc46fb53a57968" 5 | }, 6 | "1.5.1": { 7 | "url": "047136381a552f34b1963c43304a1ad4dc0d2d8e" 8 | }, 9 | "1.5": { 10 | "url": "a3da981fc9b875a51975d8f8100cc0c201c2ce54" 11 | }, 12 | "1.4.7": { 13 | "url": "53ed4b9d5c358ecfff2d8b846b4427b888287028" 14 | }, 15 | "1.4.5": { 16 | "url": "7a8a963ababfec49406e1541d3a87198e50604e5" 17 | }, 18 | "1.4.6": { 19 | "url": "116758f41b32e8d1a71a4ad6236579acd724bca7" 20 | }, 21 | "1.4.4": { 22 | "url": "b9b2a9e9adf1bc834647febc93a4222b4fd6e403" 23 | }, 24 | "1.4.3": { 25 | "url": "f7274b201219b5729055bf85683eb6ef4f8024b4" 26 | }, 27 | "1.4.2": { 28 | "url": "42d6744cfbbd2958f9e6688dd6e78d86d658d0d4" 29 | }, 30 | "1.4.1": { 31 | "url": "67604a9c206697032165fc067b6255e333e06275" 32 | }, 33 | "1.4": { 34 | "url": "2007097b53d3eb43b2c1f3f78caab4a4ef690c7a" 35 | }, 36 | "1.3.2": { 37 | "url": "c2efd57c7001ddf505ca534e54abf3d006e48309" 38 | }, 39 | "1.3.1": { 40 | "url": "33167e71e85ab8e6ddbe168bc67f6ec19d708d62" 41 | }, 42 | "1.3": { 43 | "url": "4dfb8098b39c122f2aad13768d3f0d04db910f12" 44 | }, 45 | "1.2.5": { 46 | "url": "4a2fac7504182a97dcbcd7560c6392d7c8139928" 47 | }, 48 | "1.2.4": { 49 | "url": "ad6d1fe7455857269d4185cb8f24e62cc0241aaf" 50 | }, 51 | "1.2.3": { 52 | "url": "5134e433afeba375c00bbdcd8aead1d3222813ee" 53 | }, 54 | "1.2.2": { 55 | "url": "1dadfc4de6898751f547f24f72c7271218e4e28f" 56 | }, 57 | "1.2.1": { 58 | "url": "c7662ac43dd04bfd677694a06972a2aaaf426505" 59 | }, 60 | "1.1": { 61 | "url": "f690d4136b0026d452163538495b9b0e8513d718" 62 | }, 63 | "1.0": { 64 | "url": "b679fea27f2284836202e9365e13a82552092e5d" 65 | }, 66 | "b1.8.1": { 67 | "url": "6b562463ccc2c7ff12ff350a2b04a67b3adcd37b" 68 | }, 69 | "b1.8": { 70 | "url": "3139e9c29b2c74f59ea04de760ac2af5bc21b410" 71 | }, 72 | "b1.7.3": { 73 | "url": "43db9b498cb67058d2e12d394e6507722e71bb45" 74 | }, 75 | "b1.7.2": { 76 | "url": "7dc50cc5e2ff204a7283f0c7d38cd0370b49875b" 77 | }, 78 | "b1.7": { 79 | "url": "ad7960853437bcab86bd72c4a1b95f6fe19f4258" 80 | }, 81 | "b1.6.6": { 82 | "url": "f95fe05711d09553ca2a9089f981741c13d6b8c4" 83 | }, 84 | "b1.6.5": { 85 | "url": "90ed9854b43c4d031ed07381ea3ae3071a8bba6f" 86 | }, 87 | "b1.6.4": { 88 | "url": "b5d3bdb8a7b12d163651f4787ac6ca14689aab9e" 89 | }, 90 | "b1.6.3": { 91 | "url": "924e36dbb7c64abb30a95fe35f5affb5176f6cbc" 92 | }, 93 | "b1.6.2": { 94 | "url": "e8aa50949b077b672be2e651ea3f7b1bbd9020e1" 95 | }, 96 | "b1.6.1": { 97 | "url": "63a66d6d145696296bdaaeaba0a42f738b87a362" 98 | }, 99 | "b1.6": { 100 | "url": "ecc0288d218fd7479027a17c150cbf283fa950a1" 101 | }, 102 | "b1.5_01": { 103 | "url": "e2a692e5e8160c84b29c834ecbf398618db9749c" 104 | }, 105 | "b1.5": { 106 | "url": "f5ce1699cd728213c21054fa2f1490d162b002b4" 107 | }, 108 | "b1.4_01": { 109 | "url": "6f157f26955c35006c1afa8b0479e0ce785fb864" 110 | }, 111 | "b1.4": { 112 | "url": "f6dbca5223ea2a7e89806e93d0b18162b2d58c20" 113 | }, 114 | "b1.3_01": { 115 | "url": "add3809d2c075e985d4b583632dac3d9c3872945" 116 | }, 117 | "b1.3b": { 118 | "url": "e19cfb3a2043f185c44237ef05eac80e8ad2d8e7" 119 | }, 120 | "b1.2_02": { 121 | "url": "093f371e1a05d89664cfb8068d607953687d5d94" 122 | }, 123 | "b1.2_01": { 124 | "url": "f71a5b58c9bd0e458878d78a34c9fb35e97d5222" 125 | }, 126 | "b1.2": { 127 | "url": "ba05d7a97926c61c03cf956f7ae92f3bede9474e" 128 | }, 129 | "b1.1_02": { 130 | "url": "e1c682219df45ebda589a557aadadd6ed093c86c" 131 | }, 132 | "b1.1_01": { 133 | "url": "6d778940f48389a2741f03c9f17f3c57476fb208" 134 | }, 135 | "b1.0.2": { 136 | "url": "76d35cb452e739bd4780e835d17faf0785d755f9" 137 | }, 138 | "b1.0_01": { 139 | "url": "4caf69885b64132e42d3ce49996dbdb1691d7111" 140 | }, 141 | "b1.0": { 142 | "url": "93faf3398ebf8008d59852dc3c2b22b909ca8a49" 143 | }, 144 | "a1.2.6": { 145 | "url": "a68c817afd6c05c253ba5462287c2c19bbb57935" 146 | }, 147 | "a1.2.5": { 148 | "url": "f48c7b6442ad8d01099ecee1c7c7332f1b1a80da" 149 | }, 150 | "a1.2.4_01": { 151 | "url": "7be6298b05d1b0832ab45467a87a425640bc6bf0" 152 | }, 153 | "a1.2.3_04": { 154 | "url": "7f60cb9d0d40af20001d15287b78aa26a217a910" 155 | }, 156 | "a1.2.3_02": { 157 | "url": "dc61158e1df763f87483abb6ab540dc1c42e63c4" 158 | }, 159 | "a1.2.3_01": { 160 | "url": "1d46e65022f3a7cf4b8ad30ee5a8d52b3b2b9486" 161 | }, 162 | "a1.2.3": { 163 | "url": "f4be258122cb62208b350cd2068685ad859bb447" 164 | }, 165 | "a1.2.2b": { 166 | "url": "1c28c8431392641045b59e98a81877d7c94ff0ca" 167 | }, 168 | "a1.2.2a": { 169 | "url": "7d9d85eaca9627d3a40e6d122182f2d22d39dbf9" 170 | }, 171 | "a1.2.1_01": { 172 | "url": "e4226f9ba622634e3101681bc641eec7ee9e72fd" 173 | }, 174 | "a1.2.1": { 175 | "url": "e4226f9ba622634e3101681bc641eec7ee9e72fd" 176 | }, 177 | "a1.2.0_02": { 178 | "url": "b99da0a683e6dc1ade4df1bf159e021ad07d4fca" 179 | }, 180 | "a1.2.0_01": { 181 | "url": "332bfe7bf26f6a5cc93ee85e6759ce33784409d0" 182 | }, 183 | "a1.2.0": { 184 | "url": "8632ea716fd083c2975f16d612306fd80bee46db" 185 | }, 186 | "a1.1.2_01": { 187 | "url": "daa4b9f192d2c260837d3b98c39432324da28e86" 188 | }, 189 | "a1.1.2": { 190 | "url": "f9b4b66f9c18bf4800d80f1c8865a837f92c6105" 191 | }, 192 | "a1.1.0": { 193 | "url": "d58d1db929994ff383bdbe6fed31887e04b965c3" 194 | }, 195 | "a1.0.17_04": { 196 | "url": "61cb4c717981f34bf90e8502d2eb8cf2aa6db0cd" 197 | }, 198 | "a1.0.17_02": { 199 | "url": "39f20ee472a40322e034643a8d1668836f5052bd" 200 | }, 201 | "a1.0.16": { 202 | "url": "98ce80c7630ccb3bb38687ff98bfd18935d49a57" 203 | }, 204 | "a1.0.15": { 205 | "url": "03edaff812bedd4157a90877e779d7b7ecf78e97" 206 | }, 207 | "a1.0.14": { 208 | "url": "9b4b90d8def2a680b7c9eca40dd03e2266c8977a" 209 | }, 210 | "a1.0.11": { 211 | "url": "d7ceb02909d0e1031a99ff4d8053d3f4abfbb2da" 212 | }, 213 | "a1.0.5_01": { 214 | "url": "73f569bf5556580979606049204835ae1a54f04d" 215 | }, 216 | "a1.0.4": { 217 | "url": "e5838277b3bb193e58408713f1fc6e005c5f3c0c" 218 | }, 219 | "inf-20100618": { 220 | "url": "89eab2c1a353707cc00f074dffba9cb7a4f5e304" 221 | }, 222 | "c0.30_01c": { 223 | "url": "54622801f5ef1bcc1549a842c5b04cb5d5583005" 224 | }, 225 | "c0.0.13a": { 226 | "url": "936d575b1ab1a04a341ad43d76e441e88d2cd987" 227 | }, 228 | "c0.0.13a_03": { 229 | "url": "7ba9e63aec8a15a99ecd47900c848cdce8a51a03" 230 | }, 231 | "c0.0.11a": { 232 | "url": "3a799f179b6dcac5f3a46846d687ebbd95856984" 233 | }, 234 | "rd-161348": { 235 | "url": "6323bd14ed7f83852e17ebc8ec418e55c97ddfe4" 236 | }, 237 | "rd-160052": { 238 | "url": "b100be8097195b6c9112046dc6a80d326c8df839" 239 | }, 240 | "rd-20090515": { 241 | "url": "6323bd14ed7f83852e17ebc8ec418e55c97ddfe4" 242 | }, 243 | "rd-132328": { 244 | "url": "12dace5a458617d3f90337a7ebde86c0593a6899" 245 | }, 246 | "rd-132211": { 247 | "url": "393e8d4b4d708587e2accd7c5221db65365e1075" 248 | } 249 | } 250 | } -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/InstanceManagerWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import net.glasslauncher.common.CommonConfig; 4 | import net.glasslauncher.common.FileUtils; 5 | import net.glasslauncher.legacy.components.HintTextField; 6 | import net.glasslauncher.legacy.components.ScalingButton; 7 | import net.glasslauncher.legacy.util.InstanceManager; 8 | 9 | import javax.swing.BoxLayout; 10 | import javax.swing.JComboBox; 11 | import javax.swing.JDialog; 12 | import javax.swing.JPanel; 13 | import javax.swing.JScrollPane; 14 | import javax.swing.JTabbedPane; 15 | import java.awt.Component; 16 | import java.awt.Desktop; 17 | import java.awt.Dimension; 18 | import java.awt.FileDialog; 19 | import java.awt.Frame; 20 | import java.awt.GridLayout; 21 | import java.awt.event.WindowAdapter; 22 | import java.awt.event.WindowEvent; 23 | import java.io.File; 24 | import java.io.IOException; 25 | import java.util.Objects; 26 | 27 | class InstanceManagerWindow extends JDialog { 28 | private JPanel deletePanel = new JPanel(); 29 | 30 | InstanceManagerWindow(Frame frame) { 31 | super(frame); 32 | setModal(true); 33 | setLayout(new GridLayout()); 34 | setResizable(false); 35 | setTitle("Instance Manager"); 36 | addWindowListener(new WindowAdapter() { 37 | public void windowClosing(WindowEvent we) { 38 | Main.getLogger().info("Closing instance manager..."); 39 | dispose(); 40 | } 41 | } 42 | ); 43 | setBounds(0, 0, 580, 340); 44 | 45 | JTabbedPane tabbedPane = new JTabbedPane(); 46 | tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); 47 | tabbedPane.setBounds(0, 0, 580, 340); 48 | tabbedPane.addTab("Create", makeCreateTab()); 49 | tabbedPane.addTab("Delete Instance", makeDeleteTab()); 50 | 51 | add(tabbedPane); 52 | 53 | setLocationRelativeTo(null); 54 | setVisible(true); 55 | } 56 | 57 | private JPanel makeCreateTab() { 58 | JPanel createPanel = new JPanel(); 59 | createPanel.setBounds(0, 0, 580, 340); 60 | createPanel.setLayout(null); 61 | 62 | // Local Modpack zip install. 63 | HintTextField installModpackDir = new HintTextField("Paste the path to your Modpack zip here!"); 64 | installModpackDir.setBounds(155, 5, 355, 22); 65 | 66 | ScalingButton installModpackDirButton = new ScalingButton(); 67 | installModpackDirButton.setText("Install Local Modpack"); 68 | installModpackDirButton.setBounds(5, 5, 150, 22); 69 | installModpackDirButton.addActionListener(event -> { 70 | ProgressWindow progressWindow = new ProgressWindow(this, "Installing Local Modpack..."); 71 | Thread thread = new Thread(() -> { 72 | InstanceManager.installModpack(installModpackDir.getText(), progressWindow); 73 | progressWindow.dispose(); 74 | }); 75 | progressWindow.setThread(thread); 76 | thread.start(); 77 | if (progressWindow.isDisplayable()) { 78 | progressWindow.setVisible(true); 79 | } 80 | }); 81 | 82 | ScalingButton instanceZipSelectButton = new ScalingButton(); 83 | instanceZipSelectButton.setText("..."); 84 | instanceZipSelectButton.addActionListener(event -> { 85 | FileDialog fileChooser = new FileDialog(this, "Select Modpack"); 86 | fileChooser.setFilenameFilter((e, str) -> { 87 | return str.endsWith(".zip"); 88 | }); 89 | fileChooser.setVisible(true); 90 | try { 91 | String file = fileChooser.getFiles()[0].getAbsolutePath(); 92 | installModpackDir.setText(file); 93 | } catch (NullPointerException | ArrayIndexOutOfBoundsException ignored) {} 94 | }); 95 | instanceZipSelectButton.setBounds(515, 5, 30, 22); 96 | 97 | // Remote Modpack zip install. 98 | HintTextField installModpackURL = new HintTextField("Paste the URL to your Modpack zip here!"); 99 | installModpackURL.setBounds(155, 34, 390, 22); 100 | 101 | ScalingButton installModpackURLButton = new ScalingButton(); 102 | installModpackURLButton.setText("Install Modpack from URL"); 103 | installModpackURLButton.setBounds(5, 34, 150, 22); 104 | installModpackURLButton.addActionListener(event -> { 105 | ProgressWindow progressWindow = new ProgressWindow(this, "Installing Remote Modpack..."); 106 | Thread thread = new Thread(() -> { 107 | InstanceManager.installModpack(installModpackURL.getText(), progressWindow); 108 | progressWindow.dispose(); 109 | }); 110 | progressWindow.setThread(thread); 111 | thread.start(); 112 | if (progressWindow.isDisplayable()) { 113 | progressWindow.setVisible(true); 114 | } 115 | }); 116 | 117 | // Make blank instance 118 | HintTextField instanceName = new HintTextField("Instance name"); 119 | instanceName.setBounds(155, 63, 194, 22); 120 | 121 | JComboBox instanceVersion = new JComboBox<>(); 122 | for (String version : Config.getMcVersions().getClient().keySet()) { 123 | instanceVersion.addItem(version); 124 | } 125 | instanceVersion.setBounds(351, 63, 194, 22); 126 | 127 | ScalingButton instanceVersionButton = new ScalingButton(); 128 | instanceVersionButton.setText("Create Blank Instance"); 129 | instanceVersionButton.addActionListener((e) -> { 130 | ProgressWindow progressWindow = new ProgressWindow(this, "Creating New Instance..."); 131 | Thread thread = new Thread(() -> { 132 | InstanceManager.createBlankInstance((String) instanceVersion.getSelectedItem(), instanceName.getText(), progressWindow); 133 | progressWindow.dispose(); 134 | }); 135 | progressWindow.setThread(thread); 136 | thread.start(); 137 | if (progressWindow.isDisplayable()) { 138 | progressWindow.setVisible(true); 139 | } 140 | }); 141 | instanceVersionButton.setBounds(5, 63, 150, 22); 142 | 143 | ScalingButton instanceFolderButton = new ScalingButton(); 144 | instanceFolderButton.setText("Open Instances Folder"); 145 | instanceFolderButton.addActionListener((e) -> { 146 | Main.getLogger().info("Opening instances folder..."); 147 | try { 148 | Desktop.getDesktop().open(new File(CommonConfig.GLASS_PATH + "instances")); 149 | } catch (IOException ex) { 150 | ex.printStackTrace(); 151 | } 152 | }); 153 | instanceFolderButton.setBounds(5, 257, 150, 22); 154 | 155 | createPanel.add(installModpackDir); 156 | createPanel.add(installModpackDirButton); 157 | createPanel.add(instanceZipSelectButton); 158 | createPanel.add(installModpackURL); 159 | createPanel.add(installModpackURLButton); 160 | createPanel.add(instanceName); 161 | createPanel.add(instanceVersion); 162 | createPanel.add(instanceVersionButton); 163 | createPanel.add(instanceFolderButton); 164 | return createPanel; 165 | } 166 | 167 | private JScrollPane makeDeleteTab() { 168 | deletePanel.setLayout(new BoxLayout(deletePanel, BoxLayout.Y_AXIS)); 169 | 170 | updateInstanceList(); 171 | 172 | return new JScrollPane(deletePanel); 173 | } 174 | 175 | private void updateInstanceList() { 176 | (new File(CommonConfig.GLASS_PATH + "instances")).mkdirs(); 177 | deletePanel.removeAll(); 178 | deletePanel.repaint(); 179 | for (File instance : (Objects.requireNonNull(new File(CommonConfig.GLASS_PATH + "instances").listFiles()))) { 180 | if (instance.isDirectory()) { 181 | ScalingButton deleteButton = new ScalingButton(); 182 | deleteButton.setText("Delete \"" + instance.getName() + "\"."); 183 | deleteButton.setMinimumSize(new Dimension(540, 22)); 184 | deleteButton.setMaximumSize(new Dimension(540, 22)); 185 | deleteButton.setAlignmentX(Component.CENTER_ALIGNMENT); 186 | deleteButton.addActionListener((e) -> { 187 | try { 188 | FileUtils.delete(instance); 189 | updateInstanceList(); 190 | } catch (Exception ex) { 191 | ex.printStackTrace(); 192 | } 193 | }); 194 | deletePanel.add(deleteButton); 195 | } 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/MainWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import net.glasslauncher.common.CommonConfig; 4 | import net.glasslauncher.legacy.components.DirtPanel; 5 | import net.glasslauncher.legacy.components.HintPasswordField; 6 | import net.glasslauncher.legacy.components.HintTextField; 7 | import net.glasslauncher.legacy.components.Logo; 8 | import net.glasslauncher.legacy.components.ScalingButton; 9 | import net.glasslauncher.legacy.mc.LaunchArgs; 10 | import net.glasslauncher.legacy.mc.Wrapper; 11 | 12 | import javax.swing.BorderFactory; 13 | import javax.swing.DefaultComboBoxModel; 14 | import javax.swing.JComboBox; 15 | import javax.swing.JFrame; 16 | import javax.swing.JPanel; 17 | import javax.swing.JPasswordField; 18 | import javax.swing.JScrollPane; 19 | import javax.swing.JTextPane; 20 | import javax.swing.event.HyperlinkEvent; 21 | import java.awt.BorderLayout; 22 | import java.awt.Color; 23 | import java.awt.Desktop; 24 | import java.awt.Dimension; 25 | import java.awt.Frame; 26 | import java.awt.GridLayout; 27 | import java.awt.Insets; 28 | import java.awt.Panel; 29 | import java.awt.Rectangle; 30 | import java.awt.Toolkit; 31 | import java.awt.event.FocusEvent; 32 | import java.awt.event.FocusListener; 33 | import java.awt.event.WindowAdapter; 34 | import java.awt.event.WindowEvent; 35 | import java.io.File; 36 | import java.util.Scanner; 37 | 38 | class MainWindow extends JFrame { 39 | private int orgWidth = 854; 40 | private int orgHeight = 480; 41 | 42 | protected JPasswordField password; 43 | protected HintTextField username; 44 | 45 | private final Panel mainPanel = new Panel(); 46 | 47 | private JComboBox instsel; 48 | 49 | /** 50 | * Sets up the main window object. 51 | * @// TODO: 20/09/2019 To be split into a proper class with multiple functions. 52 | * @param console The console for the launcher. Used to close the console. 53 | */ 54 | MainWindow(Frame console) { 55 | // Setting the size, icon, location and layout of the launcher 56 | Insets insets = getInsets(); 57 | Main.getLogger().info("Starting..."); 58 | setTitle("Glass Launcher " + Config.VERSION); 59 | setIconImage(Toolkit.getDefaultToolkit().createImage(MainWindow.class.getResource("assets/glass.png"))); 60 | setLayout(new GridLayout(1, 1)); 61 | setLocationRelativeTo(null); 62 | pack(); 63 | setPreferredSize(new Dimension(orgWidth + insets.left + insets.right, orgHeight + insets.top + insets.bottom)); 64 | setMinimumSize(new Dimension(650, 400)); 65 | 66 | // Container to make my brain hurt less 67 | mainPanel.setLayout(new BorderLayout()); 68 | add(mainPanel); 69 | 70 | // Makes it so the launcher closes when you press the close button 71 | addWindowListener(new WindowAdapter() { 72 | public void windowClosing(WindowEvent we) { 73 | console.dispose(); 74 | dispose(); 75 | System.exit(0); 76 | } 77 | } 78 | ); 79 | 80 | makeGUI(); 81 | } 82 | 83 | private void makeGUI() { 84 | 85 | JScrollPane blogcontainer = makeBlog(); 86 | 87 | // Login form 88 | DirtPanel loginform = new DirtPanel(); 89 | loginform.setLayout(new BorderLayout()); 90 | 91 | JPanel loginpanel = new JPanel(); 92 | loginpanel.setLayout(null); 93 | loginpanel.setOpaque(false); 94 | loginpanel.setPreferredSize(new Dimension(255, 100)); 95 | 96 | // Logo 97 | Logo logo = new Logo(); 98 | 99 | // Username field 100 | username = new HintTextField("Username or Email"); 101 | if (Config.getLauncherConfig().getLastUsedName() != null) { 102 | username.setText(Config.getLauncherConfig().getLastUsedName()); 103 | } 104 | username.setBounds(0, 14, 166, 22); 105 | username.addActionListener((e) -> { 106 | login(); 107 | }); 108 | 109 | // Password field 110 | password = new HintPasswordField("Password"); 111 | password.setBounds(0, 40, 166, 22); 112 | password.addActionListener((e) -> { 113 | login(); 114 | }); 115 | 116 | // Auto selection of username or password. 117 | addWindowListener(new WindowAdapter() { 118 | public void windowOpened(WindowEvent e){ 119 | if (username.getText().isEmpty()) { 120 | username.grabFocus(); 121 | } else { 122 | password.grabFocus(); 123 | } 124 | } 125 | }); 126 | 127 | // Instance selector 128 | instsel = new JComboBox<>(); 129 | refreshInstanceList(); 130 | instsel.setBounds(0, 66, 166, 22); 131 | 132 | // Options button 133 | ScalingButton options = new ScalingButton(); 134 | options.setText("Options"); 135 | options.setBounds(168, 14, 70, 22); 136 | options.setOpaque(false); 137 | options.addActionListener(event -> { 138 | new OptionsWindow(this, (String) instsel.getSelectedItem()); 139 | }); 140 | 141 | // Login button 142 | ScalingButton login = new ScalingButton(); 143 | login.setText("Login"); 144 | login.setBounds(168, 40, 70, 22); 145 | login.setOpaque(false); 146 | login.addActionListener(event -> { 147 | login(); 148 | }); 149 | 150 | // Instance manager button 151 | ScalingButton instancesButton = new ScalingButton(); 152 | instancesButton.setText("Instances"); 153 | instancesButton.setBounds(168, 66, 70, 22); 154 | instancesButton.setOpaque(false); 155 | instancesButton.setMargin(new Insets(0, 0, 0, 0)); 156 | instancesButton.addActionListener(event -> { 157 | new InstanceManagerWindow(this); 158 | refreshInstanceList(); 159 | }); 160 | 161 | // Adding widgets and making the launcher visible 162 | loginform.add(logo, BorderLayout.WEST); 163 | loginform.add(loginpanel, BorderLayout.EAST); 164 | loginpanel.add(instsel); 165 | loginpanel.add(username); 166 | loginpanel.add(options); 167 | loginpanel.add(password); 168 | loginpanel.add(login); 169 | loginpanel.add(instancesButton); 170 | 171 | mainPanel.add(blogcontainer, BorderLayout.CENTER); 172 | mainPanel.add(loginform, BorderLayout.SOUTH); 173 | 174 | pack(); 175 | setLocationRelativeTo(null); 176 | setVisible(true); 177 | } 178 | 179 | private JScrollPane makeBlog() { 180 | String page = new Scanner(MainWindow.class.getResourceAsStream("assets/blog.html"), "UTF-8").useDelimiter("\\A").next(); 181 | page = page.replaceAll("\\$\\{root}\\$", MainWindow.class.getResource("assets/").toString()); 182 | JTextPane blog = new JTextPane(); 183 | blog.setContentType("text/html"); 184 | blog.setText(page); 185 | blog.setBorder(BorderFactory.createEmptyBorder()); 186 | blog.setEditable(false); 187 | blog.addHyperlinkListener(event -> { 188 | if (event.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) { 189 | try { 190 | Desktop.getDesktop().browse(event.getURL().toURI()); 191 | } catch (Exception e) { 192 | e.printStackTrace(); 193 | } 194 | } 195 | }); 196 | JScrollPane blogcontainer = new JScrollPane(blog); 197 | blogcontainer.setBorder(BorderFactory.createEmptyBorder()); 198 | blogcontainer.setBounds(new Rectangle(0, 0, mainPanel.getWidth(), mainPanel.getHeight() - 200)); 199 | return blogcontainer; 200 | } 201 | 202 | public void refreshInstanceList() { 203 | Main.getLogger().info("Refreshing instance list..."); 204 | instsel.setModel(new DefaultComboBoxModel<>()); 205 | File file = new File(CommonConfig.GLASS_PATH + "instances"); 206 | String[] instances = file.list((current, name) -> new File(current, name).isDirectory()); 207 | String lastUsedInstance = Config.getLauncherConfig().getLastUsedInstance(); 208 | boolean exists = false; 209 | if (instances != null) { 210 | for (String instance : instances) { 211 | if (instance.equals(lastUsedInstance)) { 212 | exists = true; 213 | } 214 | instsel.addItem(instance); 215 | } 216 | } 217 | if (lastUsedInstance != null && !lastUsedInstance.isEmpty() && exists) { 218 | instsel.setSelectedItem(lastUsedInstance); 219 | } 220 | } 221 | 222 | private void login() { 223 | Main.getLogger().info("Starting instance: " + instsel.getSelectedItem()); 224 | String pass = ""; 225 | if (password.getForeground() != Color.gray) { 226 | pass = String.valueOf(password.getPassword()); 227 | } 228 | String[] launchargs = {username.getText(), pass, (String) instsel.getSelectedItem()}; 229 | launchargs = (new LaunchArgs()).getArgs(launchargs); 230 | if (launchargs != null) { 231 | Config.getLauncherConfig().setLastUsedName(username.getText()); 232 | Config.getLauncherConfig().setLastUsedInstance((String) instsel.getSelectedItem()); 233 | Config.getLauncherConfig().saveFile(); 234 | Wrapper mc = new Wrapper(launchargs); 235 | mc.startMC(); 236 | } else { 237 | password.setText(""); 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/OptionsWindow.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy; 2 | 3 | import net.glasslauncher.common.CommonConfig; 4 | import net.glasslauncher.common.JsonConfig; 5 | import net.glasslauncher.legacy.jsontemplate.InstanceConfig; 6 | import net.glasslauncher.legacy.jsontemplate.Mod; 7 | import net.glasslauncher.legacy.jsontemplate.ModList; 8 | import net.glasslauncher.legacy.components.DragDropList; 9 | import net.glasslauncher.legacy.components.ScalingButton; 10 | import net.glasslauncher.legacy.util.InstanceManager; 11 | 12 | import javax.swing.JCheckBox; 13 | import javax.swing.JDialog; 14 | import javax.swing.JLabel; 15 | import javax.swing.JOptionPane; 16 | import javax.swing.JPanel; 17 | import javax.swing.JScrollPane; 18 | import javax.swing.JTabbedPane; 19 | import javax.swing.JTextField; 20 | import javax.swing.ListModel; 21 | import java.awt.Color; 22 | import java.awt.Dimension; 23 | import java.awt.FileDialog; 24 | import java.awt.Frame; 25 | import java.awt.GridLayout; 26 | import java.awt.event.WindowAdapter; 27 | import java.awt.event.WindowEvent; 28 | import java.io.File; 29 | import java.io.IOException; 30 | import java.nio.file.Files; 31 | import java.util.ArrayList; 32 | 33 | public class OptionsWindow extends JDialog { 34 | private JPanel panel; 35 | private InstanceConfig instanceConfig; 36 | private ModList modList; 37 | private ArrayList jarMods; 38 | DragDropList modDragDropList; 39 | 40 | private String instpath; 41 | private String instName; 42 | 43 | private JTextField javaargs; 44 | private JTextField minram; 45 | private JTextField maxram; 46 | private JCheckBox skinproxy; 47 | private JCheckBox capeproxy; 48 | private JCheckBox soundproxy; 49 | private JCheckBox loginproxy; 50 | 51 | /** 52 | * Sets up options window for given instance. 53 | * @param panel Frame object to block while open. 54 | * @param instance Target instance. 55 | */ 56 | public OptionsWindow(Frame panel, String instance) { 57 | super(panel); 58 | setModal(true); 59 | setLayout(new GridLayout()); 60 | setResizable(false); 61 | setTitle("Instance Options"); 62 | this.panel = new JPanel(); 63 | add(this.panel); 64 | 65 | instName = instance; 66 | instpath = CommonConfig.GLASS_PATH + "instances/" + instance + "/"; 67 | if (!(new File(instpath)).exists()) { 68 | JOptionPane.showMessageDialog(this, "Selected instance does not exist, or one hasn't been selected.", "Warning", JOptionPane.WARNING_MESSAGE); 69 | return; 70 | } 71 | instanceConfig = (InstanceConfig) JsonConfig.loadConfig(instpath + "instance_config.json", InstanceConfig.class); 72 | if (instanceConfig == null) { 73 | instanceConfig = new InstanceConfig(instpath + "instance_config.json"); 74 | } 75 | modList = (ModList) JsonConfig.loadConfig(instpath + "mods/mods.json", ModList.class); 76 | if (modList == null) { 77 | modList = new ModList(instpath + "mods/mods.json"); 78 | } 79 | 80 | JTabbedPane tabpane = new JTabbedPane(); 81 | tabpane.setOpaque(false); 82 | tabpane.setPreferredSize(new Dimension(837, 448 - tabpane.getInsets().top - tabpane.getInsets().bottom)); 83 | 84 | tabpane.addTab("Settings", makeInstSettings()); 85 | tabpane.addTab("Mods", makeMods()); 86 | 87 | addWindowListener( 88 | new WindowAdapter() { 89 | public void windowClosing(WindowEvent we) { 90 | instanceConfig.setJavaArgs(javaargs.getText()); 91 | instanceConfig.setMaxRam(maxram.getText()); 92 | instanceConfig.setMinRam(minram.getText()); 93 | instanceConfig.setProxySkin(skinproxy.isSelected()); 94 | instanceConfig.setProxyCape(capeproxy.isSelected()); 95 | instanceConfig.setProxySound(soundproxy.isSelected()); 96 | instanceConfig.setProxyLogin(loginproxy.isSelected()); 97 | 98 | modList.setJarMods(new ArrayList<>()); 99 | ListModel listModel = modDragDropList.model; 100 | for (int i = 0; i< listModel.getSize(); i++) { 101 | try { 102 | Mod mod = (Mod) listModel.getElementAt(i); 103 | modList.getJarMods().add(mod); 104 | } catch (Exception e) { 105 | e.printStackTrace(); 106 | } 107 | } 108 | try { 109 | modList.saveFile(); 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | } 113 | try { 114 | instanceConfig.saveFile(); 115 | } catch (Exception e) { 116 | e.printStackTrace(); 117 | } 118 | dispose(); 119 | } 120 | } 121 | ); 122 | 123 | this.panel.add(tabpane); 124 | pack(); 125 | setLocationRelativeTo(null); 126 | setVisible(true); 127 | } 128 | 129 | /** 130 | * Makes the things that do the thing for the instance JSON. 131 | * 132 | * @return The panel object containing all created components. 133 | */ 134 | private JPanel makeInstSettings() { 135 | JPanel instsettings = new JPanel(); 136 | instsettings.setOpaque(false); 137 | instsettings.setLayout(null); 138 | 139 | JLabel javaargslabel = new JLabel("Java Arguments:"); 140 | javaargslabel.setBounds(20, 22, 100, 20); 141 | instsettings.add(javaargslabel); 142 | 143 | javaargs = new JTextField(); 144 | javaargs.setBounds(150, 20, 400, 24); 145 | javaargs.setText(instanceConfig.getJavaArgs()); 146 | instsettings.add(javaargs); 147 | 148 | JLabel ramalloclabel = new JLabel("RAM Allocation:"); 149 | ramalloclabel.setBounds(20, 50, 100, 20); 150 | instsettings.add(ramalloclabel); 151 | 152 | JLabel maxramlabel = new JLabel("Maximum:"); 153 | maxramlabel.setBounds(150, 50, 65, 20); 154 | instsettings.add(maxramlabel); 155 | 156 | maxram = new JTextField(); 157 | maxram.setBounds(245, 48, 100, 24); 158 | maxram.setText(instanceConfig.getMaxRam()); 159 | instsettings.add(maxram); 160 | 161 | JLabel minramlabel = new JLabel("Minimum:"); 162 | minramlabel.setBounds(360, 50, 65, 20); 163 | instsettings.add(minramlabel); 164 | 165 | minram = new JTextField(); 166 | minram.setBounds(450, 48, 100, 24); 167 | minram.setText(instanceConfig.getMinRam()); 168 | instsettings.add(minram); 169 | 170 | JLabel skinproxylabel = new JLabel("Enable Skin Proxy:"); 171 | skinproxylabel.setBounds(20, 96, 120, 20); 172 | instsettings.add(skinproxylabel); 173 | 174 | skinproxy = new JCheckBox(); 175 | skinproxy.setOpaque(false); 176 | skinproxy.setBounds(150, 97, 20, 20); 177 | skinproxy.setSelected(instanceConfig.isProxySkin()); 178 | instsettings.add(skinproxy); 179 | 180 | JLabel capeproxylabel = new JLabel("Enable Cape Proxy:"); 181 | capeproxylabel.setBounds(20, 124, 120, 20); 182 | instsettings.add(capeproxylabel); 183 | 184 | capeproxy = new JCheckBox(); 185 | capeproxy.setOpaque(false); 186 | capeproxy.setBounds(150, 125, 20, 20); 187 | capeproxy.setSelected(instanceConfig.isProxyCape()); 188 | instsettings.add(capeproxy); 189 | 190 | JLabel soundproxylabel = new JLabel("Enable Sound Proxy:"); 191 | soundproxylabel.setBounds(20, 152, 120, 20); 192 | instsettings.add(soundproxylabel); 193 | 194 | soundproxy = new JCheckBox(); 195 | soundproxy.setOpaque(false); 196 | soundproxy.setBounds(150, 153, 20, 20); 197 | soundproxy.setSelected(instanceConfig.isProxySound()); 198 | instsettings.add(soundproxy); 199 | 200 | JLabel loginproxylabel = new JLabel("Enable Login Proxy:"); 201 | loginproxylabel.setBounds(20, 180, 120, 20); 202 | instsettings.add(loginproxylabel); 203 | 204 | loginproxy = new JCheckBox(); 205 | loginproxy.setOpaque(false); 206 | loginproxy.setBounds(150, 181, 20, 20); 207 | loginproxy.setSelected(instanceConfig.isProxyLogin()); 208 | instsettings.add(loginproxy); 209 | 210 | return instsettings; 211 | } 212 | 213 | private JPanel makeMods() { 214 | JPanel modsPanel = new JPanel(); 215 | modsPanel.setOpaque(false); 216 | modsPanel.setLayout(null); 217 | 218 | jarMods = new ArrayList<>(); 219 | JScrollPane modListScroll = new JScrollPane(); 220 | modDragDropList = new DragDropList(jarMods); 221 | refreshModList(); 222 | modListScroll.setBounds(20, 20, 200, 200); 223 | modListScroll.setViewportView(modDragDropList); 224 | 225 | ScalingButton toggleModsButton = new ScalingButton(); 226 | toggleModsButton.setText("Toggle Selected Mods"); 227 | toggleModsButton.addActionListener(event -> { 228 | for (Object modObj : modDragDropList.getSelectedValuesList()) { 229 | if (modObj instanceof Mod) { 230 | Mod mod = (Mod) modObj; 231 | mod.setEnabled(!mod.isEnabled()); 232 | } else { 233 | Main.getLogger().severe("Mod object not instance of Mod!"); 234 | } 235 | } 236 | modDragDropList.repaint(); 237 | }); 238 | toggleModsButton.setBounds(20, 230, 200, 22); 239 | 240 | ScalingButton applyModsButton = new ScalingButton(); 241 | applyModsButton.setText("Apply Current Mod Configuration"); 242 | applyModsButton.addActionListener(event -> { 243 | ProgressWindow progressWindow = new ProgressWindow(this, "Applying Mods"); 244 | Thread thread = new Thread(() -> { 245 | progressWindow.setProgressMax(2); 246 | progressWindow.setProgress(0); 247 | progressWindow.setProgressText("Setting up"); 248 | File vanillaJar = new File(instpath + ".minecraft/bin/minecraft_vanilla.jar"); 249 | File moddedJar = new File(instpath + ".minecraft/bin/minecraft.jar"); 250 | try { 251 | if (vanillaJar.exists()) { 252 | moddedJar.delete(); 253 | Files.copy(vanillaJar.toPath(), moddedJar.toPath()); 254 | } else { 255 | Files.copy(moddedJar.toPath(), vanillaJar.toPath()); 256 | } 257 | } catch (Exception e) { 258 | e.printStackTrace(); 259 | return; 260 | } 261 | progressWindow.setProgress(1); 262 | progressWindow.setProgressText("Applying Mods"); 263 | InstanceManager.addMods(instName, modDragDropList.model); 264 | progressWindow.setProgress(2); 265 | progressWindow.setProgressText("Done"); 266 | progressWindow.dispose(); 267 | JOptionPane.showMessageDialog(this, "Mods Applied!", "Info", JOptionPane.INFORMATION_MESSAGE); 268 | }); 269 | progressWindow.setThread(thread); 270 | thread.start(); 271 | if (progressWindow.isDisplayable()) { 272 | progressWindow.setVisible(true); 273 | } 274 | }); 275 | applyModsButton.setBounds(20, 262, 200, 22); 276 | 277 | ScalingButton addModsButton = new ScalingButton(); 278 | addModsButton.setText("Add Mods"); 279 | addModsButton.addActionListener(event -> { 280 | FileDialog fileChooser = new FileDialog(this, "Select Mod"); 281 | fileChooser.setFilenameFilter((e, str) -> { 282 | return str.endsWith(".jar") || str.endsWith(".zip"); 283 | }); 284 | fileChooser.setMultipleMode(true); 285 | fileChooser.setVisible(true); 286 | File[] files = fileChooser.getFiles(); 287 | try { 288 | for (File file : files) { 289 | Files.copy(file.toPath(), new File(instpath + "mods/" + file.getName()).toPath()); 290 | } 291 | } catch (IOException e) { 292 | e.printStackTrace(); 293 | } catch (NullPointerException ignored) {} 294 | refreshModList(); 295 | }); 296 | addModsButton.setBounds(20, 294, 200, 22); 297 | 298 | ScalingButton removeModsButton = new ScalingButton(); 299 | removeModsButton.setText("Remove Selected Mods"); 300 | removeModsButton.setForeground(new Color(81, 0, 0)); 301 | removeModsButton.addActionListener(event -> { 302 | for (Object modObj : modDragDropList.getSelectedValuesList()) { 303 | Mod mod = (Mod) modObj; 304 | (new File(instpath + "mods/" + mod.getFileName())).delete(); 305 | } 306 | refreshModList(); 307 | }); 308 | removeModsButton.setBounds(20, 326, 200, 22); 309 | 310 | modsPanel.add(modListScroll); 311 | modsPanel.add(toggleModsButton); 312 | modsPanel.add(applyModsButton); 313 | modsPanel.add(addModsButton); 314 | modsPanel.add(removeModsButton); 315 | 316 | return modsPanel; 317 | } 318 | 319 | private void refreshModList() { 320 | jarMods = new ArrayList<>(); 321 | File modsFolder = new File(instpath + "mods"); 322 | modsFolder.mkdirs(); 323 | File[] mods = modsFolder.listFiles(); 324 | if (mods != null) { 325 | for (Mod mod : modList.getJarMods()) { 326 | if ((new File(instpath + "mods/" + mod.getFileName())).exists()) { 327 | jarMods.add(jarMods.size(), mod); 328 | } 329 | } 330 | for (File modFile : mods) { 331 | boolean trip = false; 332 | for (Mod jarMod : modList.getJarMods()) { 333 | if (jarMod.getFileName().equals(modFile.getName())) { 334 | trip = true; 335 | } 336 | } 337 | if (!trip && (modFile.getName().endsWith(".jar") || modFile.getName().endsWith(".zip"))) { 338 | String modName = modFile.getName(); 339 | if (modName.contains(".")) { 340 | modName = modName.substring(0, modName.lastIndexOf('.')); 341 | } 342 | jarMods.add(jarMods.size(), new Mod(modFile.getName(), modName, 0, true)); 343 | } 344 | } 345 | } 346 | modDragDropList.model.clear(); 347 | for (Mod mod : jarMods) { 348 | modDragDropList.model.addElement(mod); 349 | } 350 | modDragDropList.repaint(); 351 | } 352 | } 353 | -------------------------------------------------------------------------------- /src/main/java/net/glasslauncher/legacy/util/InstanceManager.java: -------------------------------------------------------------------------------- 1 | package net.glasslauncher.legacy.util; 2 | 3 | import com.google.gson.Gson; 4 | import net.glasslauncher.common.CommonConfig; 5 | import net.glasslauncher.common.FileUtils; 6 | import net.glasslauncher.common.GenericInvalidVersionException; 7 | import net.glasslauncher.common.JsonConfig; 8 | import net.glasslauncher.legacy.jsontemplate.InstanceConfig; 9 | import net.glasslauncher.legacy.jsontemplate.MinecraftResource; 10 | import net.glasslauncher.legacy.jsontemplate.MinecraftResources; 11 | import net.glasslauncher.legacy.jsontemplate.Mod; 12 | import net.glasslauncher.legacy.jsontemplate.ModList; 13 | import net.glasslauncher.legacy.jsontemplate.MultiMCComponent; 14 | import net.glasslauncher.legacy.jsontemplate.MultiMCPack; 15 | import net.glasslauncher.legacy.Config; 16 | import net.glasslauncher.legacy.Main; 17 | import net.glasslauncher.legacy.ProgressWindow; 18 | import net.glasslauncher.legacy.VerifyAccountWindow; 19 | import net.glasslauncher.proxy.web.WebUtils; 20 | 21 | import javax.swing.DefaultListModel; 22 | import javax.swing.JOptionPane; 23 | import javax.swing.ListModel; 24 | import java.io.File; 25 | import java.io.InputStream; 26 | import java.net.URL; 27 | import java.nio.file.FileAlreadyExistsException; 28 | import java.nio.file.FileSystem; 29 | import java.nio.file.FileSystems; 30 | import java.nio.file.Files; 31 | import java.util.ArrayList; 32 | import java.util.Objects; 33 | 34 | public class InstanceManager { 35 | 36 | /** 37 | * Detects what kind of modpack zip has been provided and then calls the related install function for the type. 38 | * @param path Path to instance zip file. 39 | */ 40 | public static void installModpack(String path, ProgressWindow progressWindow) { 41 | path = path.replace("\\", "/"); 42 | Main.getLogger().info("Installing " + path); 43 | boolean isURL = true; 44 | try { 45 | try { 46 | new URL(path); 47 | } catch (Exception e) { 48 | isURL = false; 49 | } 50 | 51 | String filename = path.substring(path.lastIndexOf('/') + 1); 52 | if (isURL) { 53 | FileUtils.downloadFile(path, Config.CACHE_PATH + "instancezips"); 54 | installModpackZip(Config.CACHE_PATH + "instancezips/" + filename, filename, progressWindow); 55 | } else { 56 | if ((new File(path)).exists()) { 57 | installModpackZip(path, filename, progressWindow); 58 | } 59 | } 60 | } catch (Exception e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | private static void installModpackZip(String path, String filename, ProgressWindow progressWindow) { 66 | progressWindow.setProgress(0); 67 | progressWindow.setProgressMax(2); 68 | progressWindow.setProgressText("Initializing..."); 69 | String instanceName = filename.replaceFirst("\\.jar$", "").replaceFirst("\\.zip$", ""); 70 | TempZipFile instanceZipFile = new TempZipFile(path); 71 | try { 72 | progressWindow.setProgressText("Checking modpack type..."); 73 | boolean isMultiMC = false; 74 | URL mmcPackURL = null; 75 | String mmcZipInstDir = ""; 76 | 77 | // Because MMC makes a subfolder named after the instance and puts the instance files in there. 78 | for (File file : Objects.requireNonNull(instanceZipFile.getFile("").listFiles())) { 79 | if (file.isDirectory()) { 80 | for (File subFile : Objects.requireNonNull(file.listFiles())) { 81 | if (subFile.getName().equals("mmc-pack.json") && subFile.isFile()) { 82 | isMultiMC = true; 83 | mmcPackURL = subFile.toURI().toURL(); 84 | mmcZipInstDir = file.getName(); 85 | } 86 | } 87 | } 88 | } 89 | 90 | if ((new File(CommonConfig.GLASS_PATH + "instances/" + instanceName)).exists()) { 91 | Main.getLogger().info("Instance \"" + instanceName + "\" already exists!"); 92 | return; 93 | } 94 | if (isMultiMC) { 95 | progressWindow.increaseProgress(); 96 | progressWindow.setProgressText("Installing MultiMC Modpack..."); 97 | Main.getLogger().info("Provided instance is a MultiMC instance. Importing..."); 98 | InputStream inputStream = mmcPackURL.openStream(); 99 | String jsonText = FileUtils.convertStreamToString(inputStream); 100 | inputStream.close(); 101 | MultiMCPack multiMCPack = (new Gson()).fromJson(jsonText, MultiMCPack.class); 102 | importMultiMC(instanceZipFile, instanceName, mmcZipInstDir, multiMCPack, progressWindow); 103 | 104 | } else if (instanceZipFile.getFile("instance_config.json").exists()) { 105 | progressWindow.increaseProgress(); 106 | progressWindow.setProgressText("Installing Glass Launcher Modpack..."); 107 | Main.getLogger().info("Provided instance is a Glass Launcher instance. Importing..."); 108 | InstanceConfig instanceConfig = (InstanceConfig) JsonConfig.loadConfig(instanceZipFile.getFile("instance_config.json").getAbsolutePath(), InstanceConfig.class); 109 | createBlankInstance(instanceConfig.getVersion(), instanceName, progressWindow); 110 | if (new File(Config.getInstancePath(instanceName)).exists()) { 111 | instanceZipFile.copyContentsToDir("", Config.getInstancePath(instanceName)); 112 | } 113 | progressWindow.setProgressText("Installing mods..."); 114 | ModList modList = (ModList) JsonConfig.loadConfig(Config.getInstancePath(instanceName) + "mods/mods.json", ModList.class); 115 | DefaultListModel mods = new DefaultListModel<>(); 116 | for (Mod mod : modList.getJarMods()) { 117 | mods.add(mods.getSize(), mod); 118 | } 119 | addMods(instanceName, mods); 120 | } 121 | } catch (Exception e) { 122 | e.printStackTrace(); 123 | } finally { 124 | instanceZipFile.close(false); 125 | } 126 | } 127 | 128 | public static void createBlankInstance(String version, String instance, ProgressWindow progressWindow) { 129 | progressWindow.setProgress(1); 130 | progressWindow.setProgressMax(4); 131 | progressWindow.setProgressText("Initializing..."); 132 | if (!(new VerifyAccountWindow(progressWindow)).isLoginValid()) { 133 | JOptionPane.showMessageDialog(progressWindow, "Account not validated! Aborting."); 134 | return; 135 | } 136 | Main.getLogger().info("Creating instance \"" + instance + "\" on version " + version); 137 | String versionsCachePath = Config.CACHE_PATH + "versions"; 138 | String instanceFolder = Config.getInstancePath(instance); 139 | String minecraftFolder = instanceFolder + "/.minecraft"; 140 | (new File(versionsCachePath)).mkdirs(); 141 | (new File(minecraftFolder + "/bin/")).mkdirs(); 142 | 143 | progressWindow.increaseProgress(); 144 | progressWindow.setProgressText("Getting minecraft.jar for " + version + "..."); 145 | File versionCacheJar = new File(versionsCachePath + "/" + version + ".jar"); 146 | if (Config.getMcVersions().getClient().containsKey(version)) { 147 | if (versionCacheJar.exists()) { 148 | try { 149 | Files.copy(versionCacheJar.toPath(), new File(minecraftFolder + "/bin/minecraft.jar").toPath()); 150 | } catch (FileAlreadyExistsException e) { 151 | Main.getLogger().info("Instance \"" + instance + "\" already exists!"); 152 | return; 153 | } catch (Exception e) { 154 | e.printStackTrace(); 155 | try { 156 | FileUtils.delete(new File(minecraftFolder)); 157 | } catch (Exception ex) { 158 | e.printStackTrace(); 159 | ex.printStackTrace(); 160 | } 161 | return; 162 | } 163 | } else { 164 | try { 165 | String url = Config.getMcVersions().getClient().get(version).getUrl(); 166 | if (!(url.startsWith("https://") || url.startsWith("http://"))) { 167 | url = "https://launcher.mojang.com/v1/objects/" + Config.getMcVersions().getClient().get(version).getUrl() + "/client.jar"; 168 | } 169 | FileUtils.downloadFile(url, versionsCachePath, null, version + ".jar"); 170 | Files.copy(versionCacheJar.toPath(), new File(minecraftFolder + "/bin/minecraft.jar").toPath()); 171 | } catch (Exception e) { 172 | try { 173 | FileUtils.delete(new File(minecraftFolder)); 174 | } catch (Exception ex) { 175 | e.printStackTrace(); 176 | ex.printStackTrace(); 177 | } 178 | return; 179 | } 180 | } 181 | } 182 | else if (version.equals("custom")) {} 183 | else { 184 | try { 185 | FileUtils.delete(new File(minecraftFolder)); 186 | } catch (Exception e) { 187 | e.printStackTrace(); 188 | } 189 | } 190 | 191 | progressWindow.increaseProgress(); 192 | progressWindow.setProgressText("Adding sounds..."); 193 | addSounds(instance); 194 | progressWindow.increaseProgress(); 195 | progressWindow.setProgressText("Adding LWJGL and JInput..."); 196 | File lwjglCacheZip = new File(versionsCachePath + "/lwjgl.zip"); 197 | if (!lwjglCacheZip.exists()) { 198 | FileUtils.downloadFile("https://files.pymcl.net/client/lwjgl/lwjgl." + Config.OS + ".zip", versionsCachePath, null, "lwjgl.zip"); 199 | } 200 | FileUtils.extractZip(lwjglCacheZip.getPath(), minecraftFolder + "/bin"); 201 | } 202 | 203 | public static void addMods(String instance, ListModel mods) { 204 | instance = Config.getInstancePath(instance); 205 | try { 206 | File moddedJar = new File(instance, "/.minecraft/bin/minecraft.jar"); 207 | File vanillaJar = new File(instance, "/.minecraft/bin/minecraft_vanilla.jar"); 208 | if (moddedJar.exists() && !vanillaJar.exists()) { 209 | Files.move(moddedJar.toPath(), vanillaJar.toPath()); 210 | } 211 | else if (moddedJar.exists()) { 212 | moddedJar.delete(); 213 | } 214 | ArrayList zips = new ArrayList<>(); 215 | for (int i = mods.getSize(); i != 0; i--) { 216 | Main.getLogger().info(instance + "mods/" + mods.getElementAt(i-1).getFileName()); 217 | zips.add(new File(instance + "mods/" + mods.getElementAt(i-1).getFileName())); 218 | } 219 | zips.add(vanillaJar); 220 | FileUtils.mergeZips(moddedJar, zips); 221 | FileSystem jarFs = FileSystems.newFileSystem(moddedJar.toPath(), null); 222 | try { 223 | Files.delete(jarFs.getPath("META-INF/MOJANG_C.DSA")); 224 | Files.delete(jarFs.getPath("META-INF/MOJANG_C.SF")); 225 | Files.delete(jarFs.getPath("META-INF/MANIFEST.MF")); 226 | } 227 | catch (Exception e) { 228 | e.printStackTrace(); 229 | } 230 | jarFs.close(); 231 | } catch (Exception e) { 232 | e.printStackTrace(); 233 | } 234 | } 235 | 236 | private static void importMultiMC(TempZipFile mmcZip, String instance, String mmcZipInstDir, MultiMCPack multiMCPack, ProgressWindow progressWindow) throws GenericInvalidVersionException { 237 | String instPath = Config.getInstancePath(instance); 238 | InstanceConfig instanceConfig = new InstanceConfig(instPath + "instance_config.json"); 239 | ModList modList = new ModList(instPath + "mods/mods.json"); 240 | boolean hasCustomJar = false; 241 | 242 | if (multiMCPack.getFormatVersion() != 1) { 243 | throw new GenericInvalidVersionException("MultiMC instance version is unsupported!"); 244 | } 245 | 246 | progressWindow.setProgress(0); 247 | progressWindow.setProgressMax(3); 248 | progressWindow.setProgressText("Generating mod list..."); 249 | for (MultiMCComponent component : multiMCPack.getComponents()) { 250 | if (component.isImportant()) { 251 | instanceConfig.setVersion(component.getCachedVersion()); 252 | } 253 | else if (component.isDependencyOnly() || (component.getUid().equals("customjar") && component.isDisabled())) {} 254 | else if (component.getUid().equals("customjar") && !component.isDisabled()) { 255 | hasCustomJar = true; 256 | } 257 | else if (component.getCachedName() != null) { 258 | modList.getJarMods().add(modList.getJarMods().size(), new Mod(component.getUid().replace("org.multimc.jarmod.", "") + ".jar", component.getCachedName(), 0, !component.isDisabled())); 259 | } 260 | } 261 | 262 | createBlankInstance(instanceConfig.getVersion(), instance, progressWindow); 263 | if (!(new File(instPath)).exists()) { 264 | mmcZip.close(false); 265 | return; 266 | } 267 | 268 | progressWindow.setProgress(1); 269 | progressWindow.setProgressMax(3); 270 | progressWindow.setProgressText("Copying mods..."); 271 | 272 | for (Mod mod : modList.getJarMods()) { 273 | try { 274 | org.apache.commons.io.FileUtils.copyFile(mmcZip.getFile(mmcZipInstDir + "/jarmods/" + mod.getFileName()), new File(instPath + "/mods/" + mod.getFileName())); 275 | } catch (Exception e) { 276 | e.printStackTrace(); 277 | } 278 | } 279 | 280 | progressWindow.setProgressText("Applying mods..."); 281 | DefaultListModel mods = new DefaultListModel<>(); 282 | for (Mod mod : modList.getJarMods()) { 283 | mods.add(mods.getSize(), mod); 284 | } 285 | 286 | File vanillaJar = new File(instPath + ".minecraft/bin/minecraft_vanilla.jar"); 287 | File moddedJar = new File(instPath + ".minecraft/bin/minecraft.jar"); 288 | try { 289 | if (vanillaJar.exists()) { 290 | moddedJar.delete(); 291 | Files.copy(vanillaJar.toPath(), moddedJar.toPath()); 292 | } else { 293 | Files.copy(moddedJar.toPath(), vanillaJar.toPath()); 294 | } 295 | } catch (Exception e) { 296 | e.printStackTrace(); 297 | return; 298 | } 299 | InstanceManager.addMods(instance, mods); 300 | 301 | try { 302 | progressWindow.setProgressText("Copying custom minecraft.jar..."); 303 | if (hasCustomJar) { 304 | File originalJar = new File(instPath + ".minecraft/bin/minecraft.jar"); 305 | originalJar.delete(); 306 | Files.copy(mmcZip.getFile(mmcZipInstDir + "/libraries/customjar-1.jar").toPath(), originalJar.toPath()); 307 | instanceConfig.setVersion("custom"); 308 | } 309 | } catch (Exception e) { 310 | e.printStackTrace(); 311 | } 312 | progressWindow.increaseProgress(); 313 | progressWindow.setProgressText("Saving config and copying instance files..."); 314 | instanceConfig.saveFile(); 315 | modList.saveFile(); 316 | mmcZip.copyContentsToDir(mmcZipInstDir + "/.minecraft", instPath + ".minecraft"); 317 | } 318 | 319 | private static void addSounds(String instance) { 320 | String baseURL = "https://mcresources.modification-station.net/MinecraftResources/"; 321 | String basePath = Config.getInstancePath(instance) + ".minecraft/resources/"; 322 | MinecraftResources minecraftResources; 323 | 324 | try { 325 | minecraftResources = (new Gson()).fromJson(WebUtils.getStringFromURL(baseURL + "json.php"), MinecraftResources.class); 326 | } catch (Exception e) { 327 | e.printStackTrace(); 328 | return; 329 | } 330 | 331 | try { 332 | for (MinecraftResource minecraftResource : minecraftResources.getFiles()) { 333 | File file = new File(basePath + minecraftResource.getFile()); 334 | File cacheFile = new File(CommonConfig.GLASS_PATH + "cache/resources/" + minecraftResource.getFile()); 335 | String md5 = minecraftResource.getMd5(); 336 | String url = baseURL + minecraftResource.getFile().replace(" ", "%20"); 337 | 338 | FileUtils.downloadFile(url, cacheFile.getParent(), md5); 339 | file.getParentFile().mkdirs(); 340 | Files.copy(cacheFile.toPath(), file.toPath()); 341 | } 342 | } catch (Exception e) { 343 | e.printStackTrace(); 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /src/main/resources/net/glasslauncher/legacy/assets/blog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 134 | 144 | 145 |
9 | 10 | 11 | 14 | 15 |
12 |

Minecraft News

13 |
16 |
17 |
18 |
19 |
20 |

Minecraft Beta 1.7.3

21 |

Bug fixes, bug fixes and bug fixes.

22 |

- Corrected a block duplication bug when using pistons
- Corrected a redstone torch duplication bug when using pistons
- Corrected a client crash when placing a sign in front of the piston, powering the piston and then removing the block beneath the sign
- Ice blocks are now pushed without causing water streams breaking everything
- Booster rails are no longer being powered magically without a power source
- Pistons connected to the end of a piston-transistor via redstone are now properly closed when the power goes out
- Doors no longer create purple particles
- Hacking clients can no longer edit texts of placed signs in multiplayer
- Changed so that paintings pushed by pistons will pop off

23 |
24 | 25 |
26 |
27 |
28 |
29 |

Minecraft Beta 1.7

30 |

1.7:

+ Added pistons
+ Fire or redstone is now required to trigger TNT
+ Fences can be stacked
+ Added shears
+ Shears can be used to pick up leaf blocks
+ Shears can be used to shear sheep without hurting them
* Sheep no longer drop wool from being punched
- Removed Herobrine

31 |
32 | 33 |
34 |
35 |
36 |
37 |

Minecraft Beta 1.6.6

38 |

1.6.6:

+ Added a new use for bonemeal
* Changed the material of glowstone from “glass” to “stone”. This means you need a pickaxe to get resources from it
* Made glowstone drop more loot
* Made the recipe sizes for creating cloth blocks and glowstone blocks smaller (2x2 instead of 3x3)
* Re-introduced unlimited FPS, renamed the option again
* Fixed beds not working very well at all in SMP
* Fixed destroying a boat spawning the player too low, causing them to sometimes fall through the ground
* Boats float up to the surface quicker
* Boats falling down into water lose their vertical momentum very fast
* Removed Herobrine

39 |
40 | 41 |
42 |
43 |
44 |
45 |

Minecraft Beta 1.6.5

46 |

There are some annoying bed bugs in multiplayer in this version, I’ll fix those next week.

47 |

1.6.5:

* Tweaked the connection reading code a lot. Hopefully this helps.
* Changed the “limit framerate” option to a “framerate cap” option.
* Added “Advanced OpenGL” again
* Players riding anything or sleeping in anything will stop doing so when they leave the game now
* Removed the minecraft version number from the main game window. It’s still available in the title screen
* Fixed some entities appearing to fall through the ground repeatedly in multiplayer (some might still do so)
* Fixed the server sometimes thinking the player hit a corner when walking when they didn’t
* Fixed dropped damaged items vanishing after a single use after being picked up
* Fixed dropped items not getting ejected from blocks properly
* Fixed the achievements window rendering some graphics outside the clip window
* Fixed players saving while sneaking being loaded too high up
* Fixed shift+click transferring items causing a crash when the target container is full
* Fixed lighting updates sometimes not happening correctly
* Fixed the players health appearing to be full when entering/exiting the nether
* Fixed a couple of instances where beds would act strange in multiplayer, primarily the “already occupied” bug

48 |
49 | 50 |
51 |
52 |
53 |
54 |

Minecraft Beta 1.6.4

55 |

* Fixed 1.6.3 using more CPU despite it claiming to use less
* Totally professional

56 |
57 | 58 |
59 |
60 |
61 |
62 |

Minecraft Beta 1.6.3

63 |

1.6.3:

+ The renderer is now capped at 100 fps while there are chunks to be generated. The excess time will be spent generating chunks instead of rendering frames
+ The “limit framerate” option now limits the game to 40 fps and will spend at least 10 ms per frame sleeping
+ The “limit framerate” option has been reset to “off” for all players, enable it again if you want it
* Fixed some block updates not updating lighting properly under some circumstances by reverting the “don’t always send block data” fix in 1.6
* Fixed a major CPU load issue in the server where a very tight loop would starve all other threads
* Fixed furnaces dropping/duplicating their contents when they change state from lit to unlit or back

1.6.2:

* Fixed an ACTUAL item duplication bug while picking up some items

1.6.1:

* Fixed a visual item duplication bug when trying to pick up items while the inventory is full

64 |
65 | 66 |
67 |
68 |
69 |
70 |

Minecraft Beta 1.6

71 |

New features:
+ Added Nether support to multiplayer
+ The client will ask minecraft.net if the current login is valid. If the server says “no”, a warning message appears in the client. You can still play the game even if this happens.
+ Added craftable maps
+ Added hatches
+ Added tall grass in some biomes
+ Mushrooms now spreads (very) slowly
+ Added server property view-distance. Sets the radius of terrain updates (in chunks) to send to the players. Range 3-15, default 10.
+ Added dead shrubs in deserts
+ Added allow-nether (set to true or false) in server.properties
+ Blocks destroyed by other players in multiplayer now shows the breaking block particle effect
+ Doors make sound for other players in multiplayer
+ The record player now supports more than 15 different songs (you can’t get the records yet, though)
+ Activated dispensers make sounds and trigger particles in multiplayer
+ Players stuck in walls will slide towards the nearest gap if there is one

Changes:
* Disabled Advanced OpenGL until we can fix some bugs with it
* It’s no longer possible to build solid blocks on the top layer of the maps (sorry!)
* Made booster tracks speedier
* Severely nerfed fire so it spread slower, and doesn’t spread infinitely
* Seeds are now found in tall grass, using a hoe on the ground no longer works
* Compressed network traffic more agressively
* Blocks that don’t change appearance when the data changes don’t send block updates when their data changes now
* Arrows shot by players can now be picked up by all players
* Nothing riding anything or being ridden by anything can enter portals

Bugfixes:
* Fixed running out of memory corrupting the current level
* Fixed the side textures of grass getting extra dark when mining them
* Fixed anaglyph 3d rendering mode having severe visual bugs
* Fixed the crash screen vanishing immediately
* Fixed not being able to target blocks when at x or z pos 1000
* Fixed the achievements screen messing up the sky color
* Fixed saving while sneaking dropping the player through the ground
* Fixed a system clock change messing up the game speed
* Fixed rain sounds not playing with fast graphics enabled
* Fixed hair and cloaks being rendered in the wrong locations on sneaking players
* Fixed the attack/swing animation not being applied to the armor layer
* Fixed player rotation not being loaded correctly when loading a saved game
* Fixed arrow physics, making them not get stuck midair when you open a door
* Fixed arrows hitting reeds, portals, and other non-solid blocks
* Fixed keybindings not getting saved properly under certain conditions
* Fixed the player not being able to sneak off lowered blocks like cacti
* Fixed a bug where the player could mine without swinging their arm
* Fixed boats placed on snow being placed too far up
* Fixed submerged boats rising very very fast
* Fixed sand dropping onto boats getting stuck in a falling animation
* Fixed a game crash when riding a vehicle or animal into the nether
* Fixed falling while riding not dealing damage to the rider
* Fixed buttons and levers too close to the player being impossible to use
* Fixed dispensers shooting through walls
* Fixed fire hurting through wall corners
* Fixed entities reaching water through wall corners
* Fixed placing doors next to cacti creating half-doors
* Fixed buttons and levers being placeable on leaves in “fast graphics” mode
* Fixed furnaces and dispensers not dropping their contents when destroyed
* Fixed dispensers biasing later slots
* Fixed farmland taking too long to dig
* Fixed tilling below some blocks being possible
* Fixed tilling the underside of blocks somehow working
* Fixed fences and stairs sometimes becoming invisible
* Fixed walking on top of fences not producing step sounds
* Fixed fire sometimes existing mid-air as an invisible block of pain
* Fixed fences and wooden stairs not being flammable
* Fixed fire effect on burning entities sometimes getting rendered in the wrong location
* Fixed fishing rod rendering being a bit lacking
* Fixed fishing rods being stackable
* Fixed mining glass hiding the clouds behind the glass
* Fixed rain falling through liquids
* Fixed items in glass blocks not getting ejected properly
* Fixed water interacting strangely with glass
* Fixed glass not blocking rain sound
* Fixed fences and signs preventing grass from growing
* Fixed rain and snow being incorrectly lit
* Fixed grass staying alive below stair blocks
* Fixed the achievement screen not pausing the game
* Fixed some screens breaking the sky tint color
* Fixed fullscreen mode switching causing mouse issues and screen closes
* Fixed chat messages surviving through game switches
* Fixed ice so it regenerates regardless of whether it’s snowing or not
* Fixed rain falling too slowly
* Fixed levers being placeable on weird locations
* Fixed floor levers sometimes not delivering a signal downwards
* Fixed floor levers sometimes not being removed when the floor is removed
* Fixed rail tiles sometimes not properly connecting to a new neighbor
* Fixed minecarts next to each other causing extreme velocities (sorry!)
* Fixed wolves not following their owner if the name has different caps
* Fixed creepers retaining charge level when they can’t see their target
* Fixed dying in the nether spawning new portals
* “Fixed” beds in the nether
* Fixed inventory acting weird when portaling by making the portal close all screens
* Fixed wooden pressure plates being mined with pickaxes
* Fixed redstone repeaters having the wrong particles
* Fixed saplings being plantable through snow onto non-grass blocks
* Fixed ore density varying per quadrant from the center of the world
* Fixed dispenser graphics being one pixel off. ONE PIXEL!!!
* Fixed mushrooms spawning everywhere during nights
* Fixed animals only spawning near light during the night
* Fixed the multiplayer join screen input field being too short
* Fixed IPv6 addresses being parsed wrongly. To connect to a specific port in IPv6, use the format [1234:567::1]:25565
* Fixed network packets being sent unbuffered, causing huge amounts of packets being sent
* Fixed entity positions going out of synch sometimes. They get re-synched every 20 seconds now.
* Fixed inventory icons not animating after being picked up in multiplayer
* Fixed mushroom soup not leaving a bowl in multiplayer
* Fixed entities above the map height limit becoming invisible
* Fixed healing not flashing the health bar in multiplayer
* Fixed arrows being animated really strangely in multiplayer
* Fixed arrows triggering too many entity move updates in multiplayer
* Fixed the compass not pointing at the spawn location in multiplayer
* Fixed fires being impossible to put out in multiplayer
* Fixed record players spawning client-side fake records in multiplayer
* Fixed records not playing for other players in multiplayer
* Fixed players spawning in the wrong location and quickly lerping to the correct location
* Fixed monsters not being visible for players with their difficulty set to peaceful
* Fixed pigs getting hit by lightning in multiplayer spawning client-side zombie pigmen
* Fixed loads of exploding tnt generating way too many particles, possibly crashing the client
* Fixed bonemeal use in multiplayer sometimes spawning fake client-side trees
* Fixed saplings sometimes spawning trees client-side in multiplayer
* Fixed weather sometimes changing client-side in multiplayer
* Fixed grasscolor.png and foliagecolor.png not being read from texture packs
* Fixed stats getting saved to different files in offline mode if the caps in the player name differ from the true spelling
* Fixed fireballs not being visible in multiplayer
* Fixed ghasts’ fireing animation not being visible in multiplayer
* Fixed receiving more items than the maximum stack size sometimes causing an oversized stack in the inventory

72 |
73 | 74 |
75 |
76 |
77 |
78 |

Minecraft Beta 1.5

79 |

(You will need to update your servers as well)

80 |

* Weather (rain, snow, thunder)
* Statistics
* Achievements
* Detector rail
* Booster rail
* Performance improvements

81 |

More statistics and achievements will be added over time as we develop the game. They’re only stored locally at the moment, but we’ll slowly be rolling out online storing of achievements over time.

82 |
83 | 84 |
85 |
86 |
87 |
88 |

Minecraft Beta 1.4_01

89 |

* Removed the April fools joke

90 |

(The server was updated as well, but it’s an optional update)

91 |
92 | 93 |
94 |
95 |
96 |
97 |

Minecraft Beta 1.4

98 |

* Added tameable wolves
* Added cookies
* Sleeping in a bed now resets your spawn position
* New Minecraft logo
* Holding shift while climbing will hang on to the ladder
* Spiders will no longer trample crops
* Lots and lots of infrastructure for Statistics lists and Achievements

99 |

While most of the code is written, statistics and achievements won’t show up until the next update. We didn’t quite have time to finish them this week, and didn’t want to delay the update longer.

100 |
101 | 102 |
103 |
104 |
105 |
106 |

Minecraft Beta 1.3

107 |

* Implemented a new lighting engine with the help of MrMessiahs (can be turned off)
* Changed the options around, added a new “Graphics options” button
* Added beds. If all players in a map sleeps in a bed during night, the game immediately skips until morning
* Added three new half-size blocks
* Added Delay/Repeater redstone dust blocks
* Added whitelisting to the server. To use, enter “whitelist <cmd>” where cmd is “on”, “off”, “add <player>”, “remove <player>”, “list” or “reload”
* New save file format, old maps need to be converted (that might take a while..)
* It’s now possible to have more than five save slots, and to rename saves
* Scrollbars in both the texture pack list, and in the map selection screen
* Replaced the Mojang splash image to reflect the new logo colors
* .. and a bunch of bug fixes and tweaks!

108 |
109 | 110 |
111 |
112 |
113 |
114 |

Welcome to the new Minecraft Launcher

115 |

This area will be used to inform you about game updates and other important news about the game. It’s highly likely we’ll also use it to promote things we like, such as any future games we make, or where to get the latest merchandise for the game. Marketing will be kept tasteful, though.

116 |

One cool feature about the new launcher you might want to know about is that you can pass it command-line features! The syntax is:

117 |

Minecraft <username> <password> <server:port>

118 |

<username> is the username you wish to log in as
<password> is your password. If this is set, the launcher will automatically start the game without waiting for you to press the button
<server:port> is an ip to a minecraft server. If this is set, the game will automatically connect to that server once the game has started.

119 |
120 | 121 |
122 |
123 |
124 |
125 |

Thanks for helping test this!

126 |

The biggest problem so far seems to be the “too many failed log in attempts” bug. That’s caused by the server getting every one connecting to the server mixed up

127 |
128 | 129 |
130 |
131 |
132 |
133 |
146 | 147 | --------------------------------------------------------------------------------