Skip to content

Commit 074326e

Browse files
committed
Updated AltManager
1 parent dcbac98 commit 074326e

File tree

10 files changed

+1258
-70
lines changed

10 files changed

+1258
-70
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,8 @@ Examples:
11461146
### Alt Manager Improved
11471147
- Can now multi-select and delete alt accounts
11481148
- Moved to multiplayer screen
1149+
- Can now accept alternative login methods
1150+
- Updated import and export to suit new method
11491151

11501152
### FreeCam Improved
11511153
- Optionally tie horizontal and vertical speeds together

src/main/java/net/wurstclient/altmanager/AltManager.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
import java.util.Collection;
1313
import java.util.Collections;
1414
import java.util.Comparator;
15+
import java.util.LinkedHashMap;
1516
import java.util.List;
17+
import java.util.Locale;
1618
import java.util.stream.Collectors;
1719

1820
public final class AltManager
@@ -138,6 +140,59 @@ else if(alt.isCheckedPremium())
138140
altsFile.save(this);
139141
}
140142

143+
public boolean dedupeByUsernamePreferRefreshToken()
144+
{
145+
LinkedHashMap<String, Alt> bestByName = new LinkedHashMap<>();
146+
ArrayList<Alt> removeList = new ArrayList<>();
147+
148+
for(Alt alt : alts)
149+
{
150+
if(alt.isCracked() || alt.getName().isEmpty())
151+
continue;
152+
153+
String key = alt.getName().toLowerCase(Locale.ROOT);
154+
Alt existing = bestByName.get(key);
155+
156+
if(existing == null)
157+
{
158+
bestByName.put(key, alt);
159+
continue;
160+
}
161+
162+
if(duplicatePriority(alt) > duplicatePriority(existing))
163+
{
164+
bestByName.put(key, alt);
165+
removeList.add(existing);
166+
}else
167+
removeList.add(alt);
168+
}
169+
170+
if(removeList.isEmpty())
171+
return false;
172+
173+
alts.removeAll(removeList);
174+
sortAlts();
175+
altsFile.save(this);
176+
return true;
177+
}
178+
179+
private int duplicatePriority(Alt alt)
180+
{
181+
int score = 0;
182+
183+
if(alt instanceof TokenAlt
184+
&& !((TokenAlt)alt).getRefreshToken().isEmpty())
185+
score += 100;
186+
187+
if(alt.isFavorite())
188+
score += 10;
189+
190+
if(alt.isCheckedPremium())
191+
score += 1;
192+
193+
return score;
194+
}
195+
141196
private void sortAlts()
142197
{
143198
Comparator<Alt> c = Comparator.comparing(a -> !a.isFavorite());

src/main/java/net/wurstclient/altmanager/AltRenderer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public final class AltRenderer
4242
private static Identifier getSkinTexture(String name)
4343
{
4444
if(name.isEmpty())
45-
name = "Steve";
45+
name = "Astrollyn";
4646

4747
Identifier offlineSkin = offlineSkins.get(name);
4848
if(offlineSkin == null)

src/main/java/net/wurstclient/altmanager/AltsFile.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,24 @@ public static ArrayList<Alt> parseJson(WsonObject wson)
121121

122122
private static Alt loadAlt(String nameOrEmail, JsonObject jsonAlt)
123123
{
124-
String password = JsonUtils.getAsString(jsonAlt.get("password"), "");
124+
String type = JsonUtils.getAsString(jsonAlt.get("type"), "");
125125
boolean starred = JsonUtils.getAsBoolean(jsonAlt.get("starred"), false);
126126

127+
if("token".equalsIgnoreCase(type))
128+
{
129+
String token = JsonUtils.getAsString(jsonAlt.get("token"), "");
130+
String refreshToken =
131+
JsonUtils.getAsString(jsonAlt.get("refresh_token"), "");
132+
String name = JsonUtils.getAsString(jsonAlt.get("name"), "");
133+
134+
if(!token.isEmpty() || !refreshToken.isEmpty())
135+
return new TokenAlt(token, refreshToken, name, starred);
136+
137+
return new CrackedAlt(nameOrEmail, starred);
138+
}
139+
140+
String password = JsonUtils.getAsString(jsonAlt.get("password"), "");
141+
127142
if(password.isEmpty())
128143
return new CrackedAlt(nameOrEmail, starred);
129144

src/main/java/net/wurstclient/altmanager/MicrosoftLoginManager.java

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,74 @@ public static void login(String email, String password)
100100
throws LoginException
101101
{
102102
MinecraftProfile mcProfile = getAccount(email, password);
103+
setSession(mcProfile);
104+
}
105+
106+
public static void loginWithToken(String token) throws LoginException
107+
{
108+
if(token == null || token.isBlank())
109+
throw new LoginException("Token cannot be empty.");
103110

104-
User session = new User(mcProfile.getName(), mcProfile.getUUID(),
105-
mcProfile.getAccessToken(), Optional.empty(), Optional.empty());
111+
String trimmedToken = token.trim();
112+
System.out.println("Logging in with token...");
113+
long startTime = System.nanoTime();
106114

107-
WurstClient.IMC.setWurstSession(session);
115+
try
116+
{
117+
try
118+
{
119+
MinecraftProfile mcProfile = getMinecraftProfile(trimmedToken);
120+
setSession(mcProfile);
121+
System.out.println("Token login successful after "
122+
+ (System.nanoTime() - startTime) / 1e6D + " ms");
123+
return;
124+
125+
}catch(LoginException ignored)
126+
{
127+
// Token may be a Microsoft token instead of a Minecraft token.
128+
}
129+
130+
MinecraftProfile mcProfile =
131+
getAccountFromMicrosoftAccessToken(trimmedToken);
132+
setSession(mcProfile);
133+
System.out.println("Token login successful after "
134+
+ (System.nanoTime() - startTime) / 1e6D + " ms");
135+
136+
}catch(LoginException e)
137+
{
138+
System.out.println("Token login failed after "
139+
+ (System.nanoTime() - startTime) / 1e6D + " ms");
140+
throw e;
141+
}
142+
}
143+
144+
public static void loginWithRefreshToken(String refreshToken)
145+
throws LoginException
146+
{
147+
if(refreshToken == null || refreshToken.isBlank())
148+
throw new LoginException("Refresh token cannot be empty.");
149+
150+
System.out.println("Logging in with refresh token...");
151+
long startTime = System.nanoTime();
152+
153+
try
154+
{
155+
String msftAccessToken =
156+
getMicrosoftAccessTokenFromRefreshToken(refreshToken.trim());
157+
158+
MinecraftProfile mcProfile =
159+
getAccountFromMicrosoftAccessToken(msftAccessToken);
160+
setSession(mcProfile);
161+
162+
System.out.println("Refresh-token login successful after "
163+
+ (System.nanoTime() - startTime) / 1e6D + " ms");
164+
165+
}catch(LoginException e)
166+
{
167+
System.out.println("Refresh-token login failed after "
168+
+ (System.nanoTime() - startTime) / 1e6D + " ms");
169+
throw e;
170+
}
108171
}
109172

110173
private static MinecraftProfile getAccount(String email, String password)
@@ -117,14 +180,8 @@ private static MinecraftProfile getAccount(String email, String password)
117180
{
118181
String authCode = getAuthorizationCode(email, password);
119182
String msftAccessToken = getMicrosoftAccessToken(authCode);
120-
121-
XBoxLiveToken xblToken = getXBLToken(msftAccessToken);
122-
String xstsToken = getXSTSToken(xblToken.getToken());
123-
124-
String mcAccessToken =
125-
getMinecraftAccessToken(xblToken.getUHS(), xstsToken);
126-
127-
MinecraftProfile mcProfile = getMinecraftProfile(mcAccessToken);
183+
MinecraftProfile mcProfile =
184+
getAccountFromMicrosoftAccessToken(msftAccessToken);
128185

129186
System.out.println("Login successful after "
130187
+ (System.nanoTime() - startTime) / 1e6D + " ms");
@@ -141,6 +198,18 @@ private static MinecraftProfile getAccount(String email, String password)
141198
}
142199
}
143200

201+
private static MinecraftProfile getAccountFromMicrosoftAccessToken(
202+
String msftAccessToken) throws LoginException
203+
{
204+
XBoxLiveToken xblToken = getXBLToken(msftAccessToken);
205+
String xstsToken = getXSTSToken(xblToken.getToken());
206+
207+
String mcAccessToken =
208+
getMinecraftAccessToken(xblToken.getUHS(), xstsToken);
209+
210+
return getMinecraftProfile(mcAccessToken);
211+
}
212+
144213
private static String getAuthorizationCode(String email, String password)
145214
throws LoginException
146215
{
@@ -295,6 +364,50 @@ private static String getMicrosoftAccessToken(String authCode)
295364
}
296365
}
297366

367+
private static String getMicrosoftAccessTokenFromRefreshToken(
368+
String refreshToken) throws LoginException
369+
{
370+
Map<String, String> postData = new HashMap<>();
371+
postData.put("client_id", CLIENT_ID);
372+
postData.put("refresh_token", refreshToken);
373+
postData.put("grant_type", "refresh_token");
374+
postData.put("redirect_uri",
375+
"https://login.live.com/oauth20_desktop.srf");
376+
postData.put("scope", SCOPE_UNENCODED);
377+
378+
byte[] encodedDataBytes =
379+
urlEncodeMap(postData).getBytes(StandardCharsets.UTF_8);
380+
381+
try
382+
{
383+
HttpURLConnection connection =
384+
(HttpURLConnection)AUTH_TOKEN_URL.openConnection();
385+
386+
connection.setRequestMethod("POST");
387+
connection.setRequestProperty("Content-Type",
388+
"application/x-www-form-urlencoded; charset=UTF-8");
389+
connection.setDoOutput(true);
390+
391+
System.out.println("Refreshing Microsoft access token...");
392+
393+
try(OutputStream out = connection.getOutputStream())
394+
{
395+
out.write(encodedDataBytes);
396+
}
397+
398+
WsonObject json = JsonUtils.parseConnectionToObject(connection);
399+
return json.getString("access_token");
400+
401+
}catch(IOException e)
402+
{
403+
throw new LoginException("Connection failed: " + e, e);
404+
405+
}catch(JsonException e)
406+
{
407+
throw new LoginException("Server sent invalid JSON.", e);
408+
}
409+
}
410+
298411
private static XBoxLiveToken getXBLToken(String msftAccessToken)
299412
throws LoginException
300413
{
@@ -518,4 +631,12 @@ private static URL createURL(String url)
518631
throw new IllegalArgumentException(e);
519632
}
520633
}
634+
635+
private static void setSession(MinecraftProfile mcProfile)
636+
{
637+
User session = new User(mcProfile.getName(), mcProfile.getUUID(),
638+
mcProfile.getAccessToken(), Optional.empty(), Optional.empty());
639+
640+
WurstClient.IMC.setWurstSession(session);
641+
}
521642
}

0 commit comments

Comments
 (0)