diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AccountsScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AccountsScreen.java index 47756608c7..d620cacf63 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AccountsScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AccountsScreen.java @@ -12,6 +12,7 @@ import meteordevelopment.meteorclient.gui.widgets.containers.WHorizontalList; import meteordevelopment.meteorclient.gui.widgets.pressable.WButton; import meteordevelopment.meteorclient.systems.accounts.Account; +import meteordevelopment.meteorclient.systems.accounts.AccountType; import meteordevelopment.meteorclient.systems.accounts.Accounts; import meteordevelopment.meteorclient.utils.misc.NbtUtils; import meteordevelopment.meteorclient.utils.network.MeteorExecutor; @@ -50,23 +51,28 @@ public static void addAccount(@Nullable AddAccountScreen screen, AccountsScreen if (screen != null) screen.locked = true; MeteorExecutor.execute(() -> { - if (account.fetchInfo()) { - account.getCache().loadHead(); + if (!account.fetchInfo()) { + mc.execute(() -> { + if (screen != null) screen.locked = false; + }); + return; + } - Accounts.get().add(account); - if (account.login()) Accounts.get().save(); + Accounts.get().add(account); + + if (account.login()) { + if (account.getType() != AccountType.Cracked) account.getCache().loadHead(parent::reload); + Accounts.get().save(); + } + mc.execute(() -> { if (screen != null) { screen.locked = false; screen.close(); } parent.reload(); - - return; - } - - if (screen != null) screen.locked = false; + }); }); } diff --git a/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AddCrackedAccountScreen.java b/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AddCrackedAccountScreen.java index 856b231961..11ebcf3b50 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AddCrackedAccountScreen.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/screens/accounts/AddCrackedAccountScreen.java @@ -11,7 +11,11 @@ import meteordevelopment.meteorclient.systems.accounts.Accounts; import meteordevelopment.meteorclient.systems.accounts.types.CrackedAccount; +import java.util.regex.Pattern; + public class AddCrackedAccountScreen extends AddAccountScreen { + private static final Pattern USERNAME_REGEX = Pattern.compile("^[A-Za-z0-9_]{3,16}$"); + public AddCrackedAccountScreen(GuiTheme theme, AccountsScreen parent) { super(theme, "Add Cracked Account", parent); } @@ -23,8 +27,7 @@ public void initWidgets() { // Name t.add(theme.label("Name: ")); WTextBox name = t.add(theme.textBox("", "seasnail8169", (text, c) -> - // Username can't contain spaces - c != ' ' + Character.isLetterOrDigit(c) || c == '_' )).minWidth(400).expandX().widget(); name.setFocused(true); t.row(); @@ -32,11 +35,12 @@ public void initWidgets() { // Add add = t.add(theme.button("Add")).expandX().widget(); add.action = () -> { - if (!name.get().isEmpty() && name.get().length() < 17) { - CrackedAccount account = new CrackedAccount(name.get()); - if (!(Accounts.get().exists(account))) { - AccountsScreen.addAccount(this, parent, account); - } + String username = name.get().trim(); + if (!USERNAME_REGEX.matcher(username).matches()) return; + + CrackedAccount account = new CrackedAccount(username); + if (!(Accounts.get().exists(account))) { + AccountsScreen.addAccount(this, parent, account); } }; diff --git a/src/main/java/meteordevelopment/meteorclient/gui/tabs/builtin/FriendsTab.java b/src/main/java/meteordevelopment/meteorclient/gui/tabs/builtin/FriendsTab.java index aeaec4e1ed..600060ff3e 100644 --- a/src/main/java/meteordevelopment/meteorclient/gui/tabs/builtin/FriendsTab.java +++ b/src/main/java/meteordevelopment/meteorclient/gui/tabs/builtin/FriendsTab.java @@ -62,11 +62,15 @@ public void initWidgets() { if (Friends.get().add(friend)) { nameW.set(""); - reload(); + initTable(table); + nameW.setFocused(true); MeteorExecutor.execute(() -> { friend.updateInfo(); - mc.execute(this::reload); + mc.execute(() -> { + initTable(table); + nameW.setFocused(true); + }); }); } }; @@ -93,7 +97,7 @@ private void initTable(WTable table) { WMinus remove = table.add(theme.minus()).expandCellX().right().widget(); remove.action = () -> { Friends.get().remove(friend); - reload(); + initTable(table); }; table.row(); diff --git a/src/main/java/meteordevelopment/meteorclient/systems/accounts/AccountCache.java b/src/main/java/meteordevelopment/meteorclient/systems/accounts/AccountCache.java index 6aab00aa94..66deaecd0f 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/accounts/AccountCache.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/accounts/AccountCache.java @@ -6,6 +6,7 @@ package meteordevelopment.meteorclient.systems.accounts; import com.mojang.util.UndashedUuid; +import meteordevelopment.meteorclient.utils.network.MeteorExecutor; import meteordevelopment.meteorclient.utils.misc.ISerializable; import meteordevelopment.meteorclient.utils.misc.NbtException; import meteordevelopment.meteorclient.utils.render.PlayerHeadTexture; @@ -18,14 +19,35 @@ public class AccountCache implements ISerializable { public String username = ""; public String uuid = ""; private PlayerHeadTexture headTexture; + private volatile boolean loadingHead; public PlayerHeadTexture getHeadTexture() { return headTexture != null ? headTexture : PlayerHeadUtils.STEVE_HEAD; } public void loadHead() { - if (uuid == null || uuid.isBlank()) return; - mc.execute(() -> headTexture = PlayerHeadUtils.fetchHead(UndashedUuid.fromStringLenient(uuid))); + loadHead(null); + } + + public void loadHead(Runnable callback) { + if (headTexture != null || uuid == null || uuid.isBlank()) { + if (callback != null) mc.execute(callback); + return; + } + + if (loadingHead) return; + + loadingHead = true; + + MeteorExecutor.execute(() -> { + byte[] head = PlayerHeadUtils.fetchHead(UndashedUuid.fromStringLenient(uuid)); + + mc.execute(() -> { + if (head != null) headTexture = new PlayerHeadTexture(head, true); + loadingHead = false; + if (callback != null) callback.run(); + }); + }); } @Override diff --git a/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/CrackedAccount.java b/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/CrackedAccount.java index bfb907f3be..cb810bb8f4 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/CrackedAccount.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/CrackedAccount.java @@ -27,7 +27,6 @@ public boolean fetchInfo() { public boolean login() { super.login(); - cache.loadHead(); setSession(new Session(name, Uuids.getOfflinePlayerUuid(name), "", Optional.empty(), Optional.empty())); return true; } diff --git a/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/MicrosoftAccount.java b/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/MicrosoftAccount.java index ac73b22f5a..4f6a308a43 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/MicrosoftAccount.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/MicrosoftAccount.java @@ -31,7 +31,6 @@ public boolean login() { if (token == null) return false; super.login(); - cache.loadHead(); setSession(new Session(cache.username, UndashedUuid.fromStringLenient(cache.uuid), token, Optional.empty(), Optional.empty())); return true; diff --git a/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/SessionAccount.java b/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/SessionAccount.java index 7d2a81f6c4..e5917c7ce6 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/SessionAccount.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/accounts/types/SessionAccount.java @@ -66,7 +66,6 @@ public boolean login() { if (accessToken == null || accessToken.isBlank()) return false; super.login(); - cache.loadHead(); setSession(new Session(cache.username, UndashedUuid.fromStringLenient(cache.uuid), accessToken, Optional.empty(), Optional.empty())); return true; diff --git a/src/main/java/meteordevelopment/meteorclient/systems/friends/Friend.java b/src/main/java/meteordevelopment/meteorclient/systems/friends/Friend.java index 125479962d..12dea12812 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/friends/Friend.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/friends/Friend.java @@ -70,7 +70,11 @@ public void updateInfo() { if (res != null && res.statusCode() == 200) { name = res.body().name; id = UndashedUuid.fromStringLenient(res.body().id); - mc.execute(() -> headTexture = PlayerHeadUtils.fetchHead(id)); + + byte[] head = PlayerHeadUtils.fetchHead(id); + mc.execute(() -> { + if (head != null) headTexture = new PlayerHeadTexture(head, true); + }); } // cracked accounts shouldn't be assigned ids diff --git a/src/main/java/meteordevelopment/meteorclient/systems/friends/Friends.java b/src/main/java/meteordevelopment/meteorclient/systems/friends/Friends.java index 271d6ae92e..36f363ab70 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/friends/Friends.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/friends/Friends.java @@ -34,15 +34,12 @@ public static Friends get() { public boolean add(Friend friend) { if (friend.name.isEmpty() || friend.name.contains(" ")) return false; + if (get(friend.name) != null) return false; - if (!friends.contains(friend)) { - friends.add(friend); - save(); - - return true; - } + friends.add(friend); + save(); - return false; + return true; } public boolean remove(Friend friend) { diff --git a/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadTexture.java b/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadTexture.java index 31c6c6b154..71a3d66756 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadTexture.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadTexture.java @@ -23,17 +23,48 @@ public class PlayerHeadTexture extends Texture { private boolean needsRotate; - public PlayerHeadTexture(String url) { + public PlayerHeadTexture(byte[] head, boolean needsRotate) { super(8, 8, TextureFormat.RGBA8, FilterMode.NEAREST, FilterMode.NEAREST); - BufferedImage skin; - try { - skin = ImageIO.read(Http.get(url).sendInputStream()); - } catch (IOException e) { + upload(BufferUtils.createByteBuffer(head.length).put(head)); + this.needsRotate = needsRotate; + } + + public PlayerHeadTexture() { + super(8, 8, TextureFormat.RGBA8, FilterMode.NEAREST, FilterMode.NEAREST); + + try (InputStream inputStream = mc.getResourceManager().getResource(MeteorClient.identifier("textures/steve.png")).get().getInputStream()) { + ByteBuffer data = TextureUtil.readResource(inputStream); + data.rewind(); + + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer width = stack.mallocInt(1); + IntBuffer height = stack.mallocInt(1); + IntBuffer comp = stack.mallocInt(1); + + ByteBuffer image = STBImage.stbi_load_from_memory(data, width, height, comp, 4); + upload(image); + STBImage.stbi_image_free(image); + } + MemoryUtil.memFree(data); + } + catch (IOException e) { e.printStackTrace(); - return; + } + } + + public boolean needsRotate() { + return needsRotate; + } + + public static byte[] downloadHead(String url) throws IOException { + BufferedImage skin; + try (InputStream in = Http.get(url).sendInputStream()) { + skin = ImageIO.read(in); } + if (skin == null) throw new IOException("Failed to decode skin image."); + byte[] head = new byte[8 * 8 * 4]; int[] pixel = new int[4]; @@ -43,8 +74,7 @@ public PlayerHeadTexture(String url) { skin.getData().getPixel(x, y, pixel); for (int j = 0; j < 4; j++) { - head[i] = (byte) pixel[j]; - i++; + head[i++] = (byte) pixel[j]; } } } @@ -56,43 +86,13 @@ public PlayerHeadTexture(String url) { if (pixel[3] != 0) { for (int j = 0; j < 4; j++) { - head[i] = (byte) pixel[j]; - i++; + head[i++] = (byte) pixel[j]; } } else i += 4; } } - upload(BufferUtils.createByteBuffer(head.length).put(head)); - - needsRotate = true; - } - - public PlayerHeadTexture() { - super(8, 8, TextureFormat.RGBA8, FilterMode.NEAREST, FilterMode.NEAREST); - - try (InputStream inputStream = mc.getResourceManager().getResource(MeteorClient.identifier("textures/steve.png")).get().getInputStream()) { - ByteBuffer data = TextureUtil.readResource(inputStream); - data.rewind(); - - try (MemoryStack stack = MemoryStack.stackPush()) { - IntBuffer width = stack.mallocInt(1); - IntBuffer height = stack.mallocInt(1); - IntBuffer comp = stack.mallocInt(1); - - ByteBuffer image = STBImage.stbi_load_from_memory(data, width, height, comp, 4); - upload(image); - STBImage.stbi_image_free(image); - } - MemoryUtil.memFree(data); - } - catch (IOException e) { - e.printStackTrace(); - } - } - - public boolean needsRotate() { - return needsRotate; + return head; } } diff --git a/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadUtils.java b/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadUtils.java index 828fb2c48e..59140867d3 100644 --- a/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadUtils.java +++ b/src/main/java/meteordevelopment/meteorclient/utils/render/PlayerHeadUtils.java @@ -21,11 +21,18 @@ public static void init() { STEVE_HEAD = new PlayerHeadTexture(); } - public static PlayerHeadTexture fetchHead(UUID id) { + public static byte[] fetchHead(UUID id) { if (id == null) return null; String url = getSkinUrl(id); - return url != null ? new PlayerHeadTexture(url) : null; + if (url == null) return null; + + try { + return PlayerHeadTexture.downloadHead(url); + } catch (java.io.IOException e) { + MeteorClient.LOG.error("Could not fetch player head for {}.", id, e); + return null; + } } public static String getSkinUrl(UUID id) {