mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-17 15:57:18 -04:00
feat: modify UUID of offline accounts. Closes #1003.
This commit is contained in:
parent
108df2293b
commit
8c3904cdcc
@ -474,4 +474,16 @@ public final class SVG {
|
||||
"M9.5,3A6.5,6.5 0 0,1 16,9.5C16,11.11 15.41,12.59 14.44,13.73L14.71,14H15.5L20.5,19L19,20.5L14,15.5V14.71L13.73,14.44C12.59,15.41 11.11,16 9.5,16A6.5,6.5 0 0,1 3,9.5A6.5,6.5 0 0,1 9.5,3M9.5,5C7,5 5,7 5,9.5C5,12 7,14 9.5,14C12,14 14,12 14,9.5C14,7 12,5 9.5,5Z",
|
||||
fill, width, height);
|
||||
}
|
||||
|
||||
public static Node menuUp(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath(
|
||||
"M7,15L12,10L17,15H7Z",
|
||||
fill, width, height);
|
||||
}
|
||||
|
||||
public static Node menuDown(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath(
|
||||
"M7,10L12,15L17,10H7Z",
|
||||
fill, width, height);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,9 @@
|
||||
package org.jackhuang.hmcl.ui.account;
|
||||
|
||||
import com.jfoenix.controls.*;
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.NamedArg;
|
||||
import javafx.beans.binding.BooleanBinding;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
@ -26,6 +28,7 @@ import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextInputControl;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.*;
|
||||
import org.jackhuang.hmcl.auth.AccountFactory;
|
||||
@ -33,6 +36,7 @@ import org.jackhuang.hmcl.auth.CharacterSelector;
|
||||
import org.jackhuang.hmcl.auth.NoSelectedCharacterException;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||
import org.jackhuang.hmcl.auth.offline.OfflineAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccountFactory;
|
||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||
@ -46,6 +50,9 @@ import org.jackhuang.hmcl.ui.Controllers;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -187,7 +194,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
AccountDetailsInputPane details = (AccountDetailsInputPane) detailsPane;
|
||||
username = details.getUsername();
|
||||
password = details.getPassword();
|
||||
additionalData = details.getAuthServer();
|
||||
additionalData = details.getAdditionalData();
|
||||
} else {
|
||||
username = null;
|
||||
password = null;
|
||||
@ -276,6 +283,7 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
private @Nullable JFXComboBox<AuthlibInjectorServer> cboServers;
|
||||
private @Nullable JFXTextField txtUsername;
|
||||
private @Nullable JFXPasswordField txtPassword;
|
||||
private @Nullable JFXTextField txtUUID;
|
||||
private BooleanBinding valid;
|
||||
|
||||
public AccountDetailsInputPane(AccountFactory<?> factory, Runnable onAction) {
|
||||
@ -369,6 +377,42 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
if (factory instanceof OfflineAccountFactory) {
|
||||
HBox box = new HBox();
|
||||
MenuUpDownButton advancedButton = new MenuUpDownButton();
|
||||
box.getChildren().setAll(advancedButton);
|
||||
advancedButton.setText(i18n("settings.advanced"));
|
||||
GridPane.setColumnSpan(box, 2);
|
||||
add(box, 0, rowIndex);
|
||||
|
||||
rowIndex++;
|
||||
|
||||
Label lblUUID = new Label(i18n("account.methods.offline.uuid"));
|
||||
lblUUID.managedProperty().bind(advancedButton.selectedProperty());
|
||||
lblUUID.visibleProperty().bind(advancedButton.selectedProperty());
|
||||
setHalignment(lblUUID, HPos.LEFT);
|
||||
add(lblUUID, 0, rowIndex);
|
||||
|
||||
txtUUID = new JFXTextField();
|
||||
txtUUID.managedProperty().bind(advancedButton.selectedProperty());
|
||||
txtUUID.visibleProperty().bind(advancedButton.selectedProperty());
|
||||
txtUUID.setValidators(new UUIDValidator());
|
||||
txtUUID.promptTextProperty().bind(BindingMapping.of(txtUsername.textProperty()).map(name -> OfflineAccountFactory.getUUIDFromUserName(name).toString()));
|
||||
txtUUID.setOnAction(e -> onAction.run());
|
||||
add(txtUUID, 1, rowIndex);
|
||||
|
||||
rowIndex++;
|
||||
|
||||
HintPane hintPane = new HintPane(MessageDialogPane.MessageType.WARNING);
|
||||
hintPane.managedProperty().bind(advancedButton.selectedProperty());
|
||||
hintPane.visibleProperty().bind(advancedButton.selectedProperty());
|
||||
hintPane.setText(i18n("account.methods.offline.uuid.hint"));
|
||||
GridPane.setColumnSpan(hintPane, 2);
|
||||
add(hintPane, 0, rowIndex);
|
||||
|
||||
rowIndex++;
|
||||
}
|
||||
|
||||
valid = new BooleanBinding() {
|
||||
{
|
||||
if (cboServers != null)
|
||||
@ -377,6 +421,8 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
bind(txtUsername.textProperty());
|
||||
if (txtPassword != null)
|
||||
bind(txtPassword.textProperty());
|
||||
if (txtUUID != null)
|
||||
bind(txtUUID.textProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -387,6 +433,8 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
return false;
|
||||
if (txtPassword != null && !txtPassword.validate())
|
||||
return false;
|
||||
if (txtUUID != null && !txtUUID.validate())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -404,6 +452,16 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Object getAdditionalData() {
|
||||
if (factory instanceof AuthlibInjectorAccountFactory) {
|
||||
return getAuthServer();
|
||||
} else if (factory instanceof OfflineAccountFactory) {
|
||||
return txtUUID == null ? null : StringUtils.isBlank(txtUUID.getText()) ? null : UUIDTypeAdapter.fromString(txtUUID.getText());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable AuthlibInjectorServer getAuthServer() {
|
||||
return cboServers == null ? null : cboServers.getValue();
|
||||
}
|
||||
@ -498,4 +556,37 @@ public class CreateAccountPane extends JFXDialogLayout implements DialogAware {
|
||||
((AccountDetailsInputPane) detailsPane).focus();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UUIDValidator extends ValidatorBase {
|
||||
|
||||
public UUIDValidator() {
|
||||
this(i18n("account.methods.offline.uuid.malformed"));
|
||||
}
|
||||
|
||||
public UUIDValidator(@NamedArg("message") String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void eval() {
|
||||
if (srcControl.get() instanceof TextInputControl) {
|
||||
evalTextInputField();
|
||||
}
|
||||
}
|
||||
|
||||
private void evalTextInputField() {
|
||||
TextInputControl textField = ((TextInputControl) srcControl.get());
|
||||
if (StringUtils.isBlank(textField.getText())) {
|
||||
hasErrors.set(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
UUIDTypeAdapter.fromString(textField.getText());
|
||||
hasErrors.set(false);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
hasErrors.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.layout.HBox;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
|
||||
|
||||
public class MenuUpDownButton extends Control {
|
||||
|
||||
private final BooleanProperty selected = new SimpleBooleanProperty(this, "selected");
|
||||
private final StringProperty text = new SimpleStringProperty(this, "text");
|
||||
|
||||
public MenuUpDownButton() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Skin<?> createDefaultSkin() {
|
||||
return new MenuUpDownButtonSkin(this);
|
||||
}
|
||||
|
||||
public boolean isSelected() {
|
||||
return selected.get();
|
||||
}
|
||||
|
||||
public BooleanProperty selectedProperty() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected.set(selected);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text.get();
|
||||
}
|
||||
|
||||
public StringProperty textProperty() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text.set(text);
|
||||
}
|
||||
|
||||
private static class MenuUpDownButtonSkin extends SkinBase<MenuUpDownButton> {
|
||||
|
||||
protected MenuUpDownButtonSkin(MenuUpDownButton control) {
|
||||
super(control);
|
||||
|
||||
HBox content = new HBox(8);
|
||||
content.setAlignment(Pos.CENTER);
|
||||
Label label = new Label();
|
||||
label.setStyle("-fx-text-fill: black;");
|
||||
label.textProperty().bind(control.text);
|
||||
|
||||
Node up = SVG.menuUp(Theme.blackFillBinding(), 16, 16);
|
||||
Node down = SVG.menuDown(Theme.blackFillBinding(), 16, 16);
|
||||
|
||||
JFXButton button = new JFXButton();
|
||||
button.setGraphic(content);
|
||||
button.setOnAction(e -> {
|
||||
control.selected.set(!control.isSelected());
|
||||
});
|
||||
|
||||
FXUtils.onChangeAndOperate(control.selected, selected -> {
|
||||
if (selected) {
|
||||
content.getChildren().setAll(label, up);
|
||||
} else {
|
||||
content.getChildren().setAll(label, down);
|
||||
}
|
||||
});
|
||||
|
||||
getChildren().setAll(button);
|
||||
}
|
||||
}
|
||||
}
|
@ -84,6 +84,9 @@ account.methods.microsoft.logging_in=Logging in...
|
||||
account.methods.microsoft.manual=You should finish authorization in the newly opened browser window. If the browser window failed to show, you can click here to copy the URL, and manually open it in your browser.
|
||||
account.methods.microsoft.waiting_browser=Waiting for authorization in opened browser window...
|
||||
account.methods.offline=Offline
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID is the unique identifier to the game character in Minecraft. The way that UUID is generated varies from different game launchers. Changing UUID to the one generated by other launcher would promise that game blocks/items in the backpack of your offline account remain. This option is for expert. Unless you know what you are doing, we do not suggest you to change this option.
|
||||
account.methods.offline.uuid.malformed=Malformed
|
||||
account.methods.yggdrasil=Mojang
|
||||
account.missing=No Account
|
||||
account.missing.add=Click here to add
|
||||
|
@ -78,6 +78,9 @@ account.methods.microsoft.hint=點擊確定以登錄
|
||||
account.methods.microsoft.manual=您需要在新打開的瀏覽器窗口中完成登錄。若頁面未能打開,您可以點擊此處複製連結,並手動在瀏覽器中打開網頁。
|
||||
account.methods.microsoft.waiting_browser=等待在新打開的瀏覽器窗口中完成登錄...
|
||||
account.methods.offline=離線模式
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 對玩家角色的唯一標識符,每個啟動器生成 UUID 的方式可能不同。通過修改 UUID 選項至原啟動器所生成的 UUID,你可以保證在切換啟動器後,遊戲還能將你的遊戲角色識別為給定 UUID 所對應的角色,從而保留原來角色的背包物品。UUID 選項為高級選項,除非你知道你在做什麼,否則你不需要調整該選項。
|
||||
account.methods.offline.uuid.malformed=格式錯誤
|
||||
account.methods.yggdrasil=正版登入
|
||||
account.missing=沒有遊戲帳戶
|
||||
account.missing.add=按一下此處加入帳戶
|
||||
|
@ -84,6 +84,9 @@ account.methods.microsoft.hint=点击确定以登录
|
||||
account.methods.microsoft.manual=您需要在新打开的浏览器窗口中完成登录。若页面未能打开,您可以点击此处复制链接,并手动在浏览器中打开网页。
|
||||
account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录...
|
||||
account.methods.offline=离线模式
|
||||
account.methods.offline.uuid=UUID
|
||||
account.methods.offline.uuid.hint=UUID 是 Minecraft 对玩家角色的唯一标识符,每个启动器生成 UUID 的方式可能不同。通过修改 UUID 选项至原启动器所生成的 UUID,你可以保证在切换启动器后,游戏还能将你的游戏角色识别为给定 UUID 所对应的角色,从而保留原来角色的背包物品。UUID 选项为高级选项,除非你知道你在做什么,否则你不需要调整该选项。
|
||||
account.methods.offline.uuid.malformed=格式错误
|
||||
account.methods.yggdrasil=Mojang 账号
|
||||
account.missing=没有游戏账户
|
||||
account.missing.add=点击此处添加账户
|
||||
|
@ -48,7 +48,13 @@ public final class OfflineAccountFactory extends AccountFactory<OfflineAccount>
|
||||
|
||||
@Override
|
||||
public OfflineAccount create(CharacterSelector selector, String username, String password, ProgressCallback progressCallback, Object additionalData) {
|
||||
return new OfflineAccount(username, getUUIDFromUserName(username));
|
||||
UUID uuid;
|
||||
if (additionalData != null) {
|
||||
uuid = (UUID) additionalData;
|
||||
} else {
|
||||
uuid = getUUIDFromUserName(username);
|
||||
}
|
||||
return new OfflineAccount(username, uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -62,7 +68,7 @@ public final class OfflineAccountFactory extends AccountFactory<OfflineAccount>
|
||||
return new OfflineAccount(username, uuid);
|
||||
}
|
||||
|
||||
private static UUID getUUIDFromUserName(String username) {
|
||||
public static UUID getUUIDFromUserName(String username) {
|
||||
return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(UTF_8));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user