Commit 7aa49e453f0b5d067cd53961b7457c7c99663a82

Authored by Mumfrey
1 parent fca9591f

LiteLoader 1.6.4_02 - dev only - added comments to LoginManager, some minor tweaks

debug/com/mumfrey/liteloader/debug/LoginManager.java
@@ -5,15 +5,17 @@ import java.io.FileReader; @@ -5,15 +5,17 @@ import java.io.FileReader;
5 import java.io.FileWriter; 5 import java.io.FileWriter;
6 import java.io.IOException; 6 import java.io.IOException;
7 import java.net.Proxy; 7 import java.net.Proxy;
  8 +import java.util.HashMap;
8 import java.util.Map; 9 import java.util.Map;
9 import java.util.UUID; 10 import java.util.UUID;
10 import java.util.logging.Logger; 11 import java.util.logging.Logger;
11 12
  13 +import javax.swing.JOptionPane;
  14 +
12 import com.google.common.base.Strings; 15 import com.google.common.base.Strings;
13 import com.google.gson.Gson; 16 import com.google.gson.Gson;
14 import com.google.gson.GsonBuilder; 17 import com.google.gson.GsonBuilder;
15 -import com.google.gson.JsonIOException;  
16 -import com.google.gson.JsonSyntaxException; 18 +import com.google.gson.annotations.SerializedName;
17 import com.mojang.authlib.Agent; 19 import com.mojang.authlib.Agent;
18 import com.mojang.authlib.GameProfile; 20 import com.mojang.authlib.GameProfile;
19 import com.mojang.authlib.exceptions.AuthenticationException; 21 import com.mojang.authlib.exceptions.AuthenticationException;
@@ -28,24 +30,62 @@ import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication; @@ -28,24 +30,62 @@ import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
28 */ 30 */
29 public class LoginManager 31 public class LoginManager
30 { 32 {
  33 + /**
  34 + * Logger instance
  35 + */
31 private static Logger logger = Logger.getLogger("liteloader"); 36 private static Logger logger = Logger.getLogger("liteloader");
32 37
  38 + /**
  39 + * Gson instance for serialising and deserialising the authentication data
  40 + */
33 private static Gson gson = new GsonBuilder().setPrettyPrinting().create(); 41 private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
34 42
  43 + /**
  44 + * Authentication service
  45 + */
35 private YggdrasilAuthenticationService authService; 46 private YggdrasilAuthenticationService authService;
36 47
  48 + /**
  49 + * Authentication agent
  50 + */
37 private YggdrasilUserAuthentication authentication; 51 private YggdrasilUserAuthentication authentication;
38 52
  53 + /**
  54 + * JSON file to load/save auth data from
  55 + */
39 private File jsonFile; 56 private File jsonFile;
40 57
  58 + /**
  59 + * Username read from the auth JSON file, we use this as the default in the login dialog in
  60 + * case login fails. This is stored in the JSON even if authentication is not successful so that
  61 + * we can display the same username next time
  62 + */
41 private String defaultUsername; 63 private String defaultUsername;
42 64
  65 + /**
  66 + * Minecraft screen name read from the auth JSON file. Use this as default in case the login fails
  67 + * or is skipped (when offline) so that at least the Minecraft client has a sensible display name.
  68 + * Defaults to user.name when not specified
  69 + */
43 private String defaultDisplayName = System.getProperty("user.name"); 70 private String defaultDisplayName = System.getProperty("user.name");
44 71
  72 + /**
  73 + * True if login should not be attempted, skips the authentication attempt and the login dialog
  74 + */
45 private boolean offline = false; 75 private boolean offline = false;
46 76
47 - private boolean showDialog = false; 77 + /**
  78 + * If authentication fails with token then the first attempt will be to use the user/pass specified
  79 + * on the command line (if any). This flag is set AFTER that first attempt so that we know to display
  80 + * the login dialog anyway (eg. the login on the command line was bad)
  81 + */
  82 + private boolean forceShowLoginDialog = false;
48 83
  84 + /**
  85 + * ctor
  86 + *
  87 + * @param jsonFile
  88 + */
49 public LoginManager(File jsonFile) 89 public LoginManager(File jsonFile)
50 { 90 {
51 this.jsonFile = jsonFile; 91 this.jsonFile = jsonFile;
@@ -55,7 +95,8 @@ public class LoginManager @@ -55,7 +95,8 @@ public class LoginManager
55 } 95 }
56 96
57 /** 97 /**
58 - * 98 + * When authenticaion fails, we regenerate the auth service and agent because trying again with the same
  99 + * client data will fail.
59 */ 100 */
60 public void resetAuth() 101 public void resetAuth()
61 { 102 {
@@ -64,12 +105,11 @@ public class LoginManager @@ -64,12 +105,11 @@ public class LoginManager
64 } 105 }
65 106
66 /** 107 /**
67 - * @throws JsonIOException  
68 - * @throws JsonSyntaxException 108 + * Load auth data from the json file
69 */ 109 */
70 private void load() 110 private void load()
71 { 111 {
72 - if (this.jsonFile.exists()) 112 + if (this.jsonFile != null && this.jsonFile.exists())
73 { 113 {
74 FileReader fileReader = null; 114 FileReader fileReader = null;
75 115
@@ -78,7 +118,7 @@ public class LoginManager @@ -78,7 +118,7 @@ public class LoginManager
78 fileReader = new FileReader(this.jsonFile); 118 fileReader = new FileReader(this.jsonFile);
79 AuthData authData = LoginManager.gson.fromJson(fileReader, AuthData.class); 119 AuthData authData = LoginManager.gson.fromJson(fileReader, AuthData.class);
80 120
81 - if (authData != null) 121 + if (authData != null && authData.validate())
82 { 122 {
83 this.logInfo("Initialising Yggdrasil authentication service with client token: %s", authData.getClientToken()); 123 this.logInfo("Initialising Yggdrasil authentication service with client token: %s", authData.getClientToken());
84 this.authService = new YggdrasilAuthenticationService(Proxy.NO_PROXY, authData.getClientToken()); 124 this.authService = new YggdrasilAuthenticationService(Proxy.NO_PROXY, authData.getClientToken());
@@ -101,38 +141,57 @@ public class LoginManager @@ -101,38 +141,57 @@ public class LoginManager
101 } 141 }
102 } 142 }
103 143
  144 + /**
  145 + * Save auth data to the JSON file
  146 + */
104 private void save() 147 private void save()
105 { 148 {
106 - FileWriter fileWriter = null;  
107 -  
108 - try 149 + if (this.jsonFile != null)
109 { 150 {
110 - fileWriter = new FileWriter(this.jsonFile); 151 + FileWriter fileWriter = null;
111 152
112 - AuthData authData = new AuthData(this.authService, this.authentication, this.offline, this.defaultUsername, this.defaultDisplayName);  
113 - LoginManager.gson.toJson(authData, fileWriter);  
114 - }  
115 - catch (IOException ex) { ex.printStackTrace(); }  
116 - finally  
117 - {  
118 try 153 try
119 { 154 {
120 - if (fileWriter != null) fileWriter.close(); 155 + fileWriter = new FileWriter(this.jsonFile);
  156 +
  157 + AuthData authData = new AuthData(this.authService, this.authentication, this.offline, this.defaultUsername, this.defaultDisplayName);
  158 + LoginManager.gson.toJson(authData, fileWriter);
121 } 159 }
122 catch (IOException ex) { ex.printStackTrace(); } 160 catch (IOException ex) { ex.printStackTrace(); }
  161 + finally
  162 + {
  163 + try
  164 + {
  165 + if (fileWriter != null) fileWriter.close();
  166 + }
  167 + catch (IOException ex) { ex.printStackTrace(); }
  168 + }
123 } 169 }
124 -  
125 } 170 }
126 171
  172 + /**
  173 + * Attempt to login. If authentication data are found on disk then tries first to log in with
  174 + * the stored token. If the token login fails then attempts to log in with the username and password
  175 + * specified. If no user or pass are specified or if they fail then displays a login dialog to
  176 + * allow the user to login. If login succeeds then the token is stored on disk and the method
  177 + * returns.
  178 + *
  179 + * If the user presses cancel in the login dialog then the method returns false.
  180 + *
  181 + * @param username User name to log in with if token login fails, if null displays the login dialog immediately
  182 + * @param password Password to log in with if token login fails, if null displays the login dialog immediately
  183 + * @param remainingTries Number of loops to go through before giving up, decremented for each try, specify -1 for unlimited
  184 + * @return false if the user presses cancel in the login dialog, otherwise returns true
  185 + */
127 public boolean login(String username, String password, int remainingTries) 186 public boolean login(String username, String password, int remainingTries)
128 { 187 {
129 - if (this.offline || remainingTries < 1) 188 + if (this.offline || remainingTries == 0)
130 { 189 {
131 this.logInfo("LoginManager is set to work offline, skipping login"); 190 this.logInfo("LoginManager is set to work offline, skipping login");
132 return false; 191 return false;
133 } 192 }
134 193
135 - this.logInfo("Remaining login tries: %d", remainingTries); 194 + this.logInfo("Remaining login tries: %s", remainingTries > 0 ? remainingTries : "unlimited");
136 195
137 try 196 try
138 { 197 {
@@ -153,37 +212,50 @@ public class LoginManager @@ -153,37 +212,50 @@ public class LoginManager
153 { 212 {
154 this.logInfo("Authentication agent reported invalid credentials: %s", ex.getMessage()); 213 this.logInfo("Authentication agent reported invalid credentials: %s", ex.getMessage());
155 this.resetAuth(); 214 this.resetAuth();
156 -  
157 - if (username == null)  
158 - {  
159 - username = this.defaultUsername;  
160 - }  
161 215
162 - if (this.showDialog || username == null || password == null) 216 + if (remainingTries > 1)
163 { 217 {
164 - LoginPanel loginPanel = LoginPanel.getLoginPanel(username, password, this.showDialog ? ex.getMessage() : null);  
165 - if (!loginPanel.showModalDialog()) 218 + if (username == null)
166 { 219 {
167 - this.logInfo("User cancelled login dialog"); 220 + username = this.defaultUsername;
  221 + }
  222 +
  223 + if (this.forceShowLoginDialog || username == null || password == null)
  224 + {
  225 + LoginPanel loginPanel = LoginPanel.getLoginPanel(username, password, this.forceShowLoginDialog ? ex.getMessage() : null);
  226 + boolean dialogResult = loginPanel.showModalDialog();
168 this.offline = loginPanel.workOffline(); 227 this.offline = loginPanel.workOffline();
169 - if (this.offline) this.save();  
170 - return false; 228 + this.defaultUsername = loginPanel.getUsername();
  229 +
  230 + if (!dialogResult)
  231 + {
  232 + this.logInfo("User cancelled login dialog");
  233 + return false;
  234 + }
  235 +
  236 + if (this.offline)
  237 + {
  238 + if (JOptionPane.showConfirmDialog(null, "<html>You have chosen to work offline. You will never be prompted to log in again.<br /><br />If you would like to re-enable login please delete the file <span style=\"color: #0000FF\">.auth.json</span> from the working dir<br />or press Cancel to return to the login dialog.</html>", "Confirm work offline", JOptionPane.OK_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.CANCEL_OPTION)
  239 + {
  240 + this.offline = false;
  241 + remainingTries = Math.max(remainingTries, 3);
  242 + }
  243 + }
  244 +
  245 + username = loginPanel.getUsername();
  246 + password = loginPanel.getPassword();
  247 + this.save();
171 } 248 }
172 -  
173 - username = loginPanel.getUsername();  
174 - password = loginPanel.getPassword();  
175 - this.offline = loginPanel.workOffline();  
176 - this.save();  
177 - }  
178 -  
179 - if (!Strings.isNullOrEmpty(username) && !Strings.isNullOrEmpty(password))  
180 - {  
181 - this.authentication.setUsername(username);  
182 - this.authentication.setPassword(password); 249 +
  250 + if (!Strings.isNullOrEmpty(username) && !Strings.isNullOrEmpty(password))
  251 + {
  252 + this.authentication.setUsername(username);
  253 + this.authentication.setPassword(password);
  254 + }
  255 +
  256 + this.forceShowLoginDialog = true;
  257 + this.login(username, password, --remainingTries);
183 } 258 }
184 -  
185 - this.showDialog = true;  
186 - this.login(username, password, --remainingTries);  
187 } 259 }
188 catch (AuthenticationException ex) 260 catch (AuthenticationException ex)
189 { 261 {
@@ -194,12 +266,34 @@ public class LoginManager @@ -194,12 +266,34 @@ public class LoginManager
194 return false; 266 return false;
195 } 267 }
196 268
  269 + /**
  270 + * Get whether user logged in
  271 + */
  272 + public boolean isLoggedIn()
  273 + {
  274 + return this.authentication.isLoggedIn();
  275 + }
  276 +
  277 + /**
  278 + * Get whether we are able to play online or not
  279 + */
  280 + public boolean canPlayOnline()
  281 + {
  282 + return this.authentication.canPlayOnline();
  283 + }
  284 +
  285 + /**
  286 + * Get the profile name (minecraft player name) from login
  287 + */
197 public String getProfileName() 288 public String getProfileName()
198 { 289 {
199 GameProfile selectedProfile = this.authentication.getSelectedProfile(); 290 GameProfile selectedProfile = this.authentication.getSelectedProfile();
200 return selectedProfile != null ? selectedProfile.getName() : this.defaultDisplayName; 291 return selectedProfile != null ? selectedProfile.getName() : this.defaultDisplayName;
201 } 292 }
202 293
  294 + /**
  295 + * Get the session token
  296 + */
203 public String getAuthenticatedToken() 297 public String getAuthenticatedToken()
204 { 298 {
205 String accessToken = this.authentication.getAuthenticatedToken(); 299 String accessToken = this.authentication.getAuthenticatedToken();
@@ -211,16 +305,25 @@ public class LoginManager @@ -211,16 +305,25 @@ public class LoginManager
211 LoginManager.logger.info(String.format(message, params)); 305 LoginManager.logger.info(String.format(message, params));
212 } 306 }
213 307
  308 + /**
  309 + * Struct for Gson serialisation of authenticaion settings
  310 + *
  311 + * @author Adam Mummery-Smith
  312 + */
214 class AuthData 313 class AuthData
215 { 314 {
  315 + @SerializedName("clientToken")
216 private String clientToken; 316 private String clientToken;
217 317
  318 + @SerializedName("workOffline")
218 private boolean workOffline; 319 private boolean workOffline;
219 320
  321 + @SerializedName("authData")
220 private Map<String, String> credentials; 322 private Map<String, String> credentials;
221 323
222 public AuthData() 324 public AuthData()
223 { 325 {
  326 + // default ctor for Gson
224 } 327 }
225 328
226 public AuthData(YggdrasilAuthenticationService authService, YggdrasilUserAuthentication authentication, boolean workOffline, String defaultUserName, String defaultDisplayName) 329 public AuthData(YggdrasilAuthenticationService authService, YggdrasilUserAuthentication authentication, boolean workOffline, String defaultUserName, String defaultDisplayName)
@@ -236,6 +339,16 @@ public class LoginManager @@ -236,6 +339,16 @@ public class LoginManager
236 this.credentials.put("displayName", defaultDisplayName); 339 this.credentials.put("displayName", defaultDisplayName);
237 } 340 }
238 341
  342 + /**
  343 + * Called after Gson deserialisation to check that deserialisation was successful
  344 + */
  345 + public boolean validate()
  346 + {
  347 + if (this.clientToken == null) this.clientToken = UUID.randomUUID().toString();
  348 + if (this.credentials == null) this.credentials = new HashMap<String, String>();
  349 + return true;
  350 + }
  351 +
239 public String getClientToken() 352 public String getClientToken()
240 { 353 {
241 return this.clientToken; 354 return this.clientToken;
debug/com/mumfrey/liteloader/debug/LoginPanel.java
@@ -44,154 +44,184 @@ import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE; @@ -44,154 +44,184 @@ import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;
44 public class LoginPanel extends JPanel 44 public class LoginPanel extends JPanel
45 { 45 {
46 private static final long serialVersionUID = 1L; 46 private static final long serialVersionUID = 1L;
  47 +
  48 + private GridBagLayout panelLoginLayout;
  49 +
  50 + private JPanel panelTitle;
  51 + private JPanel panelCentre;
  52 + private JPanel panelPadding;
  53 + private JPanel panelBottom;
  54 + private JLabel lblTitle;
  55 + private JLabel lblSubTitle;
  56 + private JLabel lblMessage;
  57 + private JLabel lblUserName;
  58 + private JLabel lblPassword;
47 private TextField txtUsername; 59 private TextField txtUsername;
48 private TextField txtPassword; 60 private TextField txtPassword;
49 private JButton btnLogin; 61 private JButton btnLogin;
50 private JButton btnCancel; 62 private JButton btnCancel;
  63 + private JCheckBox chkOffline;
51 64
52 - private boolean loginClicked = false;  
53 -  
54 private JDialog dialog; 65 private JDialog dialog;
55 - private JCheckBox chkOffline;  
56 - private JLabel lblLogIn;  
57 - private JPanel panelLogin;  
58 - private JPanel panelButtons;  
59 - private JPanel panelPadding;  
60 - private JLabel lblNewLabel;  
61 - private JLabel lblPassword;  
62 66
63 - private CustomFocusTraversal tabOrder = new CustomFocusTraversal();  
64 - private JLabel lblMessage; 67 + private ListFocusTraversal tabOrder = new ListFocusTraversal();
  68 +
  69 + private boolean dialogResult = false;
65 70
66 public LoginPanel(String username, String password, String error) 71 public LoginPanel(String username, String password, String error)
67 { 72 {
  73 + Color backColour = new Color(102, 118, 144);
  74 +
68 this.setFocusable(false); 75 this.setFocusable(false);
69 this.setPreferredSize(new Dimension(400, 260)); 76 this.setPreferredSize(new Dimension(400, 260));
70 this.setBackground(new Color(105, 105, 105)); 77 this.setBackground(new Color(105, 105, 105));
71 this.setLayout(new BorderLayout(0, 0)); 78 this.setLayout(new BorderLayout(0, 0));
72 79
73 - this.lblLogIn = new JLabel("Log In");  
74 - this.lblLogIn.setFocusable(false);  
75 - this.lblLogIn.setBorder(new EmptyBorder(10, 16, 10, 16));  
76 - this.lblLogIn.setOpaque(true);  
77 - this.lblLogIn.setBackground(new Color(119, 136, 153));  
78 - this.lblLogIn.setFont(new Font("Tahoma", Font.BOLD, 18));  
79 - this.lblLogIn.setForeground(new Color(255, 255, 255));  
80 - this.lblLogIn.setPreferredSize(new Dimension(400, 64));  
81 - this.add(this.lblLogIn, BorderLayout.NORTH);  
82 -  
83 - this.panelButtons = new JPanel();  
84 - this.panelButtons.setFocusable(false);  
85 - this.panelButtons.setBackground(new Color(112, 128, 144));  
86 - this.panelButtons.setPreferredSize(new Dimension(400, 32));  
87 - this.add(this.panelButtons, BorderLayout.SOUTH);  
88 - this.panelButtons.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5)); 80 + this.panelTitle = new JPanel();
  81 + this.panelTitle.setBackground(backColour);
  82 + this.panelTitle.setPreferredSize(new Dimension(400, 64));
  83 + this.panelTitle.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
89 84
90 - this.chkOffline = new JCheckBox("Never ask me to log in (always run offline)");  
91 - this.chkOffline.setPreferredSize(new Dimension(386, 23));  
92 - this.chkOffline.setForeground(new Color(255, 255, 255));  
93 - this.chkOffline.setOpaque(false);  
94 - this.panelButtons.add(this.chkOffline); 85 + this.panelBottom = new JPanel();
  86 + this.panelBottom.setBackground(backColour);
  87 + this.panelBottom.setPreferredSize(new Dimension(400, 32));
  88 + this.panelBottom.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
95 89
96 this.panelPadding = new JPanel(); 90 this.panelPadding = new JPanel();
97 - this.panelPadding.setFocusable(false);  
98 this.panelPadding.setBorder(new EmptyBorder(4, 8, 8, 8)); 91 this.panelPadding.setBorder(new EmptyBorder(4, 8, 8, 8));
99 this.panelPadding.setOpaque(false); 92 this.panelPadding.setOpaque(false);
100 - this.add(this.panelPadding, BorderLayout.CENTER);  
101 this.panelPadding.setLayout(new BorderLayout(0, 0)); 93 this.panelPadding.setLayout(new BorderLayout(0, 0));
102 94
103 - this.panelLogin = new JPanel();  
104 - this.panelLogin.setFocusable(false);  
105 - this.panelPadding.add(this.panelLogin);  
106 - this.panelLogin.setOpaque(false);  
107 - this.panelLogin.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Yggdrasil Login", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(255, 255, 255)));  
108 - GridBagLayout gbl_panelLogin = new GridBagLayout();  
109 - gbl_panelLogin.columnWidths = new int[] {30, 80, 120, 120, 30};  
110 - gbl_panelLogin.rowHeights = new int[] {24, 32, 32, 32};  
111 - gbl_panelLogin.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};  
112 - gbl_panelLogin.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0};  
113 - this.panelLogin.setLayout(gbl_panelLogin); 95 + this.panelCentre = new JPanel();
  96 + this.panelCentre.setOpaque(false);
  97 + this.panelCentre.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Yggdrasil Login", TitledBorder.LEADING, TitledBorder.TOP, null, Color.WHITE));
  98 + this.panelLoginLayout = new GridBagLayout();
  99 + this.panelLoginLayout.columnWidths = new int[] {30, 80, 120, 120, 30};
  100 + this.panelLoginLayout.rowHeights = new int[] {24, 32, 32, 32};
  101 + this.panelLoginLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
  102 + this.panelLoginLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0};
  103 + this.panelCentre.setLayout(this.panelLoginLayout);
  104 +
  105 + this.lblTitle = new JLabel("Log in to minecraft.net");
  106 + this.lblTitle.setBorder(new EmptyBorder(4, 16, 0, 16));
  107 + this.lblTitle.setFont(new Font("Tahoma", Font.BOLD, 18));
  108 + this.lblTitle.setForeground(Color.WHITE);
  109 + this.lblTitle.setPreferredSize(new Dimension(400, 26));
  110 +
  111 + this.lblSubTitle = new JLabel("Your password will not be stored, logging in with Yggdrasil");
  112 + this.lblSubTitle.setBorder(new EmptyBorder(0, 16, 0, 16));
  113 + this.lblSubTitle.setForeground(Color.WHITE);
  114 + this.lblSubTitle.setPreferredSize(new Dimension(400, 16));
  115 +
  116 + this.lblMessage = new JLabel("Enter your login details for minecraft.net");
  117 + this.lblMessage.setForeground(Color.WHITE);
  118 +
  119 + this.lblUserName = new JLabel("User name");
  120 + this.lblUserName.setForeground(Color.WHITE);
  121 +
  122 + this.lblPassword = new JLabel("Password");
  123 + this.lblPassword.setForeground(Color.WHITE);
114 124
  125 + this.txtUsername = new TextField();
  126 + this.txtUsername.setPreferredSize(new Dimension(200, 22));
  127 + this.txtUsername.setText(username);
  128 +
  129 + this.txtPassword = new TextField();
  130 + this.txtPassword.setEchoChar('*');
  131 + this.txtPassword.setPreferredSize(new Dimension(200, 22));
  132 + this.txtPassword.setText(password);
  133 +
115 this.btnLogin = new JButton("Log in"); 134 this.btnLogin = new JButton("Log in");
116 this.btnLogin.addActionListener(new ActionListener() { 135 this.btnLogin.addActionListener(new ActionListener() {
117 @Override public void actionPerformed(ActionEvent e) { 136 @Override public void actionPerformed(ActionEvent e) {
118 LoginPanel.this.onLoginClick(); 137 LoginPanel.this.onLoginClick();
119 } 138 }
120 }); 139 });
121 - 140 +
122 this.btnCancel = new JButton("Cancel"); 141 this.btnCancel = new JButton("Cancel");
123 this.btnCancel.addActionListener(new ActionListener() { 142 this.btnCancel.addActionListener(new ActionListener() {
124 @Override public void actionPerformed(ActionEvent e) { 143 @Override public void actionPerformed(ActionEvent e) {
125 LoginPanel.this.onCancelClick(); 144 LoginPanel.this.onCancelClick();
126 } 145 }
127 }); 146 });
  147 +
  148 + this.chkOffline = new JCheckBox("Never ask me to log in (always run offline)");
  149 + this.chkOffline.setPreferredSize(new Dimension(380, 23));
  150 + this.chkOffline.setForeground(Color.WHITE);
  151 + this.chkOffline.setOpaque(false);
  152 + this.chkOffline.addActionListener(new ActionListener()
  153 + {
  154 + @Override public void actionPerformed(ActionEvent e)
  155 + {
  156 + LoginPanel.this.onOfflineCheckedChanged();
  157 + }
  158 + });
  159 +
  160 + GridBagConstraints lblMessageConstraints = new GridBagConstraints();
  161 + lblMessageConstraints.anchor = GridBagConstraints.WEST;
  162 + lblMessageConstraints.gridwidth = 2;
  163 + lblMessageConstraints.insets = new Insets(0, 0, 5, 5);
  164 + lblMessageConstraints.gridx = 2;
  165 + lblMessageConstraints.gridy = 0;
128 166
129 - this.lblMessage = new JLabel("Enter your login details for minecraft.net");  
130 - this.lblMessage.setForeground(new Color(255, 255, 255));  
131 - GridBagConstraints gbc_lblNewLabel_1 = new GridBagConstraints();  
132 - gbc_lblNewLabel_1.anchor = GridBagConstraints.WEST;  
133 - gbc_lblNewLabel_1.gridwidth = 2;  
134 - gbc_lblNewLabel_1.insets = new Insets(0, 0, 5, 5);  
135 - gbc_lblNewLabel_1.gridx = 2;  
136 - gbc_lblNewLabel_1.gridy = 0;  
137 - this.panelLogin.add(this.lblMessage, gbc_lblNewLabel_1); 167 + GridBagConstraints lblUserNameConstraints = new GridBagConstraints();
  168 + lblUserNameConstraints.anchor = GridBagConstraints.WEST;
  169 + lblUserNameConstraints.fill = GridBagConstraints.VERTICAL;
  170 + lblUserNameConstraints.insets = new Insets(0, 0, 5, 5);
  171 + lblUserNameConstraints.gridx = 1;
  172 + lblUserNameConstraints.gridy = 1;
138 173
139 - this.lblNewLabel = new JLabel("User name");  
140 - this.lblNewLabel.setFocusable(false);  
141 - this.lblNewLabel.setForeground(new Color(255, 255, 255));  
142 - GridBagConstraints gbc_lblNewLabel = new GridBagConstraints();  
143 - gbc_lblNewLabel.anchor = GridBagConstraints.WEST;  
144 - gbc_lblNewLabel.fill = GridBagConstraints.VERTICAL;  
145 - gbc_lblNewLabel.insets = new Insets(0, 0, 5, 5);  
146 - gbc_lblNewLabel.gridx = 1;  
147 - gbc_lblNewLabel.gridy = 1;  
148 - this.panelLogin.add(this.lblNewLabel, gbc_lblNewLabel); 174 + GridBagConstraints lblPasswordConstraints = new GridBagConstraints();
  175 + lblPasswordConstraints.anchor = GridBagConstraints.WEST;
  176 + lblPasswordConstraints.fill = GridBagConstraints.VERTICAL;
  177 + lblPasswordConstraints.insets = new Insets(0, 0, 5, 5);
  178 + lblPasswordConstraints.gridx = 1;
  179 + lblPasswordConstraints.gridy = 2;
149 180
150 - this.txtUsername = new TextField();  
151 - this.txtUsername.setPreferredSize(new Dimension(200, 22));  
152 - this.txtUsername.setText(username);  
153 - GridBagConstraints gbc_txtUsername = new GridBagConstraints();  
154 - gbc_txtUsername.gridwidth = 2;  
155 - gbc_txtUsername.fill = GridBagConstraints.HORIZONTAL;  
156 - gbc_txtUsername.insets = new Insets(0, 0, 5, 0);  
157 - gbc_txtUsername.gridx = 2;  
158 - gbc_txtUsername.gridy = 1;  
159 - this.panelLogin.add(this.txtUsername, gbc_txtUsername); 181 + GridBagConstraints txtUsernameConstraints = new GridBagConstraints();
  182 + txtUsernameConstraints.gridwidth = 2;
  183 + txtUsernameConstraints.fill = GridBagConstraints.HORIZONTAL;
  184 + txtUsernameConstraints.insets = new Insets(0, 0, 5, 0);
  185 + txtUsernameConstraints.gridx = 2;
  186 + txtUsernameConstraints.gridy = 1;
160 187
161 - this.lblPassword = new JLabel("Password");  
162 - this.lblPassword.setFocusable(false);  
163 - this.lblPassword.setForeground(Color.WHITE);  
164 - GridBagConstraints gbc_lblPassword = new GridBagConstraints();  
165 - gbc_lblPassword.anchor = GridBagConstraints.WEST;  
166 - gbc_lblPassword.fill = GridBagConstraints.VERTICAL;  
167 - gbc_lblPassword.insets = new Insets(0, 0, 5, 5);  
168 - gbc_lblPassword.gridx = 1;  
169 - gbc_lblPassword.gridy = 2;  
170 - this.panelLogin.add(this.lblPassword, gbc_lblPassword); 188 + GridBagConstraints txtPasswordConstraints = new GridBagConstraints();
  189 + txtPasswordConstraints.gridwidth = 2;
  190 + txtPasswordConstraints.insets = new Insets(0, 0, 5, 0);
  191 + txtPasswordConstraints.fill = GridBagConstraints.HORIZONTAL;
  192 + txtPasswordConstraints.gridx = 2;
  193 + txtPasswordConstraints.gridy = 2;
171 194
172 - this.txtPassword = new TextField();  
173 - this.txtPassword.setEchoChar('*');  
174 - this.txtPassword.setPreferredSize(new Dimension(200, 22));  
175 - this.txtPassword.setText(password);  
176 - GridBagConstraints gbc_txtPassword = new GridBagConstraints();  
177 - gbc_txtPassword.gridwidth = 2;  
178 - gbc_txtPassword.insets = new Insets(0, 0, 5, 0);  
179 - gbc_txtPassword.fill = GridBagConstraints.HORIZONTAL;  
180 - gbc_txtPassword.gridx = 2;  
181 - gbc_txtPassword.gridy = 2;  
182 - this.panelLogin.add(this.txtPassword, gbc_txtPassword);  
183 - GridBagConstraints gbc_btnCancel = new GridBagConstraints();  
184 - gbc_btnCancel.anchor = GridBagConstraints.EAST;  
185 - gbc_btnCancel.insets = new Insets(0, 0, 0, 5);  
186 - gbc_btnCancel.gridx = 2;  
187 - gbc_btnCancel.gridy = 3;  
188 - this.panelLogin.add(this.btnCancel, gbc_btnCancel);  
189 - GridBagConstraints gbc_btnLogin = new GridBagConstraints();  
190 - gbc_btnLogin.fill = GridBagConstraints.HORIZONTAL;  
191 - gbc_btnLogin.gridx = 3;  
192 - gbc_btnLogin.gridy = 3;  
193 - this.panelLogin.add(this.btnLogin, gbc_btnLogin); 195 + GridBagConstraints btnLoginConstraints = new GridBagConstraints();
  196 + btnLoginConstraints.fill = GridBagConstraints.HORIZONTAL;
  197 + btnLoginConstraints.gridx = 3;
  198 + btnLoginConstraints.gridy = 3;
  199 +
  200 + GridBagConstraints btnCancelConstraints = new GridBagConstraints();
  201 + btnCancelConstraints.anchor = GridBagConstraints.EAST;
  202 + btnCancelConstraints.insets = new Insets(0, 0, 0, 5);
  203 + btnCancelConstraints.gridx = 2;
  204 + btnCancelConstraints.gridy = 3;
  205 +
  206 + this.add(this.panelTitle, BorderLayout.NORTH);
  207 + this.add(this.panelPadding, BorderLayout.CENTER);
  208 + this.add(this.panelBottom, BorderLayout.SOUTH);
  209 +
  210 + this.panelPadding.add(this.panelCentre);
194 211
  212 + this.panelTitle.add(this.lblTitle);
  213 + this.panelTitle.add(this.lblSubTitle);
  214 +
  215 + this.panelCentre.add(this.lblMessage, lblMessageConstraints);
  216 + this.panelCentre.add(this.lblUserName, lblUserNameConstraints);
  217 + this.panelCentre.add(this.lblPassword, lblPasswordConstraints);
  218 + this.panelCentre.add(this.txtUsername, txtUsernameConstraints);
  219 + this.panelCentre.add(this.txtPassword, txtPasswordConstraints);
  220 + this.panelCentre.add(this.btnLogin, btnLoginConstraints);
  221 + this.panelCentre.add(this.btnCancel, btnCancelConstraints);
  222 +
  223 + this.panelBottom.add(this.chkOffline);
  224 +
195 this.tabOrder.add(this.txtUsername); 225 this.tabOrder.add(this.txtUsername);
196 this.tabOrder.add(this.txtPassword); 226 this.tabOrder.add(this.txtPassword);
197 this.tabOrder.add(this.btnLogin); 227 this.tabOrder.add(this.btnLogin);
@@ -218,7 +248,7 @@ public class LoginPanel extends JPanel @@ -218,7 +248,7 @@ public class LoginPanel extends JPanel
218 248
219 protected void onLoginClick() 249 protected void onLoginClick()
220 { 250 {
221 - this.loginClicked = true; 251 + this.dialogResult = true;
222 this.dialog.setVisible(false); 252 this.dialog.setVisible(false);
223 } 253 }
224 254
@@ -227,6 +257,14 @@ public class LoginPanel extends JPanel @@ -227,6 +257,14 @@ public class LoginPanel extends JPanel
227 this.dialog.setVisible(false); 257 this.dialog.setVisible(false);
228 } 258 }
229 259
  260 + protected void onOfflineCheckedChanged()
  261 + {
  262 + boolean selected = this.chkOffline.isSelected();
  263 + this.btnLogin.setText(selected ? "Work Offline" : "Log In");
  264 + this.txtUsername.setEnabled(!selected);
  265 + this.txtPassword.setEnabled(!selected);
  266 + }
  267 +
230 /** 268 /**
231 * @param dialog 269 * @param dialog
232 * @param panel 270 * @param panel
@@ -252,7 +290,7 @@ public class LoginPanel extends JPanel @@ -252,7 +290,7 @@ public class LoginPanel extends JPanel
252 { 290 {
253 this.dialog.setVisible(true); 291 this.dialog.setVisible(true);
254 this.dialog.dispose(); 292 this.dialog.dispose();
255 - return this.loginClicked; 293 + return this.dialogResult;
256 } 294 }
257 295
258 public String getUsername() 296 public String getUsername()
@@ -290,7 +328,7 @@ public class LoginPanel extends JPanel @@ -290,7 +328,7 @@ public class LoginPanel extends JPanel
290 return panel; 328 return panel;
291 } 329 }
292 330
293 - class CustomFocusTraversal extends FocusTraversalPolicy 331 + class ListFocusTraversal extends FocusTraversalPolicy
294 { 332 {
295 private final List<Component> components = new ArrayList<Component>(); 333 private final List<Component> components = new ArrayList<Component>();
296 334
debug/com/mumfrey/liteloader/debug/Start.java
1 package com.mumfrey.liteloader.debug; 1 package com.mumfrey.liteloader.debug;
2 import java.io.File; 2 import java.io.File;
  3 +import java.util.logging.ConsoleHandler;
  4 +import java.util.logging.Handler;
3 import java.util.logging.Logger; 5 import java.util.logging.Logger;
4 6
5 import net.minecraft.launchwrapper.Launch; 7 import net.minecraft.launchwrapper.Launch;
6 8
7 import com.mumfrey.liteloader.launch.LiteLoaderTweaker; 9 import com.mumfrey.liteloader.launch.LiteLoaderTweaker;
  10 +import com.mumfrey.liteloader.util.log.LiteLoaderLogFormatter;
8 11
9 /** 12 /**
10 * Wrapper class for LaunchWrapper Main class, which logs into minecraft.net first so that online shizzle can be tested 13 * Wrapper class for LaunchWrapper Main class, which logs into minecraft.net first so that online shizzle can be tested
@@ -23,6 +26,8 @@ public abstract class Start @@ -23,6 +26,8 @@ public abstract class Start
23 */ 26 */
24 public static void main(String[] args) 27 public static void main(String[] args)
25 { 28 {
  29 + Start.prepareLogger();
  30 +
26 String usernameFromCmdLine = (args.length > 0) ? args[0] : null; 31 String usernameFromCmdLine = (args.length > 0) ? args[0] : null;
27 String passwordFromCmdLine = (args.length > 1) ? args[1] : null; 32 String passwordFromCmdLine = (args.length > 1) ? args[1] : null;
28 33
@@ -46,4 +51,13 @@ public abstract class Start @@ -46,4 +51,13 @@ public abstract class Start
46 51
47 Launch.main(args); 52 Launch.main(args);
48 } 53 }
  54 +
  55 + private static void prepareLogger()
  56 + {
  57 + for (Handler handler : Start.logger.getParent().getHandlers())
  58 + {
  59 + if (handler instanceof ConsoleHandler)
  60 + handler.setFormatter(new LiteLoaderLogFormatter());
  61 + }
  62 + }
49 } 63 }