Added Realms tab

This commit is contained in:
Lenni0451 2023-11-20 22:47:29 +01:00
parent 842239b9b5
commit 9c97409d5a
No known key found for this signature in database
GPG Key ID: 5D59B86635AD3F2F
7 changed files with 394 additions and 16 deletions

View File

@ -23,7 +23,7 @@ public abstract class AUITab {
protected final ViaProxyUI frame;
private final String name;
private final JPanel contentPane;
protected final JPanel contentPane;
public AUITab(final ViaProxyUI frame, final String name) {
this.frame = frame;
@ -44,4 +44,7 @@ public abstract class AUITab {
protected abstract void init(final JPanel contentPane);
protected void onTabOpened() {
}
}

View File

@ -26,10 +26,7 @@ import net.lenni0451.reflect.stream.RStream;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.ui.events.UICloseEvent;
import net.raphimc.viaproxy.ui.events.UIInitEvent;
import net.raphimc.viaproxy.ui.impl.AccountsTab;
import net.raphimc.viaproxy.ui.impl.AdvancedTab;
import net.raphimc.viaproxy.ui.impl.GeneralTab;
import net.raphimc.viaproxy.ui.impl.UISettingsTab;
import net.raphimc.viaproxy.ui.impl.*;
import net.raphimc.viaproxy.util.logging.Logger;
import javax.swing.*;
@ -47,12 +44,13 @@ public class ViaProxyUI extends JFrame {
public static final int BORDER_PADDING = 10;
public static final int BODY_BLOCK_PADDING = 10;
private final JTabbedPane contentPane = new JTabbedPane();
public final JTabbedPane contentPane = new JTabbedPane();
private final List<AUITab> tabs = new ArrayList<>();
public final GeneralTab generalTab = new GeneralTab(this);
public final AdvancedTab advancedTab = new AdvancedTab(this);
public final AccountsTab accountsTab = new AccountsTab(this);
public final RealmsTab realmsTab = new RealmsTab(this);
public final UISettingsTab uiSettingsTab = new UISettingsTab(this);
private ImageIcon icon;
@ -114,8 +112,13 @@ public class ViaProxyUI extends JFrame {
tab.add(this.contentPane);
this.eventManager.register(tab);
});
this.contentPane.addChangeListener(e -> {
int selectedIndex = contentPane.getSelectedIndex();
if (selectedIndex >= 0 && selectedIndex < ViaProxyUI.this.tabs.size()) ViaProxyUI.this.tabs.get(selectedIndex).onTabOpened();
});
this.contentPane.setEnabledAt(this.contentPane.indexOfTab(this.accountsTab.getName()), false);
this.contentPane.setEnabledAt(this.contentPane.indexOfTab(this.realmsTab.getName()), false);
}
@EventHandler

View File

@ -47,12 +47,12 @@ import static net.raphimc.viaproxy.ui.ViaProxyUI.BORDER_PADDING;
public class GeneralTab extends AUITab {
private JTextField serverAddress;
private JComboBox<VersionEnum> serverVersion;
private JComboBox<String> authMethod;
JTextField serverAddress;
JComboBox<VersionEnum> serverVersion;
JComboBox<String> authMethod;
private JCheckBox betaCraftAuth;
private JLabel stateLabel;
private JButton stateButton;
JButton stateButton;
public GeneralTab(final ViaProxyUI frame) {
super(frame, "general");

View File

@ -0,0 +1,252 @@
/*
* This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.viaproxy.ui.impl;
import com.google.common.collect.Iterables;
import net.raphimc.minecraftauth.responsehandler.exception.RealmsResponseException;
import net.raphimc.minecraftauth.service.realms.AbstractRealmsService;
import net.raphimc.minecraftauth.service.realms.BedrockRealmsService;
import net.raphimc.minecraftauth.service.realms.JavaRealmsService;
import net.raphimc.minecraftauth.service.realms.model.RealmsWorld;
import net.raphimc.minecraftauth.util.MicrosoftConstants;
import net.raphimc.viabedrock.protocol.data.ProtocolConstants;
import net.raphimc.vialoader.util.VersionEnum;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.cli.options.Options;
import net.raphimc.viaproxy.saves.impl.accounts.Account;
import net.raphimc.viaproxy.saves.impl.accounts.BedrockAccount;
import net.raphimc.viaproxy.saves.impl.accounts.MicrosoftAccount;
import net.raphimc.viaproxy.ui.AUITab;
import net.raphimc.viaproxy.ui.I18n;
import net.raphimc.viaproxy.ui.ViaProxyUI;
import net.raphimc.viaproxy.util.GBC;
import net.raphimc.viaproxy.util.PaddedVerticalLayout;
import net.raphimc.viaproxy.util.logging.Logger;
import org.apache.http.impl.client.CloseableHttpClient;
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class RealmsTab extends AUITab {
private static final VersionEnum LATEST_JAVA_RELEASE;
private static final VersionEnum LATEST_JAVA_SNAPSHOT;
private static final CloseableHttpClient HTTP_CLIENT = MicrosoftConstants.createHttpClient();
static {
VersionEnum latestVersion = null;
VersionEnum latestSnapshotVersion = null;
for (int i = VersionEnum.OFFICIAL_SUPPORTED_PROTOCOLS.size() - 1; i >= 0; i--) {
VersionEnum version = VersionEnum.OFFICIAL_SUPPORTED_PROTOCOLS.get(i);
if (version.getProtocol().isSnapshot() && latestSnapshotVersion == null) {
latestSnapshotVersion = version;
} else if (!version.getProtocol().isSnapshot()) {
latestVersion = version;
break;
}
}
if (latestVersion == null) throw new IllegalStateException("Could not find compatible version");
LATEST_JAVA_RELEASE = latestVersion;
LATEST_JAVA_SNAPSHOT = latestSnapshotVersion;
}
private Account currentAccount = Options.MC_ACCOUNT;
private VersionEnum currentSelectedJavaVersion = LATEST_JAVA_RELEASE;
public RealmsTab(final ViaProxyUI frame) {
super(frame, "realms");
}
@Override
protected void onTabOpened() {
if (Options.MC_ACCOUNT != this.currentAccount) {
this.currentAccount = Options.MC_ACCOUNT;
this.reinit();
}
}
private void reinit() {
this.contentPane.removeAll();
this.init(this.contentPane);
this.contentPane.revalidate();
this.contentPane.repaint();
}
@Override
protected void init(JPanel contentPane) {
final JPanel body = new JPanel();
body.setLayout(new GridBagLayout());
final JLabel statusLabel = new JLabel("");
statusLabel.setFont(statusLabel.getFont().deriveFont(20F));
statusLabel.setHorizontalAlignment(SwingConstants.CENTER);
body.add(statusLabel);
if (this.currentAccount == null) {
statusLabel.setText(I18n.get("tab.realms.no_account"));
} else if (this.currentAccount instanceof MicrosoftAccount account) {
final JavaRealmsService realmsService = new JavaRealmsService(HTTP_CLIENT, Iterables.getLast(this.currentSelectedJavaVersion.getProtocol().getIncludedVersions()), account.getMcProfile());
this.loadRealms(realmsService, body, statusLabel);
} else if (this.currentAccount instanceof BedrockAccount account) {
final BedrockRealmsService realmsService = new BedrockRealmsService(HTTP_CLIENT, ProtocolConstants.BEDROCK_VERSION_NAME, account.getRealmsXsts());
this.loadRealms(realmsService, body, statusLabel);
} else {
statusLabel.setText(I18n.get("tab.realms.unsupported_account"));
}
contentPane.setLayout(new BorderLayout());
contentPane.add(body, BorderLayout.NORTH);
}
private void loadRealms(final AbstractRealmsService realmsService, final JPanel body, final JLabel statusLabel) {
statusLabel.setText(I18n.get("tab.realms.availability_check"));
realmsService.isAvailable().thenAccept(state -> {
if (state) {
SwingUtilities.invokeLater(() -> statusLabel.setText(I18n.get("tab.realms.loading_worlds")));
realmsService.getWorlds().thenAccept(worlds -> SwingUtilities.invokeLater(() -> {
body.remove(statusLabel);
this.addHeader(body, realmsService instanceof JavaRealmsService);
final JPanel realmsPanel = new JPanel();
realmsPanel.setLayout(new PaddedVerticalLayout(5, 5));
if (worlds.isEmpty()) {
JLabel label = new JLabel(I18n.get("tab.realms.no_worlds"));
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setFont(label.getFont().deriveFont(20F));
realmsPanel.add(label);
} else {
this.addRealms(realmsPanel, realmsService, worlds);
}
final JScrollPane realmsScrollPane = new JScrollPane(realmsPanel);
realmsScrollPane.getVerticalScrollBar().setUnitIncrement(10);
contentPane.add(realmsScrollPane, BorderLayout.CENTER);
contentPane.revalidate();
contentPane.repaint();
})).exceptionally(e -> {
final Throwable cause = e.getCause();
Logger.LOGGER.error("Failed to get realms worlds", cause);
ViaProxy.getUI().showError(I18n.get("tab.realms.error_generic", cause.getMessage()));
SwingUtilities.invokeLater(() -> statusLabel.setText(I18n.get("tab.realms.error_label")));
return null;
});
} else {
SwingUtilities.invokeLater(() -> statusLabel.setText(I18n.get("tab.realms.unavailable")));
}
}).exceptionally(e -> {
final Throwable cause = e.getCause();
Logger.LOGGER.error("Failed to check realms availability", cause);
ViaProxy.getUI().showError(I18n.get("tab.realms.error_generic", cause.getMessage()));
SwingUtilities.invokeLater(() -> statusLabel.setText(I18n.get("tab.realms.error_label")));
return null;
});
}
private void addHeader(final JPanel parent, final boolean showType) {
GBC.create(parent).grid(0, 0).weightx(1).insets(5, 5, 5, 5).fill(GridBagConstraints.HORIZONTAL).add(new JLabel(I18n.get("tab.realms.account", this.currentAccount.getDisplayString())));
if (showType && LATEST_JAVA_SNAPSHOT != null) {
JComboBox<String> type = new JComboBox<>();
type.addItem(I18n.get("tab.realms.release"));
type.addItem(I18n.get("tab.realms.snapshot"));
type.setSelectedIndex(this.currentSelectedJavaVersion.getProtocol().isSnapshot() ? 1 : 0);
type.addActionListener(e -> {
VersionEnum selected = type.getSelectedIndex() == 0 ? LATEST_JAVA_RELEASE : LATEST_JAVA_SNAPSHOT;
if (selected != this.currentSelectedJavaVersion) {
this.currentSelectedJavaVersion = selected;
this.reinit();
}
});
GBC.create(parent).grid(1, 0).insets(5, 0, 5, 5).anchor(GridBagConstraints.LINE_END).add(type);
}
}
private void addRealms(final JPanel parent, final AbstractRealmsService realmsService, final List<RealmsWorld> worlds) {
worlds.sort((o1, o2) -> {
boolean o1Compatible = o1.isCompatible() && !o1.isExpired();
boolean o2Compatible = o2.isCompatible() && !o2.isExpired();
if (o1Compatible && !o2Compatible) return -1;
if (!o1Compatible && o2Compatible) return 1;
return 0;
});
for (RealmsWorld world : worlds) {
final JPanel panel = new JPanel();
panel.setLayout(new GridBagLayout());
panel.setBorder(BorderFactory.createLineBorder(UIManager.getColor("Table.gridColor")));
String nameString = "";
if (!world.getOwnerName().isEmpty()) nameString += world.getOwnerName() + " - ";
String versionString = "";
if (!world.getActiveVersion().isEmpty()) versionString += " - " + world.getActiveVersion();
GBC.create(panel).grid(0, 0).weightx(1).insets(5, 5, 0, 5).fill(GridBagConstraints.HORIZONTAL).add(new JLabel(nameString + world.getName() + " (" + world.getState() + ")"));
GBC.create(panel).grid(1, 0).insets(5, 5, 0, 5).anchor(GridBagConstraints.LINE_END).add(new JLabel(world.getWorldType() + versionString));
GBC.create(panel).grid(0, 1).insets(5, 5, 0, 5).fill(GridBagConstraints.HORIZONTAL).add(new JLabel(world.getMotd()));
final JButton join = new JButton(I18n.get("tab.realms.join"));
if (world.isExpired()) {
join.setEnabled(false);
join.setToolTipText(I18n.get("tab.realms.expired"));
} else if (!world.isCompatible()) {
join.setEnabled(false);
join.setToolTipText(I18n.get("tab.realms.incompatible"));
}
GBC.create(panel).grid(1, 1).insets(5, 0, 5, 5).anchor(GridBagConstraints.LINE_END).add(join);
join.addActionListener(event -> {
join.setEnabled(false);
join.setText(I18n.get("tab.realms.joining"));
realmsService.joinWorld(world).thenAccept(address -> SwingUtilities.invokeLater(() -> {
join.setEnabled(true);
join.setText(I18n.get("tab.realms.join"));
this.setServerAddressAndStartViaProxy(address, realmsService instanceof JavaRealmsService ? this.currentSelectedJavaVersion : VersionEnum.bedrockLatest);
})).exceptionally(e -> {
final Throwable cause = e.getCause();
SwingUtilities.invokeLater(() -> {
join.setEnabled(true);
join.setText(I18n.get("tab.realms.join"));
if (realmsService instanceof JavaRealmsService javaRealmsService && cause instanceof RealmsResponseException realmsResponseException && realmsResponseException.getRealmsErrorCode() == RealmsResponseException.TOS_NOT_ACCEPTED) {
final int chosen = JOptionPane.showConfirmDialog(ViaProxy.getUI(), I18n.get("tab.realms.accept_tos", "https://aka.ms/MinecraftRealmsTerms"), "ViaProxy", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
if (chosen == JOptionPane.YES_OPTION) {
javaRealmsService.acceptTos();
join.doClick();
}
} else {
Logger.LOGGER.error("Failed to join realm", cause);
ViaProxy.getUI().showError(I18n.get("tab.realms.error_generic", cause.getMessage()));
}
});
return null;
});
});
parent.add(panel);
}
}
private void setServerAddressAndStartViaProxy(final String address, final VersionEnum version) {
final GeneralTab generalTab = ViaProxy.getUI().generalTab;
if (generalTab.stateButton.isEnabled()) {
if (!generalTab.stateButton.getText().equals(I18n.get("tab.general.state.start"))) {
generalTab.stateButton.doClick(); // Stop the running proxy
}
generalTab.serverAddress.setText(address);
generalTab.serverVersion.setSelectedItem(version);
generalTab.authMethod.setSelectedIndex(1);
generalTab.stateButton.doClick();
ViaProxy.getUI().contentPane.setSelectedIndex(0);
}
}
}

View File

@ -0,0 +1,84 @@
/*
* This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy
* Copyright (C) 2023 RK_01/RaphiMC and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.raphimc.viaproxy.util;
import java.awt.*;
public class PaddedVerticalLayout implements LayoutManager {
private final int gap;
private final int padding;
public PaddedVerticalLayout() {
this(0, 0);
}
public PaddedVerticalLayout(final int padding, final int gap) {
this.padding = padding;
this.gap = gap;
}
@Override
public void addLayoutComponent(String name, Component c) {
}
@Override
public void layoutContainer(Container parent) {
Insets insets = parent.getInsets();
Dimension size = parent.getSize();
int width = size.width - insets.left - insets.right;
int height = insets.top + this.padding;
for (int i = 0, c = parent.getComponentCount(); i < c; i++) {
Component m = parent.getComponent(i);
if (m.isVisible()) {
m.setBounds(insets.left + this.padding, height, width - this.padding * 2, m.getPreferredSize().height);
height += m.getSize().height + gap;
}
}
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return this.preferredLayoutSize(parent);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
Insets insets = parent.getInsets();
Dimension pref = new Dimension(0, 0);
for (int i = 0, c = parent.getComponentCount(); i < c; i++) {
Component m = parent.getComponent(i);
if (m.isVisible()) {
Dimension componentPreferredSize = parent.getComponent(i).getPreferredSize();
pref.height += componentPreferredSize.height + gap;
pref.width = Math.max(pref.width, componentPreferredSize.width);
}
}
pref.width += insets.left + insets.right;
pref.height += insets.top + insets.bottom;
return pref;
}
@Override
public void removeLayoutComponent(Component c) {
}
}

View File

@ -18,11 +18,11 @@ generic.could_not_open_url=Der Link konnte nicht geöffnet werden: %s
tab.general.name=Standard
tab.general.server_address.label=Server Adresse:
tab.general.server_address.tooltip=Unterstützte Formate:\n- Adresse\n- Adress:Port\n- ClassiCube Direct URL
tab.general.server_address.tooltip=Unterstützte Formate:\n- Adresse\n- Adresse:Port\n- ClassiCube Direct URL
tab.general.server_version.label=Server Version:
tab.general.minecraft_account.label=Minecraft Account:
tab.general.minecraft_account.option_no_account=Keinen Account verwenden
tab.general.minecraft_account.option_select_account=Ausgewählten Account verwenden
tab.general.minecraft_account.label=Minecraft Konto:
tab.general.minecraft_account.option_no_account=Kein Konto verwenden
tab.general.minecraft_account.option_select_account=Ausgewähltes Konto verwenden
tab.general.minecraft_account.option_openauthmod=OpenAuthMod verwenden
tab.general.betacraft_auth.label=BetaCraft Authentifizierung (Classic)
tab.general.betacraft_auth.tooltip=Wenn du die BetaCraft Authentifizierung aktivierst, kannst du auf Classic Server, die Online Mode aktiviert haben joinen.
@ -38,7 +38,7 @@ tab.general.error.no_server_version_selected=Bitte wähle eine Server Version au
tab.general.error.invalid_classicube_url=Ungültige ClassiCube Direct URL!
tab.general.error.invalid_server_address=Ungültige Server Adresse!
tab.general.error.invalid_proxy_url=Ungültige proxy URL!
tab.general.error.failed_to_start=ViaProxy konnte nicht starten! Sorg dafür, dass der lokale Port nicht bereits belegt ist und versuchs nochmal.
tab.general.error.failed_to_start=ViaProxy konnte nicht starten! Sorge dafür, dass der lokale Port nicht bereits belegt ist und versuche es nochmal.
tab.advanced.name=Erweitert
tab.advanced.bind_port.label=Lokaler Port:
@ -61,7 +61,7 @@ tab.advanced.upload_latest_log.error_generic=Die Log-Datei konnte nicht hochgela
tab.advanced.upload_latest_log.error_not_found=Die Log-Datei konnte nicht gefunden werden.
tab.accounts.name=Konten
tab.accounts.description.line1=Um auf Online Mode Server zu joinen musst du ein Minecraft Konto im ViaProxy hinzufügen. Du kannst ein Konto auswählen indem du es rechtsklickst. Standardmäßig wird das Erste verwendet.
tab.accounts.description.line1=Um auf Online Mode Server zu joinen musst du ein Minecraft Konto im ViaProxy hinzufügen. Du kannst ein Konto auswählen indem du es rechtsklickst. Standardmäßig wird das erste verwendet.
tab.accounts.description.line2=Wenn du dein Konto oft änderst, kannst du stattdessen OpenAuthMod auf deinem Client installieren. Das erlaubt ViaProxy das aktuell eingeloggte Konto von deinem Client zu verwenden.
tab.accounts.list.context_menu.select=Konto auswählen
tab.accounts.list.context_menu.remove=Löschen
@ -75,6 +75,24 @@ tab.accounts.add_bedrock.label=Bedrock Konto
tab.accounts.add.success=Das Konto %s wurde erfolgreich hinzugefügt.
tab.accounts.add.timeout=Der Login Prozess ist abgelaufen.\nBitte logge dich innerhalb von %s Sekunden ein.
tabs.realms.name=Realms
tab.realms.error_generic=Realms Anfrage konnte nicht verarbeitet werden: %s
tab.realms.error_label=Ein unbekannter Fehler ist aufgetreten
tab.realms.no_account=Kein Konto ausgewählt
tab.realms.unsupported_account=Das ausgewählte Konto wird nicht unterstützt
tab.realms.availability_check=Prüfe Verfügbarkeit...
tab.realms.unavailable=Deine ViaProxy ist veraltet und nicht kompatibel mit Minecraft Realms.\nBitte nutze die neuste ViaProxy Version.
tab.realms.loading_worlds=Lade Welten...
tab.realms.no_worlds=Keine Welten gefunden
tab.realms.account=Konto: %s
tab.realms.release=Vollversion
tab.realms.snapshot=Entwicklungsversion
tab.realms.join=Beitreten
tab.realms.joining=Trete bei...
tab.realms.accept_tos=Um Minecraft Realms zu verwenden musst du die Nutzungsbedingungen akzeptieren: %s
tab.realms.expired=Dieser Realm ist abgelaufen
tab.realms.incompatible=Die Minecraft Version dieses Realms ist zu alt oder zu neu
tab.ui_settings.name=Benutzeroberflächeneinstellungen
tab.ui_settings.language.label=Sprache:
tab.ui_settings.language.completion=%s übersetzt

View File

@ -75,6 +75,24 @@ tab.accounts.add_bedrock.label=Bedrock Account
tab.accounts.add.success=The account %s was added successfully.
tab.accounts.add.timeout=The login request timed out.\nPlease login within %s seconds.
tab.realms.name=Realms
tab.realms.error_generic=Realms request could not be handled: %s
tab.realms.error_label=An unknown error occurred
tab.realms.no_account=No account selected
tab.realms.unsupported_account=Unsupported account selected
tab.realms.availability_check=Checking availability...
tab.realms.unavailable=Your ViaProxy is outdated and not compatible with Minecraft Realms.\nPlease use the latest version of ViaProxy.
tab.realms.loading_worlds=Loading worlds...
tab.realms.no_worlds=No worlds found
tab.realms.account=Account: %s
tab.realms.release=Release
tab.realms.snapshot=Snapshot
tab.realms.join=Join
tab.realms.joining=Joining...
tab.realms.accept_tos=To use Minecraft Realms you have to accept the Terms of Service: %s
tab.realms.expired=This Realm is expired
tab.realms.incompatible=This Realms Minecraft version is too old or too new
tab.ui_settings.name=UI Settings
tab.ui_settings.language.label=Language:
tab.ui_settings.language.completion=%s translated