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 5 import java.io.FileWriter;
6 6 import java.io.IOException;
7 7 import java.net.Proxy;
  8 +import java.util.HashMap;
8 9 import java.util.Map;
9 10 import java.util.UUID;
10 11 import java.util.logging.Logger;
11 12  
  13 +import javax.swing.JOptionPane;
  14 +
12 15 import com.google.common.base.Strings;
13 16 import com.google.gson.Gson;
14 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 19 import com.mojang.authlib.Agent;
18 20 import com.mojang.authlib.GameProfile;
19 21 import com.mojang.authlib.exceptions.AuthenticationException;
... ... @@ -28,24 +30,62 @@ import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
28 30 */
29 31 public class LoginManager
30 32 {
  33 + /**
  34 + * Logger instance
  35 + */
31 36 private static Logger logger = Logger.getLogger("liteloader");
32 37  
  38 + /**
  39 + * Gson instance for serialising and deserialising the authentication data
  40 + */
33 41 private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
34 42  
  43 + /**
  44 + * Authentication service
  45 + */
35 46 private YggdrasilAuthenticationService authService;
36 47  
  48 + /**
  49 + * Authentication agent
  50 + */
37 51 private YggdrasilUserAuthentication authentication;
38 52  
  53 + /**
  54 + * JSON file to load/save auth data from
  55 + */
39 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 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 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 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 89 public LoginManager(File jsonFile)
50 90 {
51 91 this.jsonFile = jsonFile;
... ... @@ -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 101 public void resetAuth()
61 102 {
... ... @@ -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 110 private void load()
71 111 {
72   - if (this.jsonFile.exists())
  112 + if (this.jsonFile != null && this.jsonFile.exists())
73 113 {
74 114 FileReader fileReader = null;
75 115  
... ... @@ -78,7 +118,7 @@ public class LoginManager
78 118 fileReader = new FileReader(this.jsonFile);
79 119 AuthData authData = LoginManager.gson.fromJson(fileReader, AuthData.class);
80 120  
81   - if (authData != null)
  121 + if (authData != null && authData.validate())
82 122 {
83 123 this.logInfo("Initialising Yggdrasil authentication service with client token: %s", authData.getClientToken());
84 124 this.authService = new YggdrasilAuthenticationService(Proxy.NO_PROXY, authData.getClientToken());
... ... @@ -101,38 +141,57 @@ public class LoginManager
101 141 }
102 142 }
103 143  
  144 + /**
  145 + * Save auth data to the JSON file
  146 + */
104 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 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 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 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 190 this.logInfo("LoginManager is set to work offline, skipping login");
132 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 196 try
138 197 {
... ... @@ -153,37 +212,50 @@ public class LoginManager
153 212 {
154 213 this.logInfo("Authentication agent reported invalid credentials: %s", ex.getMessage());
155 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 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 260 catch (AuthenticationException ex)
189 261 {
... ... @@ -194,12 +266,34 @@ public class LoginManager
194 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 288 public String getProfileName()
198 289 {
199 290 GameProfile selectedProfile = this.authentication.getSelectedProfile();
200 291 return selectedProfile != null ? selectedProfile.getName() : this.defaultDisplayName;
201 292 }
202 293  
  294 + /**
  295 + * Get the session token
  296 + */
203 297 public String getAuthenticatedToken()
204 298 {
205 299 String accessToken = this.authentication.getAuthenticatedToken();
... ... @@ -211,16 +305,25 @@ public class LoginManager
211 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 313 class AuthData
215 314 {
  315 + @SerializedName("clientToken")
216 316 private String clientToken;
217 317  
  318 + @SerializedName("workOffline")
218 319 private boolean workOffline;
219 320  
  321 + @SerializedName("authData")
220 322 private Map<String, String> credentials;
221 323  
222 324 public AuthData()
223 325 {
  326 + // default ctor for Gson
224 327 }
225 328  
226 329 public AuthData(YggdrasilAuthenticationService authService, YggdrasilUserAuthentication authentication, boolean workOffline, String defaultUserName, String defaultDisplayName)
... ... @@ -236,6 +339,16 @@ public class LoginManager
236 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 352 public String getClientToken()
240 353 {
241 354 return this.clientToken;
... ...
debug/com/mumfrey/liteloader/debug/LoginPanel.java
... ... @@ -44,154 +44,184 @@ import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;
44 44 public class LoginPanel extends JPanel
45 45 {
46 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 59 private TextField txtUsername;
48 60 private TextField txtPassword;
49 61 private JButton btnLogin;
50 62 private JButton btnCancel;
  63 + private JCheckBox chkOffline;
51 64  
52   - private boolean loginClicked = false;
53   -
54 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 71 public LoginPanel(String username, String password, String error)
67 72 {
  73 + Color backColour = new Color(102, 118, 144);
  74 +
68 75 this.setFocusable(false);
69 76 this.setPreferredSize(new Dimension(400, 260));
70 77 this.setBackground(new Color(105, 105, 105));
71 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 90 this.panelPadding = new JPanel();
97   - this.panelPadding.setFocusable(false);
98 91 this.panelPadding.setBorder(new EmptyBorder(4, 8, 8, 8));
99 92 this.panelPadding.setOpaque(false);
100   - this.add(this.panelPadding, BorderLayout.CENTER);
101 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 134 this.btnLogin = new JButton("Log in");
116 135 this.btnLogin.addActionListener(new ActionListener() {
117 136 @Override public void actionPerformed(ActionEvent e) {
118 137 LoginPanel.this.onLoginClick();
119 138 }
120 139 });
121   -
  140 +
122 141 this.btnCancel = new JButton("Cancel");
123 142 this.btnCancel.addActionListener(new ActionListener() {
124 143 @Override public void actionPerformed(ActionEvent e) {
125 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 225 this.tabOrder.add(this.txtUsername);
196 226 this.tabOrder.add(this.txtPassword);
197 227 this.tabOrder.add(this.btnLogin);
... ... @@ -218,7 +248,7 @@ public class LoginPanel extends JPanel
218 248  
219 249 protected void onLoginClick()
220 250 {
221   - this.loginClicked = true;
  251 + this.dialogResult = true;
222 252 this.dialog.setVisible(false);
223 253 }
224 254  
... ... @@ -227,6 +257,14 @@ public class LoginPanel extends JPanel
227 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 269 * @param dialog
232 270 * @param panel
... ... @@ -252,7 +290,7 @@ public class LoginPanel extends JPanel
252 290 {
253 291 this.dialog.setVisible(true);
254 292 this.dialog.dispose();
255   - return this.loginClicked;
  293 + return this.dialogResult;
256 294 }
257 295  
258 296 public String getUsername()
... ... @@ -290,7 +328,7 @@ public class LoginPanel extends JPanel
290 328 return panel;
291 329 }
292 330  
293   - class CustomFocusTraversal extends FocusTraversalPolicy
  331 + class ListFocusTraversal extends FocusTraversalPolicy
294 332 {
295 333 private final List<Component> components = new ArrayList<Component>();
296 334  
... ...
debug/com/mumfrey/liteloader/debug/Start.java
1 1 package com.mumfrey.liteloader.debug;
2 2 import java.io.File;
  3 +import java.util.logging.ConsoleHandler;
  4 +import java.util.logging.Handler;
3 5 import java.util.logging.Logger;
4 6  
5 7 import net.minecraft.launchwrapper.Launch;
6 8  
7 9 import com.mumfrey.liteloader.launch.LiteLoaderTweaker;
  10 +import com.mumfrey.liteloader.util.log.LiteLoaderLogFormatter;
8 11  
9 12 /**
10 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 26 */
24 27 public static void main(String[] args)
25 28 {
  29 + Start.prepareLogger();
  30 +
26 31 String usernameFromCmdLine = (args.length > 0) ? args[0] : null;
27 32 String passwordFromCmdLine = (args.length > 1) ? args[1] : null;
28 33  
... ... @@ -46,4 +51,13 @@ public abstract class Start
46 51  
47 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 }
... ...