1
0
mirror of https://github.com/SKCraft/Launcher.git synced 2025-01-05 19:09:03 +01:00

Start refactoring auth framework

Featuring:
* New account selection dialog
* Upgrade to Java 8!
* Questionable transitional class names
* Lots of swing code!
This commit is contained in:
Henry Le Grys 2021-02-08 05:48:05 +00:00
parent 25a433ab5b
commit b6252863da
14 changed files with 427 additions and 216 deletions

View File

@ -29,8 +29,8 @@ subprojects {
group = 'com.skcraft'
version = '4.4-SNAPSHOT'
sourceCompatibility = 1.6
targetCompatibility = 1.6
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()

View File

@ -12,8 +12,9 @@ import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.skcraft.launcher.auth.AccountList;
import com.skcraft.launcher.auth.LoginService;
import com.skcraft.launcher.auth.NewAccountList;
import com.skcraft.launcher.auth.UserType;
import com.skcraft.launcher.auth.YggdrasilLoginService;
import com.skcraft.launcher.launch.LaunchSupervisor;
import com.skcraft.launcher.model.minecraft.Library;
@ -63,7 +64,7 @@ public final class Launcher {
@Getter private final Properties properties;
@Getter private final InstanceList instances;
@Getter private final Configuration config;
@Getter private final AccountList accounts;
@Getter private final NewAccountList accounts;
@Getter private final AssetsRoot assets;
@Getter private final LaunchSupervisor launchSupervisor = new LaunchSupervisor(this);
@Getter private final UpdateManager updateManager = new UpdateManager(this);
@ -96,14 +97,10 @@ public final class Launcher {
this.instances = new InstanceList(this);
this.assets = new AssetsRoot(new File(baseDir, "assets"));
this.config = Persistence.load(new File(configDir, "config.json"), Configuration.class);
this.accounts = Persistence.load(new File(configDir, "accounts.dat"), AccountList.class);
this.accounts = Persistence.load(new File(configDir, "accounts.dat"), NewAccountList.class);
setDefaultConfig();
if (accounts.getSize() > 0) {
accounts.setSelectedItem(accounts.getElementAt(0));
}
executor.submit(new Runnable() {
@Override
public void run() {
@ -161,12 +158,20 @@ public final class Launcher {
}
/**
* Get a login service.
* Get the Yggdrasil login service.
*
* @return a login service
* @return the Yggdrasil (legacy) login service
*/
public LoginService getLoginService() {
return new YggdrasilLoginService(HttpRequest.url(getProperties().getProperty("yggdrasilAuthUrl")));
public YggdrasilLoginService getYggdrasil() {
return new YggdrasilLoginService(HttpRequest.url(getProperties().getProperty("yggdrasilAuthUrl")), accounts.getClientId());
}
public LoginService getLoginService(UserType type) {
if (type == UserType.MICROSOFT) {
return null; // TODO: Microsoft login service
} else {
return getYggdrasil();
}
}
/**

View File

@ -29,7 +29,7 @@ import java.util.List;
getterVisibility = JsonAutoDetect.Visibility.NONE,
setterVisibility = JsonAutoDetect.Visibility.NONE,
fieldVisibility = JsonAutoDetect.Visibility.NONE)
public class AccountList extends AbstractListModel implements ComboBoxModel {
public class AccountList extends AbstractListModel<Account> implements ComboBoxModel<Account> {
@JsonProperty
@Getter

View File

@ -7,7 +7,6 @@
package com.skcraft.launcher.auth;
import java.io.IOException;
import java.util.List;
/**
* A service for creating authenticated sessions.
@ -15,17 +14,15 @@ import java.util.List;
public interface LoginService {
/**
* Attempt to login with the given details.
* Attempt to restore a saved session into an active session.
*
* @param agent the game to authenticate for, such as "Minecraft"
* @param id the login ID
* @param password the password
* @return a list of authenticated sessions, which corresponds to identities
* @param savedSession Session to restore
* @return An authenticated session, which corresponds to a Minecraft account
* @throws IOException thrown on I/O error
* @throws InterruptedException thrown if interrupted
* @throws AuthenticationException thrown on an authentication error
*/
List<? extends Session> login(String agent, String id, String password)
Session restore(SavedSession savedSession)
throws IOException, InterruptedException, AuthenticationException;
}

View File

@ -0,0 +1,74 @@
package com.skcraft.launcher.auth;
import com.beust.jcommander.internal.Lists;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.skcraft.launcher.dialog.component.ListListenerReducer;
import com.skcraft.launcher.persistence.Scrambled;
import lombok.Data;
import org.apache.commons.lang.RandomStringUtils;
import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.util.List;
/**
* Persisted account list
*/
@Scrambled("ACCOUNT_LIST_NOT_SECURITY!")
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class NewAccountList implements ListModel<SavedSession> {
private List<SavedSession> accounts = Lists.newArrayList();
private String clientId = RandomStringUtils.randomAlphanumeric(24);
@JsonIgnore private final ListListenerReducer listeners = new ListListenerReducer();
public synchronized void add(SavedSession session) {
accounts.add(session);
int index = accounts.size() - 1;
listeners.intervalAdded(new ListDataEvent(this, ListDataEvent.INTERVAL_ADDED, index, index));
}
public synchronized void remove(SavedSession session) {
int index = accounts.indexOf(session);
if (index > -1) {
accounts.remove(index);
listeners.intervalRemoved(new ListDataEvent(this, ListDataEvent.INTERVAL_REMOVED, index, index));
}
}
public synchronized void update(SavedSession newSavedSession) {
int index = accounts.indexOf(newSavedSession);
if (index > -1) {
accounts.set(index, newSavedSession);
listeners.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, index, index));
} else {
this.add(newSavedSession);
}
}
@Override
public int getSize() {
return accounts.size();
}
@Override
public SavedSession getElementAt(int index) {
return accounts.get(index);
}
@Override
public void addListDataListener(ListDataListener l) {
listeners.addListDataListener(l);
}
@Override
public void removeListDataListener(ListDataListener l) {
listeners.removeListDataListener(l);
}
}

View File

@ -0,0 +1,36 @@
package com.skcraft.launcher.auth;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* Represents a session saved to disk.
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SavedSession {
private UserType type;
private String uuid;
private String username;
private String accessToken;
private String refreshToken;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SavedSession that = (SavedSession) o;
return getUuid().equals(that.getUuid());
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(uuid)
.toHashCode();
}
}

View File

@ -71,4 +71,19 @@ public interface Session {
*/
boolean isOnline();
/**
* Convert this session to a saved session
* @return Saved session that represents this active session
*/
default SavedSession toSavedSession() {
SavedSession savedSession = new SavedSession();
savedSession.setType(getUserType());
savedSession.setUsername(getName());
savedSession.setUuid(getUuid());
savedSession.setAccessToken(getAccessToken());
return savedSession;
}
}

View File

@ -18,7 +18,11 @@ public enum UserType {
/**
* Mojang accounts login with an email address.
*/
MOJANG;
MOJANG,
/**
* Microsoft accounts login via OAuth.
*/
MICROSOFT;
/**
* Return a lowercase version of the enum type.

View File

@ -11,45 +11,63 @@ import com.skcraft.launcher.util.HttpRequest;
import lombok.Data;
import lombok.NonNull;
import lombok.ToString;
import lombok.extern.java.Log;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Creates authenticated sessions using the Mojang Yggdrasil login protocol.
*/
@Log
public class YggdrasilLoginService implements LoginService {
private final URL authUrl;
private final String clientId;
/**
* Create a new login service with the given authentication URL.
*
* @param authUrl the authentication URL
* @param clientId
*/
public YggdrasilLoginService(@NonNull URL authUrl) {
public YggdrasilLoginService(@NonNull URL authUrl, String clientId) {
this.authUrl = authUrl;
this.clientId = clientId;
}
public Session login(String agent, String id, String password)
throws IOException, InterruptedException, AuthenticationException {
AuthenticatePayload payload = new AuthenticatePayload(new Agent(agent), id, password, clientId);
return call(this.authUrl, payload);
}
@Override
public List<? extends Session> login(String agent, String id, String password)
public Session restore(SavedSession savedSession)
throws IOException, InterruptedException, AuthenticationException {
Object payload = new AuthenticatePayload(new Agent(agent), id, password);
RefreshPayload payload = new RefreshPayload(savedSession.getAccessToken(), clientId);
HttpRequest request = HttpRequest
.post(authUrl)
return call(new URL(this.authUrl, "/refresh"), payload);
}
private Session call(URL url, Object payload)
throws IOException, InterruptedException, AuthenticationException {
HttpRequest req = HttpRequest
.post(url)
.bodyJson(payload)
.execute();
if (request.getResponseCode() != 200) {
ErrorResponse error = request.returnContent().asJson(ErrorResponse.class);
if (req.getResponseCode() != 200) {
ErrorResponse error = req.returnContent().asJson(ErrorResponse.class);
log.warning(error.toString());
throw new AuthenticationException(error.getErrorMessage(), error.getErrorMessage());
} else {
AuthenticateResponse response = request.returnContent().asJson(AuthenticateResponse.class);
return response.getAvailableProfiles();
AuthenticateResponse response = req.returnContent().asJson(AuthenticateResponse.class);
return response.getSelectedProfile();
}
}
@ -64,6 +82,14 @@ public class YggdrasilLoginService implements LoginService {
private final Agent agent;
private final String username;
private final String password;
private final String clientToken;
}
@Data
private static class RefreshPayload {
private final String accessToken;
private final String clientToken;
private boolean requestUser = true;
}
@Data
@ -71,8 +97,7 @@ public class YggdrasilLoginService implements LoginService {
private static class AuthenticateResponse {
private String accessToken;
private String clientToken;
@JsonManagedReference private List<Profile> availableProfiles;
private Profile selectedProfile;
@JsonManagedReference private Profile selectedProfile;
}
@Data

View File

@ -0,0 +1,178 @@
package com.skcraft.launcher.dialog;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.skcraft.concurrency.ObservableFuture;
import com.skcraft.concurrency.ProgressObservable;
import com.skcraft.launcher.Launcher;
import com.skcraft.launcher.auth.LoginService;
import com.skcraft.launcher.auth.SavedSession;
import com.skcraft.launcher.auth.Session;
import com.skcraft.launcher.persistence.Persistence;
import com.skcraft.launcher.swing.LinedBoxPanel;
import com.skcraft.launcher.swing.SwingHelper;
import com.skcraft.launcher.util.SharedLocale;
import com.skcraft.launcher.util.SwingExecutor;
import lombok.RequiredArgsConstructor;
import javax.swing.*;
import java.awt.*;
import java.net.URL;
import java.util.concurrent.Callable;
import static com.skcraft.launcher.util.HttpRequest.url;
public class AccountSelectDialog extends JDialog {
private final JList<SavedSession> accountList;
private final JButton loginButton = new JButton(SharedLocale.tr("login.login"));
private final JButton cancelButton = new JButton(SharedLocale.tr("button.cancel"));
private final JButton addAccountButton = new JButton(SharedLocale.tr("accounts.addNew"));
private final LinedBoxPanel buttonsPanel = new LinedBoxPanel(true);
private final Launcher launcher;
private Session selected;
public AccountSelectDialog(Window owner, Launcher launcher) {
super(owner, ModalityType.DOCUMENT_MODAL);
this.launcher = launcher;
this.accountList = new JList<>(launcher.getAccounts());
setTitle(SharedLocale.tr("accounts.title"));
initComponents();
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setMinimumSize(new Dimension(420, 50));
setResizable(false);
pack();
setLocationRelativeTo(owner);
}
private void initComponents() {
accountList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
accountList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
accountList.setVisibleRowCount(0);
accountList.setCellRenderer(new AccountRenderer());
JScrollPane accountPane = new JScrollPane(accountList);
accountPane.setPreferredSize(new Dimension(250, 100));
accountPane.setAlignmentX(LEFT_ALIGNMENT);
loginButton.setFont(loginButton.getFont().deriveFont(Font.BOLD));
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(26, 13, 13, 13));
buttonsPanel.addGlue();
buttonsPanel.addElement(loginButton);
buttonsPanel.addElement(cancelButton);
JPanel listPane = new JPanel();
listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
listPane.add(accountPane);
listPane.add(Box.createVerticalStrut(5));
listPane.add(addAccountButton);
listPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
add(listPane, BorderLayout.CENTER);
add(buttonsPanel, BorderLayout.SOUTH);
loginButton.addActionListener(ev -> attemptLogin(accountList.getSelectedValue()));
cancelButton.addActionListener(ev -> dispose());
addAccountButton.addActionListener(ev -> {
Session newSession = LoginDialog.showLoginRequest(this, launcher);
if (newSession != null) {
launcher.getAccounts().add(newSession.toSavedSession());
}
});
}
@Override
public void dispose() {
accountList.setModel(new DefaultListModel<>());
super.dispose();
}
public static Session showAccountRequest(Window owner, Launcher launcher) {
AccountSelectDialog dialog = new AccountSelectDialog(owner, launcher);
dialog.setVisible(true);
if (dialog.selected != null) {
launcher.getAccounts().update(dialog.selected.toSavedSession());
Persistence.commitAndForget(launcher.getAccounts());
}
return dialog.selected;
}
private void setResult(Session result) {
this.selected = result;
dispose();
}
private void attemptLogin(SavedSession session) {
LoginService loginService = launcher.getLoginService(session.getType());
RestoreSessionCallable callable = new RestoreSessionCallable(loginService, session);
ObservableFuture<Session> future = new ObservableFuture<>(launcher.getExecutor().submit(callable), callable);
Futures.addCallback(future, new FutureCallback<Session>() {
@Override
public void onSuccess(Session result) {
// session.setAccessToken(result.getAccessToken());
setResult(result);
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
}, SwingExecutor.INSTANCE);
ProgressDialog.showProgress(this, future, SharedLocale.tr("login.loggingInTitle"),
SharedLocale.tr("login.loggingInStatus"));
SwingHelper.addErrorDialogCallback(this, future);
}
@RequiredArgsConstructor
private static class RestoreSessionCallable implements Callable<Session>, ProgressObservable {
private final LoginService service;
private final SavedSession session;
@Override
public Session call() throws Exception {
return service.restore(session);
}
@Override
public String getStatus() {
return SharedLocale.tr("accounts.refreshingStatus");
}
@Override
public double getProgress() {
return -1;
}
}
private static class AccountRenderer extends JLabel implements ListCellRenderer<SavedSession> {
public AccountRenderer() {
setHorizontalAlignment(CENTER);
}
@Override
public Component getListCellRendererComponent(JList<? extends SavedSession> list, SavedSession value, int index, boolean isSelected, boolean cellHasFocus) {
setText(value.getUsername());
URL avatarUrl = url("https://visage.surgeplay.com/face/24/" + value.getUuid() + ".png");
setIcon(new ImageIcon(avatarUrl));
if (isSelected) {
setOpaque(true);
setBackground(new Color(0x397BBF));
} else {
setOpaque(false);
}
return this;
}
}
}

View File

@ -6,16 +6,18 @@
package com.skcraft.launcher.dialog;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.skcraft.concurrency.ObservableFuture;
import com.skcraft.concurrency.ProgressObservable;
import com.skcraft.launcher.Configuration;
import com.skcraft.launcher.Launcher;
import com.skcraft.launcher.auth.*;
import com.skcraft.launcher.swing.*;
import com.skcraft.launcher.auth.Account;
import com.skcraft.launcher.auth.AuthenticationException;
import com.skcraft.launcher.auth.Session;
import com.skcraft.launcher.auth.YggdrasilLoginService;
import com.skcraft.launcher.persistence.Persistence;
import com.skcraft.launcher.swing.*;
import com.skcraft.launcher.util.SharedLocale;
import com.skcraft.launcher.util.SwingExecutor;
import lombok.Getter;
@ -23,10 +25,10 @@ import lombok.NonNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
/**
@ -35,16 +37,12 @@ import java.util.concurrent.Callable;
public class LoginDialog extends JDialog {
private final Launcher launcher;
@Getter private final AccountList accounts;
@Getter private Session session;
private final JComboBox idCombo = new JComboBox();
private final JTextField usernameText = new JTextField();
private final JPasswordField passwordText = new JPasswordField();
private final JCheckBox rememberIdCheck = new JCheckBox(SharedLocale.tr("login.rememberId"));
private final JCheckBox rememberPassCheck = new JCheckBox(SharedLocale.tr("login.rememberPassword"));
private final JButton loginButton = new JButton(SharedLocale.tr("login.login"));
private final LinkButton recoverButton = new LinkButton(SharedLocale.tr("login.recoverAccount"));
private final JButton offlineButton = new JButton(SharedLocale.tr("login.playOffline"));
private final JButton cancelButton = new JButton(SharedLocale.tr("button.cancel"));
private final FormPanel formPanel = new FormPanel();
private final LinedBoxPanel buttonsPanel = new LinedBoxPanel(true);
@ -59,7 +57,6 @@ public class LoginDialog extends JDialog {
super(owner, ModalityType.DOCUMENT_MODAL);
this.launcher = launcher;
this.accounts = launcher.getAccounts();
setTitle(SharedLocale.tr("login.title"));
initComponents();
@ -73,39 +70,21 @@ public class LoginDialog extends JDialog {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
removeListeners();
dispose();
}
});
}
@SuppressWarnings("unchecked")
private void removeListeners() {
idCombo.setModel(new DefaultComboBoxModel());
}
@SuppressWarnings("unchecked")
private void initComponents() {
idCombo.setModel(getAccounts());
updateSelection();
rememberIdCheck.setBorder(BorderFactory.createEmptyBorder());
rememberPassCheck.setBorder(BorderFactory.createEmptyBorder());
idCombo.setEditable(true);
idCombo.getEditor().selectAll();
usernameText.setEditable(true);
loginButton.setFont(loginButton.getFont().deriveFont(Font.BOLD));
formPanel.addRow(new JLabel(SharedLocale.tr("login.idEmail")), idCombo);
formPanel.addRow(new JLabel(SharedLocale.tr("login.idEmail")), usernameText);
formPanel.addRow(new JLabel(SharedLocale.tr("login.password")), passwordText);
formPanel.addRow(new JLabel(), rememberIdCheck);
formPanel.addRow(new JLabel(), rememberPassCheck);
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(26, 13, 13, 13));
if (launcher.getConfig().isOfflineEnabled()) {
buttonsPanel.addElement(offlineButton);
buttonsPanel.addElement(Box.createHorizontalStrut(2));
}
buttonsPanel.addElement(recoverButton);
buttonsPanel.addGlue();
buttonsPanel.addElement(loginButton);
@ -118,162 +97,24 @@ public class LoginDialog extends JDialog {
passwordText.setComponentPopupMenu(TextFieldPopupMenu.INSTANCE);
idCombo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateSelection();
}
});
idCombo.getEditor().getEditorComponent().addMouseListener(new PopupMouseAdapter() {
@Override
protected void showPopup(MouseEvent e) {
popupManageMenu(e.getComponent(), e.getX(), e.getY());
}
});
recoverButton.addActionListener(
ActionListeners.openURL(recoverButton, launcher.getProperties().getProperty("resetPasswordUrl")));
loginButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
prepareLogin();
}
});
offlineButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setResult(new OfflineSession(launcher.getProperties().getProperty("offlinePlayerName")));
removeListeners();
dispose();
}
});
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
removeListeners();
dispose();
}
});
rememberPassCheck.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (rememberPassCheck.isSelected()) {
rememberIdCheck.setSelected(true);
}
}
});
rememberIdCheck.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (!rememberIdCheck.isSelected()) {
rememberPassCheck.setSelected(false);
}
}
});
}
private void popupManageMenu(Component component, int x, int y) {
Object selected = idCombo.getSelectedItem();
JPopupMenu popup = new JPopupMenu();
JMenuItem menuItem;
if (selected != null && selected instanceof Account) {
final Account account = (Account) selected;
menuItem = new JMenuItem(SharedLocale.tr("login.forgetUser"));
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
accounts.remove(account);
Persistence.commitAndForget(accounts);
}
});
popup.add(menuItem);
if (!Strings.isNullOrEmpty(account.getPassword())) {
menuItem = new JMenuItem(SharedLocale.tr("login.forgetPassword"));
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
account.setPassword(null);
Persistence.commitAndForget(accounts);
}
});
popup.add(menuItem);
}
}
menuItem = new JMenuItem(SharedLocale.tr("login.forgetAllPasswords"));
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (SwingHelper.confirmDialog(LoginDialog.this,
SharedLocale.tr("login.confirmForgetAllPasswords"),
SharedLocale.tr("login.forgetAllPasswordsTitle"))) {
accounts.forgetPasswords();
Persistence.commitAndForget(accounts);
}
}
});
popup.add(menuItem);
popup.show(component, x, y);
}
private void updateSelection() {
Object selected = idCombo.getSelectedItem();
if (selected != null && selected instanceof Account) {
Account account = (Account) selected;
String password = account.getPassword();
rememberIdCheck.setSelected(true);
if (!Strings.isNullOrEmpty(password)) {
rememberPassCheck.setSelected(true);
passwordText.setText(password);
} else {
rememberPassCheck.setSelected(false);
}
} else {
passwordText.setText("");
rememberIdCheck.setSelected(true);
rememberPassCheck.setSelected(false);
}
loginButton.addActionListener(e -> prepareLogin());
cancelButton.addActionListener(e -> dispose());
}
@SuppressWarnings("deprecation")
private void prepareLogin() {
Object selected = idCombo.getSelectedItem();
if (selected != null && selected instanceof Account) {
Account account = (Account) selected;
if (!usernameText.getText().isEmpty()) {
String password = passwordText.getText();
if (password == null || password.isEmpty()) {
SwingHelper.showErrorDialog(this, SharedLocale.tr("login.noPasswordError"), SharedLocale.tr("login.noPasswordTitle"));
} else {
if (rememberPassCheck.isSelected()) {
account.setPassword(password);
} else {
account.setPassword(null);
}
if (rememberIdCheck.isSelected()) {
accounts.add(account);
} else {
accounts.remove(account);
}
Account account = new Account(usernameText.getText());
account.setLastUsed(new Date());
Persistence.commitAndForget(accounts);
attemptLogin(account, password);
}
} else {
@ -303,7 +144,6 @@ public class LoginDialog extends JDialog {
private void setResult(Session session) {
this.session = session;
removeListeners();
dispose();
}
@ -324,12 +164,12 @@ public class LoginDialog extends JDialog {
@Override
public Session call() throws AuthenticationException, IOException, InterruptedException {
LoginService service = launcher.getLoginService();
List<? extends Session> identities = service.login(launcher.getProperties().getProperty("agentName"), account.getId(), password);
YggdrasilLoginService service = launcher.getYggdrasil();
Session identity = service.login(launcher.getProperties().getProperty("agentName"), account.getId(), password);
// The list of identities (profiles in Mojang terms) corresponds to whether the account
// The presence of the identity (profile in Mojang terms) corresponds to whether the account
// owns the game, so we need to check that
if (identities.size() > 0) {
if (identity != null) {
// Set offline enabled flag to true
Configuration config = launcher.getConfig();
if (!config.isOfflineEnabled()) {
@ -337,8 +177,7 @@ public class LoginDialog extends JDialog {
Persistence.commitAndForget(config);
}
Persistence.commitAndForget(getAccounts());
return identities.get(0);
return identity;
} else {
throw new AuthenticationException("Minecraft not owned", SharedLocale.tr("login.minecraftNotOwnedError"));
}

View File

@ -0,0 +1,34 @@
package com.skcraft.launcher.dialog.component;
import com.beust.jcommander.internal.Lists;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import java.util.List;
public class ListListenerReducer implements ListDataListener {
private final List<ListDataListener> listeners = Lists.newArrayList();
@Override
public void intervalAdded(ListDataEvent e) {
listeners.forEach(it -> it.intervalAdded(e));
}
@Override
public void intervalRemoved(ListDataEvent e) {
listeners.forEach(it -> it.intervalRemoved(e));
}
@Override
public void contentsChanged(ListDataEvent e) {
listeners.forEach(it -> it.contentsChanged(e));
}
public void addListDataListener(ListDataListener l) {
listeners.add(l);
}
public void removeListDataListener(ListDataListener l) {
listeners.remove(l);
}
}

View File

@ -13,7 +13,7 @@ import com.skcraft.concurrency.ObservableFuture;
import com.skcraft.launcher.Instance;
import com.skcraft.launcher.Launcher;
import com.skcraft.launcher.auth.Session;
import com.skcraft.launcher.dialog.LoginDialog;
import com.skcraft.launcher.dialog.AccountSelectDialog;
import com.skcraft.launcher.dialog.ProgressDialog;
import com.skcraft.launcher.launch.LaunchOptions.UpdatePolicy;
import com.skcraft.launcher.persistence.Persistence;
@ -61,7 +61,7 @@ public class LaunchSupervisor {
if (options.getSession() != null) {
session = options.getSession();
} else {
session = LoginDialog.showLoginRequest(window, launcher);
session = AccountSelectDialog.showAccountRequest(window, launcher);
if (session == null) {
return;
}

View File

@ -87,6 +87,10 @@ launcher.notInstalledHint=(not installed)
launcher.requiresUpdateHint=(requires update)
launcher.updatePendingHint=(pending update)
accounts.title=Select the account to play with
accounts.refreshingStatus=Refreshing login session...
accounts.addNew=Add a new account...
login.rememberId=Remember my account in the list
login.rememberPassword=Remember my password
login.login=Login...