Initial commit

This commit is contained in:
BrightSkyz 2019-01-17 15:11:15 -05:00
parent 2ef0f27afa
commit de72c324d6
166 changed files with 13193 additions and 9 deletions

18
.gitignore vendored
View file

@ -1,14 +1,14 @@
## Intellij
.idea
.idea/hotswap_agent.xml
/out/
## Gradle
.gradle
/build/
/gradle/
gradlew*
# Ignore Gradle GUI config
gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# Cache of project
.gradletasknamecache
# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
gradle/wrapper/gradle-wrapper.properties

32
build.gradle Normal file
View file

@ -0,0 +1,32 @@
plugins {
id 'java'
id 'application'
id 'com.github.johnrengelman.shadow' version '1.2.4'
}
mainClassName = 'net.minecraft.launcher.Main'
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
repositories {
mavenCentral()
jcenter()
maven {
name = "mojang"
url = "https://libraries.minecraft.net/"
}
}
dependencies {
compile group: 'com.google.code.gson', name: 'gson', version: '2.3.1'
compile group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.0'
compile group: 'com.google.guava', name: 'guava', version: '18.0'
compile group: 'net.sf.jopt-simple', name: 'jopt-simple', version: '4.8'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.3.2'
compile group: 'commons-io', name: 'commons-io', version: '2.4'
compile group: 'commons-codec', name: 'commons-codec', version: '1.10'
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.1'
compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.1'
}

2
settings.gradle Normal file
View file

@ -0,0 +1,2 @@
rootProject.name = 'mclaunch'

View file

@ -0,0 +1,26 @@
package com.mojang.authlib;
public class Agent {
public static final Agent MINECRAFT = new Agent("Minecraft", 1);
public static final Agent SCROLLS = new Agent("Scrolls", 1);
private final String name;
private final int version;
public Agent(String name, int version) {
this.name = name;
this.version = version;
}
public String getName() {
return this.name;
}
public int getVersion() {
return this.version;
}
public String toString() {
return "Agent{name='" + this.name + '\'' + ", version=" + this.version + '}';
}
}

View file

@ -0,0 +1,15 @@
package com.mojang.authlib;
import com.mojang.authlib.Agent;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.UserAuthentication;
import com.mojang.authlib.minecraft.MinecraftSessionService;
public interface AuthenticationService {
public UserAuthentication createUserAuthentication(Agent var1);
public MinecraftSessionService createMinecraftSessionService();
public GameProfileRepository createProfileRepository();
}

View file

@ -0,0 +1,8 @@
package com.mojang.authlib;
import com.mojang.authlib.AuthenticationService;
public abstract class BaseAuthenticationService
implements AuthenticationService {
}

View file

@ -0,0 +1,251 @@
package com.mojang.authlib;
import com.google.common.collect.Multimap;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.UserAuthentication;
import com.mojang.authlib.UserType;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.util.UUIDTypeAdapter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class BaseUserAuthentication
implements UserAuthentication {
private static final Logger LOGGER = LogManager.getLogger();
protected static final String STORAGE_KEY_PROFILE_NAME = "displayName";
protected static final String STORAGE_KEY_PROFILE_ID = "uuid";
protected static final String STORAGE_KEY_PROFILE_PROPERTIES = "profileProperties";
protected static final String STORAGE_KEY_USER_NAME = "username";
protected static final String STORAGE_KEY_USER_ID = "userid";
protected static final String STORAGE_KEY_USER_PROPERTIES = "userProperties";
private final AuthenticationService authenticationService;
private final PropertyMap userProperties = new PropertyMap();
private String userid;
private String username;
private String password;
private GameProfile selectedProfile;
private UserType userType;
protected BaseUserAuthentication(AuthenticationService authenticationService) {
Validate.notNull(authenticationService);
this.authenticationService = authenticationService;
}
@Override
public boolean canLogIn() {
return !this.canPlayOnline() && StringUtils.isNotBlank(this.getUsername()) && StringUtils.isNotBlank(this.getPassword());
}
@Override
public void logOut() {
this.password = null;
this.userid = null;
this.setSelectedProfile(null);
this.getModifiableUserProperties().clear();
this.setUserType(null);
}
@Override
public boolean isLoggedIn() {
return this.getSelectedProfile() != null;
}
@Override
public void setUsername(String username) {
if (this.isLoggedIn() && this.canPlayOnline()) {
throw new IllegalStateException("Cannot change username whilst logged in & online");
}
this.username = username;
}
@Override
public void setPassword(String password) {
if (this.isLoggedIn() && this.canPlayOnline() && StringUtils.isNotBlank(password)) {
throw new IllegalStateException("Cannot set password whilst logged in & online");
}
this.password = password;
}
protected String getUsername() {
return this.username;
}
protected String getPassword() {
return this.password;
}
@Override
public void loadFromStorage(Map<String, Object> credentials) {
this.logOut();
this.setUsername(String.valueOf(credentials.get("username")));
if (credentials.containsKey("userid")) {
this.userid = String.valueOf(credentials.get("userid"));
} else {
this.userid = this.username;
}
if (credentials.containsKey("userProperties")) {
try {
final List<Map<String, String>> list = (List<Map<String, String>>) credentials.get("userProperties");
for (final Map<String, String> propertyMap : list) {
final String name = propertyMap.get("name");
final String value = propertyMap.get("value");
final String signature = propertyMap.get("signature");
if (signature == null) {
this.getModifiableUserProperties().put(name, new Property(name, value));
} else {
this.getModifiableUserProperties().put(name, new Property(name, value, signature));
}
}
} catch (Throwable t) {
BaseUserAuthentication.LOGGER.warn("Couldn't deserialize user properties", t);
}
}
if (credentials.containsKey("displayName") && credentials.containsKey("uuid")) {
final GameProfile profile = new GameProfile(
UUIDTypeAdapter.fromString(String.valueOf(credentials.get("uuid"))),
String.valueOf(credentials.get("displayName")));
if (credentials.containsKey("profileProperties")) {
try {
final List<Map<String, String>> list2 = (List<Map<String, String>>) credentials.get("profileProperties");
for (final Map<String, String> propertyMap2 : list2) {
final String name2 = propertyMap2.get("name");
final String value2 = propertyMap2.get("value");
final String signature2 = propertyMap2.get("signature");
if (signature2 == null) {
profile.getProperties().put(name2, new Property(name2, value2));
} else {
profile.getProperties().put(name2, new Property(name2, value2, signature2));
}
}
} catch (Throwable t2) {
BaseUserAuthentication.LOGGER.warn("Couldn't deserialize profile properties", t2);
}
}
this.setSelectedProfile(profile);
}
}
@Override
public Map<String, Object> saveForStorage() {
GameProfile selectedProfile;
HashMap<String, Object> result = new HashMap<String, Object>();
if (this.getUsername() != null) {
result.put(STORAGE_KEY_USER_NAME, this.getUsername());
}
if (this.getUserID() != null) {
result.put(STORAGE_KEY_USER_ID, this.getUserID());
} else if (this.getUsername() != null) {
result.put(STORAGE_KEY_USER_NAME, this.getUsername());
}
if (!this.getUserProperties().isEmpty()) {
ArrayList properties = new ArrayList();
for (Property userProperty : this.getUserProperties().values()) {
HashMap<String, String> property = new HashMap<String, String>();
property.put("name", userProperty.getName());
property.put("value", userProperty.getValue());
property.put("signature", userProperty.getSignature());
properties.add(property);
}
result.put(STORAGE_KEY_USER_PROPERTIES, properties);
}
if ((selectedProfile = this.getSelectedProfile()) != null) {
result.put(STORAGE_KEY_PROFILE_NAME, selectedProfile.getName());
result.put(STORAGE_KEY_PROFILE_ID, selectedProfile.getId());
ArrayList properties = new ArrayList();
for (Property profileProperty : selectedProfile.getProperties().values()) {
HashMap<String, String> property = new HashMap<String, String>();
property.put("name", profileProperty.getName());
property.put("value", profileProperty.getValue());
property.put("signature", profileProperty.getSignature());
properties.add(property);
}
if (!properties.isEmpty()) {
result.put(STORAGE_KEY_PROFILE_PROPERTIES, properties);
}
}
return result;
}
protected void setSelectedProfile(GameProfile selectedProfile) {
this.selectedProfile = selectedProfile;
}
@Override
public GameProfile getSelectedProfile() {
return this.selectedProfile;
}
public String toString() {
StringBuilder result = new StringBuilder();
result.append(this.getClass().getSimpleName());
result.append("{");
if (this.isLoggedIn()) {
result.append("Logged in as ");
result.append(this.getUsername());
if (this.getSelectedProfile() != null) {
result.append(" / ");
result.append(this.getSelectedProfile());
result.append(" - ");
if (this.canPlayOnline()) {
result.append("Online");
} else {
result.append("Offline");
}
}
} else {
result.append("Not logged in");
}
result.append("}");
return result.toString();
}
public AuthenticationService getAuthenticationService() {
return this.authenticationService;
}
@Override
public String getUserID() {
return this.userid;
}
@Override
public PropertyMap getUserProperties() {
if (this.isLoggedIn()) {
PropertyMap result = new PropertyMap();
result.putAll(this.getModifiableUserProperties());
return result;
}
return new PropertyMap();
}
protected PropertyMap getModifiableUserProperties() {
return this.userProperties;
}
@Override
public UserType getUserType() {
if (this.isLoggedIn()) {
return this.userType == null ? UserType.LEGACY : this.userType;
}
return null;
}
protected void setUserType(UserType userType) {
this.userType = userType;
}
protected void setUserid(String userid) {
this.userid = userid;
}
}

View file

@ -0,0 +1,69 @@
package com.mojang.authlib;
import com.mojang.authlib.properties.PropertyMap;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class GameProfile {
private final UUID id;
private final String name;
private final PropertyMap properties = new PropertyMap();
private boolean legacy;
public GameProfile(UUID id, String name) {
if (id == null && StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Name and ID cannot both be blank");
}
this.id = id;
this.name = name;
}
public UUID getId() {
return this.id;
}
public String getName() {
return this.name;
}
public PropertyMap getProperties() {
return this.properties;
}
public boolean isComplete() {
return this.id != null && StringUtils.isNotBlank(this.getName());
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
GameProfile that = (GameProfile)o;
if (this.id != null ? !this.id.equals(that.id) : that.id != null) {
return false;
}
if (this.name != null ? !this.name.equals(that.name) : that.name != null) {
return false;
}
return true;
}
public int hashCode() {
int result = this.id != null ? this.id.hashCode() : 0;
result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
return result;
}
public String toString() {
return new ToStringBuilder(this).append("id", this.id).append("name", this.name).append("properties", this.properties).append("legacy", this.legacy).toString();
}
public boolean isLegacy() {
return this.legacy;
}
}

View file

@ -0,0 +1,9 @@
package com.mojang.authlib;
import com.mojang.authlib.Agent;
import com.mojang.authlib.ProfileLookupCallback;
public interface GameProfileRepository {
public void findProfilesByNames(String[] var1, Agent var2, ProfileLookupCallback var3);
}

View file

@ -0,0 +1,178 @@
package com.mojang.authlib;
import com.mojang.authlib.BaseAuthenticationService;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class HttpAuthenticationService
extends BaseAuthenticationService {
private static final Logger LOGGER = LogManager.getLogger();
private final Proxy proxy;
protected HttpAuthenticationService(Proxy proxy) {
Validate.notNull(proxy);
this.proxy = proxy;
}
public Proxy getProxy() {
return this.proxy;
}
protected HttpURLConnection createUrlConnection(URL url) throws IOException {
Validate.notNull(url);
LOGGER.debug("Opening connection to " + url);
HttpURLConnection connection = (HttpURLConnection)url.openConnection(this.proxy);
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
connection.setUseCaches(false);
return connection;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public String performPostRequest(URL url, String post, String contentType) throws IOException {
HttpURLConnection connection;
Validate.notNull(url);
Validate.notNull(post);
Validate.notNull(contentType);
connection = this.createUrlConnection(url);
byte[] postAsBytes = post.getBytes(Charsets.UTF_8);
connection.setRequestProperty("Content-Type", contentType + "; charset=utf-8");
connection.setRequestProperty("Content-Length", "" + postAsBytes.length);
connection.setDoOutput(true);
LOGGER.debug("Writing POST data to " + url + ": " + post);
OutputStream outputStream = null;
try {
outputStream = connection.getOutputStream();
IOUtils.write(postAsBytes, outputStream);
}
finally {
IOUtils.closeQuietly(outputStream);
}
LOGGER.debug("Reading data from " + url);
InputStream inputStream = null;
try {
inputStream = connection.getInputStream();
String result = IOUtils.toString(inputStream, Charsets.UTF_8);
LOGGER.debug("Successful read, server response was " + connection.getResponseCode());
LOGGER.debug("Response: " + result);
String string = result;
return string;
}
catch (IOException e) {
IOUtils.closeQuietly(inputStream);
inputStream = connection.getErrorStream();
if (inputStream != null) {
LOGGER.debug("Reading error page from " + url);
String result = IOUtils.toString(inputStream, Charsets.UTF_8);
LOGGER.debug("Successful read, server response was " + connection.getResponseCode());
LOGGER.debug("Response: " + result);
String string = result;
return string;
}
LOGGER.debug("Request failed", (Throwable)e);
throw e;
}
finally {
IOUtils.closeQuietly(inputStream);
}
}
public String performGetRequest(URL url) throws IOException {
Validate.notNull(url);
HttpURLConnection connection = this.createUrlConnection(url);
LOGGER.debug("Reading data from " + url);
InputStream inputStream = null;
try {
inputStream = connection.getInputStream();
String result = IOUtils.toString(inputStream, Charsets.UTF_8);
LOGGER.debug("Successful read, server response was " + connection.getResponseCode());
LOGGER.debug("Response: " + result);
String string = result;
return string;
}
catch (IOException e) {
IOUtils.closeQuietly(inputStream);
inputStream = connection.getErrorStream();
if (inputStream != null) {
LOGGER.debug("Reading error page from " + url);
String result = IOUtils.toString(inputStream, Charsets.UTF_8);
LOGGER.debug("Successful read, server response was " + connection.getResponseCode());
LOGGER.debug("Response: " + result);
String string = result;
return string;
}
LOGGER.debug("Request failed", (Throwable)e);
throw e;
}
finally {
IOUtils.closeQuietly(inputStream);
}
}
public static URL constantURL(String url) {
try {
return new URL(url);
}
catch (MalformedURLException ex) {
throw new Error("Couldn't create constant for " + url, ex);
}
}
public static String buildQuery(Map<String, Object> query) {
if (query == null) {
return "";
}
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Object> entry : query.entrySet()) {
if (builder.length() > 0) {
builder.append('&');
}
try {
builder.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
}
catch (UnsupportedEncodingException e) {
LOGGER.error("Unexpected exception building query", (Throwable)e);
}
if (entry.getValue() == null) continue;
builder.append('=');
try {
builder.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
}
catch (UnsupportedEncodingException e) {
LOGGER.error("Unexpected exception building query", (Throwable)e);
}
}
return builder.toString();
}
public static URL concatenateURL(URL url, String query) {
try {
if (url.getQuery() != null && url.getQuery().length() > 0) {
return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile() + "&" + query);
}
return new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile() + "?" + query);
}
catch (MalformedURLException ex) {
throw new IllegalArgumentException("Could not concatenate given URL with GET arguments!", ex);
}
}
}

View file

@ -0,0 +1,18 @@
package com.mojang.authlib;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.BaseUserAuthentication;
import com.mojang.authlib.HttpAuthenticationService;
public abstract class HttpUserAuthentication
extends BaseUserAuthentication {
protected HttpUserAuthentication(HttpAuthenticationService authenticationService) {
super(authenticationService);
}
@Override
public HttpAuthenticationService getAuthenticationService() {
return (HttpAuthenticationService)super.getAuthenticationService();
}
}

View file

@ -0,0 +1,10 @@
package com.mojang.authlib;
import com.mojang.authlib.GameProfile;
public interface ProfileLookupCallback {
public void onProfileLookupSucceeded(GameProfile var1);
public void onProfileLookupFailed(GameProfile var1, Exception var2);
}

View file

@ -0,0 +1,42 @@
package com.mojang.authlib;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.UserType;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.properties.PropertyMap;
import java.util.Map;
public interface UserAuthentication {
public boolean canLogIn();
public void logIn() throws AuthenticationException;
public void logOut();
public boolean isLoggedIn();
public boolean canPlayOnline();
public GameProfile[] getAvailableProfiles();
public GameProfile getSelectedProfile();
public void selectGameProfile(GameProfile var1) throws AuthenticationException;
public void loadFromStorage(Map<String, Object> var1);
public Map<String, Object> saveForStorage();
public void setUsername(String var1);
public void setPassword(String var1);
public String getAuthenticatedToken();
public String getUserID();
public PropertyMap getUserProperties();
public UserType getUserType();
}

View file

@ -0,0 +1,32 @@
package com.mojang.authlib;
import java.util.HashMap;
import java.util.Map;
public enum UserType {
LEGACY("legacy"),
MOJANG("mojang");
private static final Map<String, UserType> BY_NAME;
private final String name;
private UserType(String name) {
this.name = name;
}
public static UserType byName(String name) {
return BY_NAME.get(name.toLowerCase());
}
public String getName() {
return this.name;
}
static {
BY_NAME = new HashMap<String, UserType>();
for (UserType type : UserType.values()) {
BY_NAME.put(type.name, type);
}
}
}

View file

@ -0,0 +1,20 @@
package com.mojang.authlib.exceptions;
public class AuthenticationException
extends Exception {
public AuthenticationException() {
}
public AuthenticationException(String message) {
super(message);
}
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
public AuthenticationException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,22 @@
package com.mojang.authlib.exceptions;
import com.mojang.authlib.exceptions.AuthenticationException;
public class AuthenticationUnavailableException
extends AuthenticationException {
public AuthenticationUnavailableException() {
}
public AuthenticationUnavailableException(String message) {
super(message);
}
public AuthenticationUnavailableException(String message, Throwable cause) {
super(message, cause);
}
public AuthenticationUnavailableException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,22 @@
package com.mojang.authlib.exceptions;
import com.mojang.authlib.exceptions.AuthenticationException;
public class InvalidCredentialsException
extends AuthenticationException {
public InvalidCredentialsException() {
}
public InvalidCredentialsException(String message) {
super(message);
}
public InvalidCredentialsException(String message, Throwable cause) {
super(message, cause);
}
public InvalidCredentialsException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,22 @@
package com.mojang.authlib.exceptions;
import com.mojang.authlib.exceptions.InvalidCredentialsException;
public class UserMigratedException
extends InvalidCredentialsException {
public UserMigratedException() {
}
public UserMigratedException(String message) {
super(message);
}
public UserMigratedException(String message, Throwable cause) {
super(message, cause);
}
public UserMigratedException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,38 @@
package com.mojang.authlib.legacy;
import com.mojang.authlib.Agent;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.UserAuthentication;
import com.mojang.authlib.legacy.LegacyMinecraftSessionService;
import com.mojang.authlib.legacy.LegacyUserAuthentication;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import java.net.Proxy;
import org.apache.commons.lang3.Validate;
public class LegacyAuthenticationService
extends HttpAuthenticationService {
protected LegacyAuthenticationService(Proxy proxy) {
super(proxy);
}
@Override
public LegacyUserAuthentication createUserAuthentication(Agent agent) {
Validate.notNull(agent);
if (agent != Agent.MINECRAFT) {
throw new IllegalArgumentException("Legacy authentication cannot handle anything but Minecraft");
}
return new LegacyUserAuthentication(this);
}
@Override
public LegacyMinecraftSessionService createMinecraftSessionService() {
return new LegacyMinecraftSessionService(this);
}
@Override
public GameProfileRepository createProfileRepository() {
throw new UnsupportedOperationException("Legacy authentication service has no profile repository");
}
}

View file

@ -0,0 +1,74 @@
package com.mojang.authlib.legacy;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
import com.mojang.authlib.legacy.LegacyAuthenticationService;
import com.mojang.authlib.minecraft.HttpMinecraftSessionService;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class LegacyMinecraftSessionService
extends HttpMinecraftSessionService {
private static final String BASE_URL = "http://session.minecraft.net/game/";
private static final URL JOIN_URL = HttpAuthenticationService.constantURL("http://session.minecraft.net/game/joinserver.jsp");
private static final URL CHECK_URL = HttpAuthenticationService.constantURL("http://session.minecraft.net/game/checkserver.jsp");
protected LegacyMinecraftSessionService(LegacyAuthenticationService authenticationService) {
super(authenticationService);
}
@Override
public void joinServer(GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException {
HashMap<String, Object> arguments = new HashMap<String, Object>();
arguments.put("user", profile.getName());
arguments.put("sessionId", authenticationToken);
arguments.put("serverId", serverId);
URL url = HttpAuthenticationService.concatenateURL(JOIN_URL, HttpAuthenticationService.buildQuery(arguments));
try {
String response = this.getAuthenticationService().performGetRequest(url);
if (!response.equals("OK")) {
throw new AuthenticationException(response);
}
}
catch (IOException e) {
throw new AuthenticationUnavailableException(e);
}
}
@Override
public GameProfile hasJoinedServer(GameProfile user, String serverId) throws AuthenticationUnavailableException {
HashMap<String, Object> arguments = new HashMap<String, Object>();
arguments.put("user", user.getName());
arguments.put("serverId", serverId);
URL url = HttpAuthenticationService.concatenateURL(CHECK_URL, HttpAuthenticationService.buildQuery(arguments));
try {
String response = this.getAuthenticationService().performGetRequest(url);
return response.equals("YES") ? user : null;
}
catch (IOException e) {
throw new AuthenticationUnavailableException(e);
}
}
@Override
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
return new HashMap<MinecraftProfileTexture.Type, MinecraftProfileTexture>();
}
@Override
public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
return profile;
}
@Override
public LegacyAuthenticationService getAuthenticationService() {
return (LegacyAuthenticationService)super.getAuthenticationService();
}
}

View file

@ -0,0 +1,108 @@
package com.mojang.authlib.legacy;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.HttpUserAuthentication;
import com.mojang.authlib.UserType;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.exceptions.InvalidCredentialsException;
import com.mojang.authlib.legacy.LegacyAuthenticationService;
import com.mojang.util.UUIDTypeAdapter;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
public class LegacyUserAuthentication
extends HttpUserAuthentication {
private static final URL AUTHENTICATION_URL = HttpAuthenticationService.constantURL("https://login.minecraft.net");
private static final int AUTHENTICATION_VERSION = 14;
private static final int RESPONSE_PART_PROFILE_NAME = 2;
private static final int RESPONSE_PART_SESSION_TOKEN = 3;
private static final int RESPONSE_PART_PROFILE_ID = 4;
private String sessionToken;
protected LegacyUserAuthentication(LegacyAuthenticationService authenticationService) {
super(authenticationService);
}
@Override
public void logIn() throws AuthenticationException {
String response;
String sessionToken;
String profileId;
String profileName;
if (StringUtils.isBlank(this.getUsername())) {
throw new InvalidCredentialsException("Invalid username");
}
if (StringUtils.isBlank(this.getPassword())) {
throw new InvalidCredentialsException("Invalid password");
}
HashMap<String, Object> args = new HashMap<String, Object>();
args.put("user", this.getUsername());
args.put("password", this.getPassword());
args.put("version", 14);
try {
response = this.getAuthenticationService().performPostRequest(AUTHENTICATION_URL, HttpAuthenticationService.buildQuery(args), "application/x-www-form-urlencoded").trim();
}
catch (IOException e) {
throw new AuthenticationException("Authentication server is not responding", e);
}
String[] split = response.split(":");
if (split.length == 5) {
profileId = split[4];
profileName = split[2];
sessionToken = split[3];
if (StringUtils.isBlank(profileId) || StringUtils.isBlank(profileName) || StringUtils.isBlank(sessionToken)) {
throw new AuthenticationException("Unknown response from authentication server: " + response);
}
} else {
throw new InvalidCredentialsException(response);
}
this.setSelectedProfile(new GameProfile(UUIDTypeAdapter.fromString(profileId), profileName));
this.sessionToken = sessionToken;
this.setUserType(UserType.LEGACY);
}
@Override
public void logOut() {
super.logOut();
this.sessionToken = null;
}
@Override
public boolean canPlayOnline() {
return this.isLoggedIn() && this.getSelectedProfile() != null && this.getAuthenticatedToken() != null;
}
@Override
public GameProfile[] getAvailableProfiles() {
if (this.getSelectedProfile() != null) {
return new GameProfile[]{this.getSelectedProfile()};
}
return new GameProfile[0];
}
@Override
public void selectGameProfile(GameProfile profile) throws AuthenticationException {
throw new UnsupportedOperationException("Game profiles cannot be changed in the legacy authentication service");
}
@Override
public String getAuthenticatedToken() {
return this.sessionToken;
}
@Override
public String getUserID() {
return this.getUsername();
}
@Override
public LegacyAuthenticationService getAuthenticationService() {
return (LegacyAuthenticationService)super.getAuthenticationService();
}
}

View file

@ -0,0 +1,18 @@
package com.mojang.authlib.minecraft;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.minecraft.MinecraftSessionService;
public abstract class BaseMinecraftSessionService
implements MinecraftSessionService {
private final AuthenticationService authenticationService;
protected BaseMinecraftSessionService(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
public AuthenticationService getAuthenticationService() {
return this.authenticationService;
}
}

View file

@ -0,0 +1,18 @@
package com.mojang.authlib.minecraft;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.minecraft.BaseMinecraftSessionService;
public abstract class HttpMinecraftSessionService
extends BaseMinecraftSessionService {
protected HttpMinecraftSessionService(HttpAuthenticationService authenticationService) {
super(authenticationService);
}
@Override
public HttpAuthenticationService getAuthenticationService() {
return (HttpAuthenticationService)super.getAuthenticationService();
}
}

View file

@ -0,0 +1,48 @@
package com.mojang.authlib.minecraft;
import com.mojang.authlib.GameProfile;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;
public class InsecureTextureException
extends RuntimeException {
public InsecureTextureException(String message) {
super(message);
}
public static class MissingTextureException
extends InsecureTextureException {
public MissingTextureException() {
super("No texture information found");
}
}
public static class OutdatedTextureException
extends InsecureTextureException {
private final Date validFrom;
private final Calendar limit;
public OutdatedTextureException(Date validFrom, Calendar limit) {
super("Decrypted textures payload is too old (" + validFrom + ", but we need it to be at least " + limit + ")");
this.validFrom = validFrom;
this.limit = limit;
}
}
public static class WrongTextureOwnerException
extends InsecureTextureException {
private final GameProfile expected;
private final UUID resultId;
private final String resultName;
public WrongTextureOwnerException(GameProfile expected, UUID resultId, String resultName) {
super("Decrypted textures payload was for another user (expected " + expected.getId() + "/" + expected.getName() + " but was for " + resultId + "/" + resultName + ")");
this.expected = expected;
this.resultId = resultId;
this.resultName = resultName;
}
}
}

View file

@ -0,0 +1,47 @@
package com.mojang.authlib.minecraft;
import java.util.Map;
import javax.annotation.Nullable;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
public class MinecraftProfileTexture {
private final String url;
private final Map<String, String> metadata;
public MinecraftProfileTexture(String url, Map<String, String> metadata) {
this.url = url;
this.metadata = metadata;
}
public String getUrl() {
return this.url;
}
@Nullable
public String getMetadata(String key) {
if (this.metadata == null) {
return null;
}
return this.metadata.get(key);
}
public String getHash() {
return FilenameUtils.getBaseName(this.url);
}
public String toString() {
return new ToStringBuilder(this).append("url", this.url).append("hash", this.getHash()).toString();
}
public static enum Type {
SKIN,
CAPE;
private Type() {
}
}
}

View file

@ -0,0 +1,18 @@
package com.mojang.authlib.minecraft;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import java.util.Map;
public interface MinecraftSessionService {
public void joinServer(GameProfile var1, String var2, String var3) throws AuthenticationException;
public GameProfile hasJoinedServer(GameProfile var1, String var2) throws AuthenticationUnavailableException;
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile var1, boolean var2);
public GameProfile fillProfileProperties(GameProfile var1, boolean var2);
}

View file

@ -0,0 +1,60 @@
package com.mojang.authlib.properties;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import org.apache.commons.codec.binary.Base64;
public class Property {
private final String name;
private final String value;
private final String signature;
public Property(String value, String name) {
this(value, name, null);
}
public Property(String name, String value, String signature) {
this.name = name;
this.value = value;
this.signature = signature;
}
public String getName() {
return this.name;
}
public String getValue() {
return this.value;
}
public String getSignature() {
return this.signature;
}
public boolean hasSignature() {
return this.signature != null;
}
public boolean isSignatureValid(PublicKey publicKey) {
try {
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
signature.update(this.value.getBytes());
return signature.verify(Base64.decodeBase64(this.signature));
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (InvalidKeyException e) {
e.printStackTrace();
}
catch (SignatureException e) {
e.printStackTrace();
}
return false;
}
}

View file

@ -0,0 +1,82 @@
package com.mojang.authlib.properties;
import com.google.common.collect.ForwardingMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.mojang.authlib.properties.Property;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class PropertyMap
extends ForwardingMultimap<String, Property> {
private final Multimap<String, Property> properties = LinkedHashMultimap.create();
@Override
protected Multimap<String, Property> delegate() {
return this.properties;
}
public static class Serializer
implements JsonSerializer<PropertyMap>,
JsonDeserializer<PropertyMap> {
@Override
public PropertyMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
PropertyMap result;
block5 : {
block4 : {
result = new PropertyMap();
if (!(json instanceof JsonObject)) break block4;
JsonObject object = (JsonObject)json;
for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
if (!(entry.getValue() instanceof JsonArray)) continue;
for (JsonElement element : (JsonArray)entry.getValue()) {
result.put(entry.getKey(), new Property(entry.getKey(), element.getAsString()));
}
}
break block5;
}
if (!(json instanceof JsonArray)) break block5;
for (JsonElement element : (JsonArray)json) {
if (!(element instanceof JsonObject)) continue;
JsonObject object = (JsonObject)element;
String name = object.getAsJsonPrimitive("name").getAsString();
String value = object.getAsJsonPrimitive("value").getAsString();
if (object.has("signature")) {
result.put(name, new Property(name, value, object.getAsJsonPrimitive("signature").getAsString()));
continue;
}
result.put(name, new Property(name, value));
}
}
return result;
}
@Override
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
JsonArray result = new JsonArray();
for (Property property : src.values()) {
JsonObject object = new JsonObject();
object.addProperty("name", property.getName());
object.addProperty("value", property.getValue());
if (property.hasSignature()) {
object.addProperty("signature", property.getSignature());
}
result.add(object);
}
return result;
}
}
}

View file

@ -0,0 +1,20 @@
package com.mojang.authlib.yggdrasil;
public class ProfileIncompleteException
extends RuntimeException {
public ProfileIncompleteException() {
}
public ProfileIncompleteException(String message) {
super(message);
}
public ProfileIncompleteException(String message, Throwable cause) {
super(message, cause);
}
public ProfileIncompleteException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,20 @@
package com.mojang.authlib.yggdrasil;
public class ProfileNotFoundException
extends RuntimeException {
public ProfileNotFoundException() {
}
public ProfileNotFoundException(String message) {
super(message);
}
public ProfileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public ProfileNotFoundException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,130 @@
package com.mojang.authlib.yggdrasil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.mojang.authlib.Agent;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.UserAuthentication;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
import com.mojang.authlib.exceptions.InvalidCredentialsException;
import com.mojang.authlib.exceptions.UserMigratedException;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
import com.mojang.authlib.yggdrasil.response.ProfileSearchResultsResponse;
import com.mojang.authlib.yggdrasil.response.Response;
import com.mojang.util.UUIDTypeAdapter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.Proxy;
import java.net.URL;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
public class YggdrasilAuthenticationService
extends HttpAuthenticationService {
private final String clientToken;
private final Gson gson;
public YggdrasilAuthenticationService(Proxy proxy, String clientToken) {
super(proxy);
this.clientToken = clientToken;
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter((Type)((Object)GameProfile.class), new GameProfileSerializer());
builder.registerTypeAdapter((Type)((Object)PropertyMap.class), new PropertyMap.Serializer());
builder.registerTypeAdapter((Type)((Object)UUID.class), new UUIDTypeAdapter());
builder.registerTypeAdapter((Type)((Object)ProfileSearchResultsResponse.class), new ProfileSearchResultsResponse.Serializer());
this.gson = builder.create();
}
@Override
public UserAuthentication createUserAuthentication(Agent agent) {
return new YggdrasilUserAuthentication(this, agent);
}
@Override
public MinecraftSessionService createMinecraftSessionService() {
return new YggdrasilMinecraftSessionService(this);
}
@Override
public GameProfileRepository createProfileRepository() {
return new YggdrasilGameProfileRepository(this);
}
protected <T extends Response> T makeRequest(URL url, Object input, Class<T> classOfT) throws AuthenticationException {
try {
String jsonResult = input == null ? this.performGetRequest(url) : this.performPostRequest(url, this.gson.toJson(input), "application/json");
//System.out.println(jsonResult);
Response result = (Response)this.gson.fromJson(jsonResult, classOfT);
if (result == null) {
return null;
}
if (StringUtils.isNotBlank(result.getError())) {
if ("UserMigratedException".equals(result.getCause())) {
throw new UserMigratedException(result.getErrorMessage());
}
if (result.getError().equals("ForbiddenOperationException")) {
throw new InvalidCredentialsException(result.getErrorMessage());
}
throw new AuthenticationException(result.getErrorMessage());
}
return (T)result;
}
catch (IOException e) {
throw new AuthenticationUnavailableException("Cannot contact authentication server", e);
}
catch (IllegalStateException e) {
throw new AuthenticationUnavailableException("Cannot contact authentication server", e);
}
catch (JsonParseException e) {
throw new AuthenticationUnavailableException("Cannot contact authentication server", e);
}
}
public String getClientToken() {
return this.clientToken;
}
private static class GameProfileSerializer
implements JsonSerializer<GameProfile>,
JsonDeserializer<GameProfile> {
private GameProfileSerializer() {
}
@Override
public GameProfile deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject object = (JsonObject)json;
UUID id = object.has("id") ? (UUID)context.deserialize(object.get("id"), (Type)((Object)UUID.class)) : null;
String name = object.has("name") ? object.getAsJsonPrimitive("name").getAsString() : null;
return new GameProfile(id, name);
}
@Override
public JsonElement serialize(GameProfile src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
if (src.getId() != null) {
result.add("id", context.serialize(src.getId()));
}
if (src.getName() != null) {
result.addProperty("name", src.getName());
}
return result;
}
}
}

View file

@ -0,0 +1,93 @@
package com.mojang.authlib.yggdrasil;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.mojang.authlib.Agent;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.ProfileLookupCallback;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.yggdrasil.ProfileNotFoundException;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.response.ProfileSearchResultsResponse;
import com.mojang.authlib.yggdrasil.response.Response;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class YggdrasilGameProfileRepository
implements GameProfileRepository {
private static final Logger LOGGER = LogManager.getLogger();
private static final String BASE_URL = "https://api.mojang.com/";
private static final String SEARCH_PAGE_URL = "https://api.mojang.com/profiles/";
private static final int ENTRIES_PER_PAGE = 2;
private static final int MAX_FAIL_COUNT = 3;
private static final int DELAY_BETWEEN_PAGES = 100;
private static final int DELAY_BETWEEN_FAILURES = 750;
private final YggdrasilAuthenticationService authenticationService;
public YggdrasilGameProfileRepository(YggdrasilAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
@Override
public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback callback) {
final Set<String> criteria = Sets.newHashSet();
for (final String name : names) {
if (!Strings.isNullOrEmpty(name)) {
criteria.add(name.toLowerCase());
}
}
final int page = 0;
for (final List<String> request : Iterables.partition(criteria, 2)) {
int failCount = 0;
boolean failed;
do {
failed = false;
try {
final ProfileSearchResultsResponse response = this.authenticationService.makeRequest(HttpAuthenticationService.constantURL("https://api.mojang.com/profiles/" + agent.getName().toLowerCase()), request, ProfileSearchResultsResponse.class);
failCount = 0;
YggdrasilGameProfileRepository.LOGGER.debug("Page {} returned {} results, parsing", page,
response.getProfiles().length);
final Set<String> missing = Sets.newHashSet(request);
for (final GameProfile profile : response.getProfiles()) {
YggdrasilGameProfileRepository.LOGGER.debug("Successfully looked up profile {}", profile);
missing.remove(profile.getName().toLowerCase());
callback.onProfileLookupSucceeded(profile);
}
for (final String name2 : missing) {
YggdrasilGameProfileRepository.LOGGER.debug("Couldn't find profile {}", name2);
callback.onProfileLookupFailed(new GameProfile(null, name2), new ProfileNotFoundException(
"Server did not find the requested profile"));
}
try {
Thread.sleep(100L);
} catch (InterruptedException ex) {
}
} catch (AuthenticationException e) {
if (++failCount == 3) {
for (final String name3 : request) {
YggdrasilGameProfileRepository.LOGGER.debug(
"Couldn't find profile {} because of a server error", name3);
callback.onProfileLookupFailed(new GameProfile(null, name3), e);
}
} else {
try {
Thread.sleep(750L);
} catch (InterruptedException ex2) {
}
failed = true;
}
}
} while (failed);
}
}
}

View file

@ -0,0 +1,176 @@
package com.mojang.authlib.yggdrasil;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.exceptions.AuthenticationUnavailableException;
import com.mojang.authlib.minecraft.HttpMinecraftSessionService;
import com.mojang.authlib.minecraft.InsecureTextureException;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.request.JoinMinecraftServerRequest;
import com.mojang.authlib.yggdrasil.response.HasJoinedMinecraftServerResponse;
import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse;
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
import com.mojang.authlib.yggdrasil.response.Response;
import com.mojang.util.UUIDTypeAdapter;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.Charsets;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class YggdrasilMinecraftSessionService
extends HttpMinecraftSessionService {
private static final Logger LOGGER = LogManager.getLogger();
private static final String BASE_URL = "https://sessionserver.mojang.com/session/minecraft/";
private static final URL JOIN_URL = HttpAuthenticationService.constantURL("https://sessionserver.mojang.com/session/minecraft/join");
private static final URL CHECK_URL = HttpAuthenticationService.constantURL("https://sessionserver.mojang.com/session/minecraft/hasJoined");
private final PublicKey publicKey;
private final Gson gson = new GsonBuilder().registerTypeAdapter((Type)((Object)UUID.class), new UUIDTypeAdapter()).create();
private final LoadingCache<GameProfile, GameProfile> insecureProfiles = CacheBuilder.newBuilder().expireAfterWrite(6L, TimeUnit.HOURS).build(new CacheLoader<GameProfile, GameProfile>(){
@Override
public GameProfile load(GameProfile key) throws Exception {
return YggdrasilMinecraftSessionService.this.fillGameProfile(key, false);
}
});
protected YggdrasilMinecraftSessionService(YggdrasilAuthenticationService authenticationService) {
super(authenticationService);
try {
X509EncodedKeySpec spec = new X509EncodedKeySpec(IOUtils.toByteArray(YggdrasilMinecraftSessionService.class.getResourceAsStream("/yggdrasil_session_pubkey.der")));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
this.publicKey = keyFactory.generatePublic(spec);
}
catch (Exception e) {
throw new Error("Missing/invalid yggdrasil public key!");
}
}
@Override
public void joinServer(GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException {
JoinMinecraftServerRequest request = new JoinMinecraftServerRequest();
request.accessToken = authenticationToken;
request.selectedProfile = profile.getId();
request.serverId = serverId;
this.getAuthenticationService().makeRequest(JOIN_URL, request, Response.class);
}
@Override
public GameProfile hasJoinedServer(GameProfile user, String serverId) throws AuthenticationUnavailableException {
HashMap<String, Object> arguments = new HashMap<String, Object>();
arguments.put("username", user.getName());
arguments.put("serverId", serverId);
URL url = HttpAuthenticationService.concatenateURL(CHECK_URL, HttpAuthenticationService.buildQuery(arguments));
try {
HasJoinedMinecraftServerResponse response = this.getAuthenticationService().makeRequest(url, null, HasJoinedMinecraftServerResponse.class);
if (response != null && response.getId() != null) {
GameProfile result = new GameProfile(response.getId(), user.getName());
if (response.getProperties() != null) {
result.getProperties().putAll(response.getProperties());
}
return result;
}
return null;
}
catch (AuthenticationUnavailableException e) {
throw e;
}
catch (AuthenticationException e) {
return null;
}
}
@Override
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures(GameProfile profile, boolean requireSecure) {
MinecraftTexturesPayload result;
Property textureProperty = Iterables.getFirst(profile.getProperties().get("textures"), null);
if (textureProperty == null) {
return new HashMap<MinecraftProfileTexture.Type, MinecraftProfileTexture>();
}
if (requireSecure) {
if (!textureProperty.hasSignature()) {
LOGGER.error("Signature is missing from textures payload");
throw new InsecureTextureException("Signature is missing from textures payload");
}
if (!textureProperty.isSignatureValid(this.publicKey)) {
LOGGER.error("Textures payload has been tampered with (signature invalid)");
throw new InsecureTextureException("Textures payload has been tampered with (signature invalid)");
}
}
try {
String json = new String(Base64.decodeBase64(textureProperty.getValue()), Charsets.UTF_8);
result = this.gson.fromJson(json, MinecraftTexturesPayload.class);
}
catch (JsonParseException e) {
LOGGER.error("Could not decode textures payload", (Throwable)e);
return new HashMap<MinecraftProfileTexture.Type, MinecraftProfileTexture>();
}
return result.getTextures() == null ? new HashMap() : result.getTextures();
}
@Override
public GameProfile fillProfileProperties(GameProfile profile, boolean requireSecure) {
if (profile.getId() == null) {
return profile;
}
if (!requireSecure) {
return this.insecureProfiles.getUnchecked(profile);
}
return this.fillGameProfile(profile, true);
}
protected GameProfile fillGameProfile(GameProfile profile, boolean requireSecure) {
try {
URL url = HttpAuthenticationService.constantURL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(profile.getId()));
url = HttpAuthenticationService.concatenateURL(url, "unsigned=" + !requireSecure);
MinecraftProfilePropertiesResponse response = this.getAuthenticationService().makeRequest(url, null, MinecraftProfilePropertiesResponse.class);
if (response == null) {
LOGGER.debug("Couldn't fetch profile properties for " + profile + " as the profile does not exist");
return profile;
}
GameProfile result = new GameProfile(response.getId(), response.getName());
result.getProperties().putAll(response.getProperties());
profile.getProperties().putAll(response.getProperties());
LOGGER.debug("Successfully fetched profile properties for " + profile);
return result;
}
catch (AuthenticationException e) {
LOGGER.warn("Couldn't look up profile properties for " + profile, (Throwable)e);
return profile;
}
}
@Override
public YggdrasilAuthenticationService getAuthenticationService() {
return (YggdrasilAuthenticationService)super.getAuthenticationService();
}
}

View file

@ -0,0 +1,248 @@
package com.mojang.authlib.yggdrasil;
import com.google.common.collect.Multimap;
import com.mojang.authlib.Agent;
import com.mojang.authlib.AuthenticationService;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.HttpAuthenticationService;
import com.mojang.authlib.HttpUserAuthentication;
import com.mojang.authlib.UserType;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.exceptions.InvalidCredentialsException;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.request.AuthenticationRequest;
import com.mojang.authlib.yggdrasil.request.RefreshRequest;
import com.mojang.authlib.yggdrasil.request.ValidateRequest;
import com.mojang.authlib.yggdrasil.response.AuthenticationResponse;
import com.mojang.authlib.yggdrasil.response.RefreshResponse;
import com.mojang.authlib.yggdrasil.response.Response;
import com.mojang.authlib.yggdrasil.response.User;
import java.net.URL;
import java.util.Arrays;
import java.util.Map;
import java.util.UUID;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class YggdrasilUserAuthentication
extends HttpUserAuthentication {
private static final Logger LOGGER = LogManager.getLogger();
private static final String BASE_URL = "https://authserver.mojang.com/";
private static final URL ROUTE_AUTHENTICATE = HttpAuthenticationService.constantURL("https://authserver.mojang.com/authenticate");
private static final URL ROUTE_REFRESH = HttpAuthenticationService.constantURL("https://authserver.mojang.com/refresh");
private static final URL ROUTE_VALIDATE = HttpAuthenticationService.constantURL("https://authserver.mojang.com/validate");
private static final URL ROUTE_INVALIDATE = HttpAuthenticationService.constantURL("https://authserver.mojang.com/invalidate");
private static final URL ROUTE_SIGNOUT = HttpAuthenticationService.constantURL("https://authserver.mojang.com/signout");
private static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken";
private final Agent agent;
private GameProfile[] profiles;
private String accessToken;
private boolean isOnline;
public YggdrasilUserAuthentication(YggdrasilAuthenticationService authenticationService, Agent agent) {
super(authenticationService);
this.agent = agent;
}
@Override
public boolean canLogIn() {
return !this.canPlayOnline() && StringUtils.isNotBlank(this.getUsername()) && (StringUtils.isNotBlank(this.getPassword()) || StringUtils.isNotBlank(this.getAuthenticatedToken()));
}
@Override
public void logIn() throws AuthenticationException {
if (StringUtils.isBlank(this.getUsername())) {
throw new InvalidCredentialsException("Invalid username");
}
if (StringUtils.isNotBlank(this.getAuthenticatedToken())) {
this.logInWithToken();
} else if (StringUtils.isNotBlank(this.getPassword())) {
this.logInWithPassword();
} else {
throw new InvalidCredentialsException("Invalid password");
}
}
protected void logInWithPassword() throws AuthenticationException {
if (StringUtils.isBlank(this.getUsername())) {
throw new InvalidCredentialsException("Invalid username");
}
if (StringUtils.isBlank(this.getPassword())) {
throw new InvalidCredentialsException("Invalid password");
}
LOGGER.info("Logging in with username & password");
AuthenticationRequest request = new AuthenticationRequest(this, this.getUsername(), this.getPassword());
AuthenticationResponse response = this.getAuthenticationService().makeRequest(ROUTE_AUTHENTICATE, request, AuthenticationResponse.class);
if (!response.getClientToken().equals(this.getAuthenticationService().getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
}
if (response.getSelectedProfile() != null) {
this.setUserType(response.getSelectedProfile().isLegacy() ? UserType.LEGACY : UserType.MOJANG);
} else if (ArrayUtils.isNotEmpty(response.getAvailableProfiles())) {
this.setUserType(response.getAvailableProfiles()[0].isLegacy() ? UserType.LEGACY : UserType.MOJANG);
}
User user = response.getUser();
if (user != null && user.getId() != null) {
this.setUserid(user.getId());
} else {
this.setUserid(this.getUsername());
}
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.profiles = response.getAvailableProfiles();
this.setSelectedProfile(response.getSelectedProfile());
this.getModifiableUserProperties().clear();
this.updateUserProperties(user);
}
protected void updateUserProperties(User user) {
if (user == null) {
return;
}
if (user.getProperties() != null) {
this.getModifiableUserProperties().putAll(user.getProperties());
}
}
protected void logInWithToken() throws AuthenticationException {
if (StringUtils.isBlank(this.getUserID())) {
if (StringUtils.isBlank(this.getUsername())) {
this.setUserid(this.getUsername());
} else {
throw new InvalidCredentialsException("Invalid uuid & username");
}
}
if (StringUtils.isBlank(this.getAuthenticatedToken())) {
throw new InvalidCredentialsException("Invalid access token");
}
LOGGER.info("Logging in with access token");
if (this.checkTokenValidity()) {
LOGGER.debug("Skipping refresh call as we're safely logged in.");
this.isOnline = true;
return;
}
RefreshRequest request = new RefreshRequest(this);
RefreshResponse response = this.getAuthenticationService().makeRequest(ROUTE_REFRESH, request, RefreshResponse.class);
if (!response.getClientToken().equals(this.getAuthenticationService().getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
}
if (response.getSelectedProfile() != null) {
this.setUserType(response.getSelectedProfile().isLegacy() ? UserType.LEGACY : UserType.MOJANG);
} else if (ArrayUtils.isNotEmpty(response.getAvailableProfiles())) {
this.setUserType(response.getAvailableProfiles()[0].isLegacy() ? UserType.LEGACY : UserType.MOJANG);
}
if (response.getUser() != null && response.getUser().getId() != null) {
this.setUserid(response.getUser().getId());
} else {
this.setUserid(this.getUsername());
}
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.profiles = response.getAvailableProfiles();
this.setSelectedProfile(response.getSelectedProfile());
this.getModifiableUserProperties().clear();
this.updateUserProperties(response.getUser());
}
protected boolean checkTokenValidity() throws AuthenticationException {
ValidateRequest request = new ValidateRequest(this);
try {
this.getAuthenticationService().makeRequest(ROUTE_VALIDATE, request, Response.class);
return true;
}
catch (AuthenticationException ex) {
return false;
}
}
@Override
public void logOut() {
super.logOut();
this.accessToken = null;
this.profiles = null;
this.isOnline = false;
}
@Override
public GameProfile[] getAvailableProfiles() {
return this.profiles;
}
@Override
public boolean isLoggedIn() {
return StringUtils.isNotBlank(this.accessToken);
}
@Override
public boolean canPlayOnline() {
return this.isLoggedIn() && this.getSelectedProfile() != null && this.isOnline;
}
@Override
public void selectGameProfile(GameProfile profile) throws AuthenticationException {
if (!this.isLoggedIn()) {
throw new AuthenticationException("Cannot change game profile whilst not logged in");
}
if (this.getSelectedProfile() != null) {
throw new AuthenticationException("Cannot change game profile. You must log out and back in.");
}
if (profile == null || !ArrayUtils.contains(this.profiles, profile)) {
throw new IllegalArgumentException("Invalid profile '" + profile + "'");
}
RefreshRequest request = new RefreshRequest(this, profile);
RefreshResponse response = this.getAuthenticationService().makeRequest(ROUTE_REFRESH, request, RefreshResponse.class);
if (!response.getClientToken().equals(this.getAuthenticationService().getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
}
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.setSelectedProfile(response.getSelectedProfile());
}
@Override
public void loadFromStorage(Map<String, Object> credentials) {
super.loadFromStorage(credentials);
this.accessToken = String.valueOf(credentials.get(STORAGE_KEY_ACCESS_TOKEN));
}
@Override
public Map<String, Object> saveForStorage() {
Map<String, Object> result = super.saveForStorage();
if (StringUtils.isNotBlank(this.getAuthenticatedToken())) {
result.put(STORAGE_KEY_ACCESS_TOKEN, this.getAuthenticatedToken());
}
return result;
}
@Deprecated
public String getSessionToken() {
if (this.isLoggedIn() && this.getSelectedProfile() != null && this.canPlayOnline()) {
return String.format("token:%s:%s", this.getAuthenticatedToken(), this.getSelectedProfile().getId());
}
return null;
}
@Override
public String getAuthenticatedToken() {
return this.accessToken;
}
public Agent getAgent() {
return this.agent;
}
@Override
public String toString() {
return "YggdrasilAuthenticationService{agent=" + this.agent + ", profiles=" + Arrays.toString(this.profiles) + ", selectedProfile=" + this.getSelectedProfile() + ", username='" + this.getUsername() + '\'' + ", isLoggedIn=" + this.isLoggedIn() + ", userType=" + (Object)((Object)this.getUserType()) + ", canPlayOnline=" + this.canPlayOnline() + ", accessToken='" + this.accessToken + '\'' + ", clientToken='" + this.getAuthenticationService().getClientToken() + '\'' + '}';
}
@Override
public YggdrasilAuthenticationService getAuthenticationService() {
return (YggdrasilAuthenticationService)super.getAuthenticationService();
}
}

View file

@ -0,0 +1,21 @@
package com.mojang.authlib.yggdrasil.request;
import com.mojang.authlib.Agent;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
public class AuthenticationRequest {
private Agent agent;
private String username;
private String password;
private String clientToken;
private boolean requestUser = true;
public AuthenticationRequest(YggdrasilUserAuthentication authenticationService, String username, String password) {
this.agent = authenticationService.getAgent();
this.username = username;
this.clientToken = authenticationService.getAuthenticationService().getClientToken();
this.password = password;
}
}

View file

@ -0,0 +1,15 @@
package com.mojang.authlib.yggdrasil.request;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
public class InvalidateRequest {
private String accessToken;
private String clientToken;
public InvalidateRequest(YggdrasilUserAuthentication authenticationService) {
this.accessToken = authenticationService.getAuthenticatedToken();
this.clientToken = authenticationService.getAuthenticationService().getClientToken();
}
}

View file

@ -0,0 +1,10 @@
package com.mojang.authlib.yggdrasil.request;
import java.util.UUID;
public class JoinMinecraftServerRequest {
public String accessToken;
public UUID selectedProfile;
public String serverId;
}

View file

@ -0,0 +1,23 @@
package com.mojang.authlib.yggdrasil.request;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
public class RefreshRequest {
private String clientToken;
private String accessToken;
private GameProfile selectedProfile;
private boolean requestUser = true;
public RefreshRequest(YggdrasilUserAuthentication authenticationService) {
this(authenticationService, null);
}
public RefreshRequest(YggdrasilUserAuthentication authenticationService, GameProfile profile) {
this.clientToken = authenticationService.getAuthenticationService().getClientToken();
this.accessToken = authenticationService.getAuthenticatedToken();
this.selectedProfile = profile;
}
}

View file

@ -0,0 +1,15 @@
package com.mojang.authlib.yggdrasil.request;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
public class ValidateRequest {
private String clientToken;
private String accessToken;
public ValidateRequest(YggdrasilUserAuthentication authenticationService) {
this.clientToken = authenticationService.getAuthenticationService().getClientToken();
this.accessToken = authenticationService.getAuthenticatedToken();
}
}

View file

@ -0,0 +1,35 @@
package com.mojang.authlib.yggdrasil.response;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.yggdrasil.response.Response;
import com.mojang.authlib.yggdrasil.response.User;
public class AuthenticationResponse
extends Response {
private String accessToken;
private String clientToken;
private GameProfile selectedProfile;
private GameProfile[] availableProfiles;
private User user;
public String getAccessToken() {
return this.accessToken;
}
public String getClientToken() {
return this.clientToken;
}
public GameProfile[] getAvailableProfiles() {
return this.availableProfiles;
}
public GameProfile getSelectedProfile() {
return this.selectedProfile;
}
public User getUser() {
return this.user;
}
}

View file

@ -0,0 +1,20 @@
package com.mojang.authlib.yggdrasil.response;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.yggdrasil.response.Response;
import java.util.UUID;
public class HasJoinedMinecraftServerResponse
extends Response {
private UUID id;
private PropertyMap properties;
public UUID getId() {
return this.id;
}
public PropertyMap getProperties() {
return this.properties;
}
}

View file

@ -0,0 +1,25 @@
package com.mojang.authlib.yggdrasil.response;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.yggdrasil.response.Response;
import java.util.UUID;
public class MinecraftProfilePropertiesResponse
extends Response {
private UUID id;
private String name;
private PropertyMap properties;
public UUID getId() {
return this.id;
}
public String getName() {
return this.name;
}
public PropertyMap getProperties() {
return this.properties;
}
}

View file

@ -0,0 +1,34 @@
package com.mojang.authlib.yggdrasil.response;
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
import java.util.Map;
import java.util.UUID;
public class MinecraftTexturesPayload {
private long timestamp;
private UUID profileId;
private String profileName;
private boolean isPublic;
private Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> textures;
public long getTimestamp() {
return this.timestamp;
}
public UUID getProfileId() {
return this.profileId;
}
public String getProfileName() {
return this.profileName;
}
public boolean isPublic() {
return this.isPublic;
}
public Map<MinecraftProfileTexture.Type, MinecraftProfileTexture> getTextures() {
return this.textures;
}
}

View file

@ -0,0 +1,45 @@
package com.mojang.authlib.yggdrasil.response;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.yggdrasil.response.Response;
import java.lang.reflect.Type;
public class ProfileSearchResultsResponse
extends Response {
private GameProfile[] profiles;
public GameProfile[] getProfiles() {
return this.profiles;
}
public static class Serializer
implements JsonDeserializer<ProfileSearchResultsResponse> {
@Override
public ProfileSearchResultsResponse deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
ProfileSearchResultsResponse result = new ProfileSearchResultsResponse();
if (json instanceof JsonObject) {
JsonObject object = (JsonObject)json;
if (object.has("error")) {
result.setError(object.getAsJsonPrimitive("error").getAsString());
}
if (object.has("errorMessage")) {
result.setError(object.getAsJsonPrimitive("errorMessage").getAsString());
}
if (object.has("cause")) {
result.setError(object.getAsJsonPrimitive("cause").getAsString());
}
} else {
result.profiles = (GameProfile[])context.deserialize(json, (Type)((Object)GameProfile[].class));
}
return result;
}
}
}

View file

@ -0,0 +1,35 @@
package com.mojang.authlib.yggdrasil.response;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.yggdrasil.response.Response;
import com.mojang.authlib.yggdrasil.response.User;
public class RefreshResponse
extends Response {
private String accessToken;
private String clientToken;
private GameProfile selectedProfile;
private GameProfile[] availableProfiles;
private User user;
public String getAccessToken() {
return this.accessToken;
}
public String getClientToken() {
return this.clientToken;
}
public GameProfile[] getAvailableProfiles() {
return this.availableProfiles;
}
public GameProfile getSelectedProfile() {
return this.selectedProfile;
}
public User getUser() {
return this.user;
}
}

View file

@ -0,0 +1,32 @@
package com.mojang.authlib.yggdrasil.response;
public class Response {
private String error;
private String errorMessage;
private String cause;
public String getError() {
return this.error;
}
public String getCause() {
return this.cause;
}
public String getErrorMessage() {
return this.errorMessage;
}
protected void setError(String error) {
this.error = error;
}
protected void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
protected void setCause(String cause) {
this.cause = cause;
}
}

View file

@ -0,0 +1,17 @@
package com.mojang.authlib.yggdrasil.response;
import com.mojang.authlib.properties.PropertyMap;
public class User {
private String id;
private PropertyMap properties;
public String getId() {
return this.id;
}
public PropertyMap getProperties() {
return this.properties;
}
}

View file

@ -0,0 +1,65 @@
package com.mojang.launcher;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Http {
private static final Logger LOGGER = LogManager.getLogger();
private Http() {
}
public static String buildQuery(Map<String, Object> query) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, Object> entry : query.entrySet()) {
if (builder.length() > 0) {
builder.append('&');
}
try {
builder.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
}
catch (UnsupportedEncodingException e) {
LOGGER.error("Unexpected exception building query", (Throwable)e);
}
if (entry.getValue() == null) continue;
builder.append('=');
try {
builder.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
}
catch (UnsupportedEncodingException e) {
LOGGER.error("Unexpected exception building query", (Throwable)e);
}
}
return builder.toString();
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public static String performGet(URL url, Proxy proxy) throws IOException {
HttpURLConnection connection = (HttpURLConnection)url.openConnection(proxy);
connection.setConnectTimeout(15000);
connection.setReadTimeout(60000);
connection.setRequestMethod("GET");
InputStream inputStream = connection.getInputStream();
try {
String string = IOUtils.toString(inputStream);
return string;
}
finally {
IOUtils.closeQuietly(inputStream);
}
}
}

View file

@ -0,0 +1,85 @@
package com.mojang.launcher;
import com.mojang.authlib.Agent;
import com.mojang.launcher.UserInterface;
import com.mojang.launcher.updater.ExceptionalThreadPoolExecutor;
import com.mojang.launcher.updater.VersionManager;
import com.mojang.launcher.versions.ReleaseTypeFactory;
import java.io.File;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Launcher {
private static final Logger LOGGER;
private final VersionManager versionManager;
private final File workingDirectory;
private final UserInterface ui;
private final Proxy proxy;
private final PasswordAuthentication proxyAuth;
private final ThreadPoolExecutor downloaderExecutorService = new ExceptionalThreadPoolExecutor(16, 16, 30L, TimeUnit.SECONDS);
private final Agent agent;
private final ReleaseTypeFactory releaseTypeFactory;
private final int launcherFormatVersion;
public Launcher(UserInterface ui, File workingDirectory, Proxy proxy, PasswordAuthentication proxyAuth, VersionManager versionManager, Agent agent, ReleaseTypeFactory releaseTypeFactory, int launcherFormatVersion) {
this.ui = ui;
this.proxy = proxy;
this.proxyAuth = proxyAuth;
this.workingDirectory = workingDirectory;
this.agent = agent;
this.versionManager = versionManager;
this.releaseTypeFactory = releaseTypeFactory;
this.launcherFormatVersion = launcherFormatVersion;
this.downloaderExecutorService.allowCoreThreadTimeOut(true);
}
public ReleaseTypeFactory getReleaseTypeFactory() {
return this.releaseTypeFactory;
}
public VersionManager getVersionManager() {
return this.versionManager;
}
public File getWorkingDirectory() {
return this.workingDirectory;
}
public UserInterface getUserInterface() {
return this.ui;
}
public Proxy getProxy() {
return this.proxy;
}
public PasswordAuthentication getProxyAuth() {
return this.proxyAuth;
}
public ThreadPoolExecutor getDownloaderExecutorService() {
return this.downloaderExecutorService;
}
public void shutdownLauncher() {
this.getUserInterface().shutdownLauncher();
}
public Agent getAgent() {
return this.agent;
}
public int getLauncherFormatVersion() {
return this.launcherFormatVersion;
}
static {
Thread.currentThread().setContextClassLoader(Launcher.class.getClassLoader());
LOGGER = LogManager.getLogger();
}
}

View file

@ -0,0 +1,30 @@
package com.mojang.launcher;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Set;
public class LegacyPropertyMapSerializer
implements JsonSerializer<PropertyMap> {
@Override
public JsonElement serialize(PropertyMap src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject result = new JsonObject();
for (String key : src.keySet()) {
JsonArray values = new JsonArray();
for (Property property : src.get(key)) {
values.add(new JsonPrimitive(property.getValue()));
}
result.add(key, values);
}
return result;
}
}

View file

@ -0,0 +1,111 @@
package com.mojang.launcher;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public enum OperatingSystem {
LINUX("linux", "linux", "unix"),
WINDOWS("windows", "win"),
OSX("osx", "mac"),
UNKNOWN("unknown", new String[0]);
private static final Logger LOGGER;
private final String name;
private final String[] aliases;
private /* varargs */ OperatingSystem(String name, String ... aliases) {
this.name = name;
this.aliases = aliases == null ? new String[]{} : aliases;
}
public String getName() {
return this.name;
}
public String[] getAliases() {
return this.aliases;
}
public boolean isSupported() {
return this != UNKNOWN;
}
public String getJavaDir() {
String separator = System.getProperty("file.separator");
String path = System.getProperty("java.home") + separator + "bin" + separator;
if (OperatingSystem.getCurrentPlatform() == WINDOWS && new File(path + "javaw.exe").isFile()) {
return path + "javaw.exe";
}
return path + "java";
}
public static OperatingSystem getCurrentPlatform() {
String osName = System.getProperty("os.name").toLowerCase();
for (OperatingSystem os : OperatingSystem.values()) {
for (String alias : os.getAliases()) {
if (!osName.contains(alias)) continue;
return os;
}
}
return UNKNOWN;
}
public static void openLink(URI link) {
try {
Class<?> desktopClass = Class.forName("java.awt.Desktop");
Object o = desktopClass.getMethod("getDesktop", new Class[0]).invoke(null, new Object[0]);
desktopClass.getMethod("browse", URI.class).invoke(o, link);
}
catch (Throwable e) {
if (OperatingSystem.getCurrentPlatform() == OSX) {
try {
Runtime.getRuntime().exec(new String[]{"/usr/bin/open", link.toString()});
}
catch (IOException e1) {
LOGGER.error("Failed to open link " + link.toString(), (Throwable)e1);
}
}
LOGGER.error("Failed to open link " + link.toString(), e);
}
}
public static void openFolder(File path) {
String absolutePath = path.getAbsolutePath();
OperatingSystem os = OperatingSystem.getCurrentPlatform();
if (os == OSX) {
try {
Runtime.getRuntime().exec(new String[]{"/usr/bin/open", absolutePath});
return;
}
catch (IOException e) {
LOGGER.error("Couldn't open " + path + " through /usr/bin/open", (Throwable)e);
}
} else if (os == WINDOWS) {
String cmd = String.format("cmd.exe /C start \"Open file\" \"%s\"", absolutePath);
try {
Runtime.getRuntime().exec(cmd);
return;
}
catch (IOException e) {
LOGGER.error("Couldn't open " + path + " through cmd.exe", (Throwable)e);
}
}
try {
Class<?> desktopClass = Class.forName("java.awt.Desktop");
Object desktop = desktopClass.getMethod("getDesktop", new Class[0]).invoke(null, new Object[0]);
desktopClass.getMethod("browse", URI.class).invoke(desktop, path.toURI());
}
catch (Throwable e) {
LOGGER.error("Couldn't open " + path + " through Desktop.browse()", e);
}
}
static {
LOGGER = LogManager.getLogger();
}
}

View file

@ -0,0 +1,7 @@
package com.mojang.launcher;
public class SharedLauncherConstants {
public static final int VERSION_FORMAT = 14;
public static final String DEFAULT_VERSION_INCOMPATIBILITY_REASON = "This version is incompatible with your computer. Please try another one by going into Edit Profile and selecting one through the dropdown. Sorry!";
}

View file

@ -0,0 +1,24 @@
package com.mojang.launcher;
import com.mojang.launcher.updater.DownloadProgress;
import com.mojang.launcher.versions.CompleteVersion;
import java.io.File;
public interface UserInterface {
public void showLoginPrompt();
public void setVisible(boolean var1);
public void shutdownLauncher();
public void hideDownloadProgress();
public void setDownloadProgress(DownloadProgress var1);
public void showCrashReport(CompleteVersion var1, File var2, String var3);
public void gameLaunchFailure(String var1);
public void updatePlayState();
}

View file

@ -0,0 +1,8 @@
package com.mojang.launcher.events;
import com.mojang.launcher.game.process.GameProcess;
public interface GameOutputLogProcessor {
public void onGameOutput(GameProcess var1, String var2);
}

View file

@ -0,0 +1,8 @@
package com.mojang.launcher.events;
import com.mojang.launcher.updater.VersionManager;
public interface RefreshedVersionsListener {
public void onVersionsRefreshed(VersionManager var1);
}

View file

@ -0,0 +1,25 @@
package com.mojang.launcher.game;
public enum GameInstanceStatus {
PREPARING("Preparing..."),
DOWNLOADING("Downloading..."),
INSTALLING("Installing..."),
LAUNCHING("Launching..."),
PLAYING("Playing..."),
IDLE("Idle");
private final String name;
private GameInstanceStatus(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
return this.name;
}
}

View file

@ -0,0 +1,42 @@
package com.mojang.launcher.game.process;
import com.google.common.base.Predicate;
import com.mojang.launcher.game.process.GameProcess;
import com.mojang.launcher.game.process.GameProcessRunnable;
import java.util.List;
public abstract class AbstractGameProcess
implements GameProcess {
protected final List<String> arguments;
protected final Predicate<String> sysOutFilter;
private GameProcessRunnable onExit;
public AbstractGameProcess(List<String> arguments, Predicate<String> sysOutFilter) {
this.arguments = arguments;
this.sysOutFilter = sysOutFilter;
}
@Override
public Predicate<String> getSysOutFilter() {
return this.sysOutFilter;
}
@Override
public List<String> getStartupArguments() {
return this.arguments;
}
@Override
public void setExitRunnable(GameProcessRunnable runnable) {
this.onExit = runnable;
if (!this.isRunning() && runnable != null) {
runnable.onGameProcessEnded(this);
}
}
@Override
public GameProcessRunnable getExitRunnable() {
return this.onExit;
}
}

View file

@ -0,0 +1,25 @@
package com.mojang.launcher.game.process;
import com.google.common.base.Predicate;
import com.mojang.launcher.game.process.GameProcessRunnable;
import java.util.Collection;
import java.util.List;
public interface GameProcess {
public List<String> getStartupArguments();
public Collection<String> getSysOutLines();
public Predicate<String> getSysOutFilter();
public boolean isRunning();
public void setExitRunnable(GameProcessRunnable var1);
public GameProcessRunnable getExitRunnable();
public int getExitCode();
public void stop();
}

View file

@ -0,0 +1,86 @@
package com.mojang.launcher.game.process;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.mojang.launcher.OperatingSystem;
import com.mojang.launcher.events.GameOutputLogProcessor;
import com.mojang.launcher.game.process.GameProcess;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public class GameProcessBuilder {
private final String processPath;
private final List<String> arguments = Lists.newArrayList();
private Predicate<String> sysOutFilter = Predicates.alwaysTrue();
private GameOutputLogProcessor logProcessor = new GameOutputLogProcessor(){
@Override
public void onGameOutput(GameProcess process, String logLine) {
}
};
private File directory;
public GameProcessBuilder(String processPath) {
if (processPath == null) {
processPath = OperatingSystem.getCurrentPlatform().getJavaDir();
}
this.processPath = processPath;
}
public List<String> getFullCommands() {
ArrayList<String> result = new ArrayList<String>(this.arguments);
result.add(0, this.getProcessPath());
return result;
}
public /* varargs */ GameProcessBuilder withArguments(String ... commands) {
this.arguments.addAll(Arrays.asList(commands));
return this;
}
public List<String> getArguments() {
return this.arguments;
}
public GameProcessBuilder directory(File directory) {
this.directory = directory;
return this;
}
public File getDirectory() {
return this.directory;
}
public GameProcessBuilder withSysOutFilter(Predicate<String> predicate) {
this.sysOutFilter = predicate;
return this;
}
public GameProcessBuilder withLogProcessor(GameOutputLogProcessor logProcessor) {
this.logProcessor = logProcessor;
return this;
}
public Predicate<String> getSysOutFilter() {
return this.sysOutFilter;
}
protected String getProcessPath() {
return this.processPath;
}
public GameOutputLogProcessor getLogProcessor() {
return this.logProcessor;
}
public String toString() {
return Objects.toStringHelper(this).add("processPath", this.processPath).add("arguments", this.arguments).add("sysOutFilter", this.sysOutFilter).add("directory", this.directory).add("logProcessor", this.logProcessor).toString();
}
}

View file

@ -0,0 +1,10 @@
package com.mojang.launcher.game.process;
import com.mojang.launcher.game.process.GameProcess;
import com.mojang.launcher.game.process.GameProcessBuilder;
import java.io.IOException;
public interface GameProcessFactory {
public GameProcess startGame(GameProcessBuilder var1) throws IOException;
}

View file

@ -0,0 +1,8 @@
package com.mojang.launcher.game.process;
import com.mojang.launcher.game.process.GameProcess;
public interface GameProcessRunnable {
public void onGameProcessEnded(GameProcess var1);
}

View file

@ -0,0 +1,66 @@
package com.mojang.launcher.game.process.direct;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.EvictingQueue;
import com.mojang.launcher.events.GameOutputLogProcessor;
import com.mojang.launcher.game.process.AbstractGameProcess;
import com.mojang.launcher.game.process.direct.DirectProcessInputMonitor;
import java.util.Collection;
import java.util.List;
public class DirectGameProcess
extends AbstractGameProcess {
private static final int MAX_SYSOUT_LINES = 5;
private final Process process;
protected final DirectProcessInputMonitor monitor;
private final Collection<String> sysOutLines = EvictingQueue.create(5);
public DirectGameProcess(List<String> commands, Process process, Predicate<String> sysOutFilter, GameOutputLogProcessor logProcessor) {
super(commands, sysOutFilter);
this.process = process;
this.monitor = new DirectProcessInputMonitor(this, logProcessor);
this.monitor.start();
}
public Process getRawProcess() {
return this.process;
}
@Override
public Collection<String> getSysOutLines() {
return this.sysOutLines;
}
@Override
public boolean isRunning() {
try {
this.process.exitValue();
}
catch (IllegalThreadStateException ex) {
return true;
}
return false;
}
@Override
public int getExitCode() {
try {
return this.process.exitValue();
}
catch (IllegalThreadStateException ex) {
ex.fillInStackTrace();
throw ex;
}
}
public String toString() {
return Objects.toStringHelper(this).add("process", this.process).add("monitor", this.monitor).toString();
}
@Override
public void stop() {
this.process.destroy();
}
}

View file

@ -0,0 +1,21 @@
package com.mojang.launcher.game.process.direct;
import com.google.common.base.Predicate;
import com.mojang.launcher.events.GameOutputLogProcessor;
import com.mojang.launcher.game.process.GameProcess;
import com.mojang.launcher.game.process.GameProcessBuilder;
import com.mojang.launcher.game.process.GameProcessFactory;
import com.mojang.launcher.game.process.direct.DirectGameProcess;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class DirectGameProcessFactory
implements GameProcessFactory {
@Override
public GameProcess startGame(GameProcessBuilder builder) throws IOException {
List<String> full = builder.getFullCommands();
return new DirectGameProcess(full, new ProcessBuilder(full).directory(builder.getDirectory()).redirectErrorStream(true).start(), builder.getSysOutFilter(), builder.getLogProcessor());
}
}

View file

@ -0,0 +1,58 @@
package com.mojang.launcher.game.process.direct;
import com.google.common.base.Predicate;
import com.mojang.launcher.events.GameOutputLogProcessor;
import com.mojang.launcher.game.process.GameProcess;
import com.mojang.launcher.game.process.GameProcessRunnable;
import com.mojang.launcher.game.process.direct.DirectGameProcess;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class DirectProcessInputMonitor
extends Thread {
private static final Logger LOGGER = LogManager.getLogger();
private final DirectGameProcess process;
private final GameOutputLogProcessor logProcessor;
public DirectProcessInputMonitor(DirectGameProcess process, GameOutputLogProcessor logProcessor) {
this.process = process;
this.logProcessor = logProcessor;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public void run() {
InputStreamReader reader = new InputStreamReader(this.process.getRawProcess().getInputStream());
BufferedReader buf = new BufferedReader(reader);
String line = null;
while (this.process.isRunning()) {
try {
while ((line = buf.readLine()) != null) {
this.logProcessor.onGameOutput(this.process, line);
if (this.process.getSysOutFilter().apply(line) != Boolean.TRUE.booleanValue()) continue;
this.process.getSysOutLines().add(line);
}
}
catch (IOException ex) {
LOGGER.error(ex);
}
finally {
IOUtils.closeQuietly(reader);
}
}
GameProcessRunnable onExit = this.process.getExitRunnable();
if (onExit != null) {
onExit.onGameProcessEnded(this.process);
}
}
}

View file

@ -0,0 +1,238 @@
package com.mojang.launcher.game.runner;
import com.google.common.collect.Lists;
import com.mojang.launcher.Launcher;
import com.mojang.launcher.UserInterface;
import com.mojang.launcher.game.GameInstanceStatus;
import com.mojang.launcher.game.runner.GameRunner;
import com.mojang.launcher.game.runner.GameRunnerListener;
import com.mojang.launcher.updater.DownloadProgress;
import com.mojang.launcher.updater.VersionManager;
import com.mojang.launcher.updater.VersionSyncInfo;
import com.mojang.launcher.updater.download.DownloadJob;
import com.mojang.launcher.updater.download.DownloadListener;
import com.mojang.launcher.updater.download.Downloadable;
import com.mojang.launcher.updater.download.ProgressContainer;
import com.mojang.launcher.versions.CompleteVersion;
import com.mojang.launcher.versions.Version;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class AbstractGameRunner
implements GameRunner,
DownloadListener {
protected static final Logger LOGGER = LogManager.getLogger();
protected final Object lock = new Object();
private final List<DownloadJob> jobs = new ArrayList<DownloadJob>();
protected CompleteVersion version;
private GameInstanceStatus status = GameInstanceStatus.IDLE;
private final List<GameRunnerListener> listeners = Lists.newArrayList();
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
protected void setStatus(GameInstanceStatus status) {
Object object = this.lock;
synchronized (object) {
this.status = status;
for (GameRunnerListener listener : Lists.newArrayList(this.listeners)) {
listener.onGameInstanceChangedState(this, status);
}
}
}
protected abstract Launcher getLauncher();
@Override
public GameInstanceStatus getStatus() {
return this.status;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public void playGame(VersionSyncInfo syncInfo) {
Object object = this.lock;
synchronized (object) {
if (this.getStatus() != GameInstanceStatus.IDLE) {
LOGGER.warn("Tried to play game but game is already starting!");
return;
}
this.setStatus(GameInstanceStatus.PREPARING);
}
LOGGER.info("Getting syncinfo for selected version");
if (syncInfo == null) {
LOGGER.warn("Tried to launch a version without a version being selected...");
this.setStatus(GameInstanceStatus.IDLE);
return;
}
object = this.lock;
synchronized (object) {
LOGGER.info("Queueing library & version downloads");
try {
this.version = this.getLauncher().getVersionManager().getLatestCompleteVersion(syncInfo);
}
catch (IOException e) {
LOGGER.error("Couldn't get complete version info for " + syncInfo.getLatestVersion(), (Throwable)e);
this.setStatus(GameInstanceStatus.IDLE);
return;
}
if (syncInfo.getRemoteVersion() != null && syncInfo.getLatestSource() != VersionSyncInfo.VersionSource.REMOTE && !this.version.isSynced()) {
try {
syncInfo = this.getLauncher().getVersionManager().syncVersion(syncInfo);
this.version = this.getLauncher().getVersionManager().getLatestCompleteVersion(syncInfo);
}
catch (IOException e) {
LOGGER.error("Couldn't sync local and remote versions", (Throwable)e);
}
this.version.setSynced(true);
}
if (!this.version.appliesToCurrentEnvironment()) {
String reason = this.version.getIncompatibilityReason();
if (reason == null) {
reason = "This version is incompatible with your computer. Please try another one by going into Edit Profile and selecting one through the dropdown. Sorry!";
}
LOGGER.error("Version " + this.version.getId() + " is incompatible with current environment: " + reason);
this.getLauncher().getUserInterface().gameLaunchFailure(reason);
this.setStatus(GameInstanceStatus.IDLE);
return;
}
if (this.version.getMinimumLauncherVersion() > this.getLauncher().getLauncherFormatVersion()) {
LOGGER.error("An update to your launcher is available and is required to play " + this.version.getId() + ". Please restart your launcher.");
this.setStatus(GameInstanceStatus.IDLE);
return;
}
if (!syncInfo.isUpToDate()) {
try {
this.getLauncher().getVersionManager().installVersion(this.version);
}
catch (IOException e) {
LOGGER.error("Couldn't save version info to install " + syncInfo.getLatestVersion(), (Throwable)e);
this.setStatus(GameInstanceStatus.IDLE);
return;
}
}
this.setStatus(GameInstanceStatus.DOWNLOADING);
this.downloadRequiredFiles(syncInfo);
}
}
protected void downloadRequiredFiles(VersionSyncInfo syncInfo) {
try {
DownloadJob librariesJob = new DownloadJob("Version & Libraries", false, this);
this.addJob(librariesJob);
this.getLauncher().getVersionManager().downloadVersion(syncInfo, librariesJob);
librariesJob.startDownloading(this.getLauncher().getDownloaderExecutorService());
DownloadJob resourceJob = new DownloadJob("Resources", true, this);
this.addJob(resourceJob);
this.getLauncher().getVersionManager().downloadResources(resourceJob, this.version);
resourceJob.startDownloading(this.getLauncher().getDownloaderExecutorService());
}
catch (IOException e) {
LOGGER.error("Couldn't get version info for " + syncInfo.getLatestVersion(), (Throwable)e);
this.setStatus(GameInstanceStatus.IDLE);
}
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
protected void updateProgressBar() {
Object object = this.lock;
synchronized (object) {
if (this.hasRemainingJobs()) {
long total = 0L;
long current = 0L;
Downloadable longestRunning = null;
for (DownloadJob job : this.jobs) {
for (Downloadable file : job.getAllFiles()) {
total += file.getMonitor().getTotal();
current += file.getMonitor().getCurrent();
if (longestRunning != null && longestRunning.getEndTime() <= 0L && (file.getStartTime() >= longestRunning.getStartTime() || file.getEndTime() != 0L)) continue;
longestRunning = file;
}
}
this.getLauncher().getUserInterface().setDownloadProgress(new DownloadProgress(current, total, longestRunning == null ? null : longestRunning.getStatus()));
} else {
this.jobs.clear();
this.getLauncher().getUserInterface().hideDownloadProgress();
}
}
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public boolean hasRemainingJobs() {
Object object = this.lock;
synchronized (object) {
for (DownloadJob job : this.jobs) {
if (job.isComplete()) continue;
return true;
}
}
return false;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public void addJob(DownloadJob job) {
Object object = this.lock;
synchronized (object) {
this.jobs.add(job);
}
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public void onDownloadJobFinished(DownloadJob job) {
this.updateProgressBar();
Object object = this.lock;
synchronized (object) {
if (job.getFailures() > 0) {
LOGGER.error("Job '" + job.getName() + "' finished with " + job.getFailures() + " failure(s)! (took " + job.getStopWatch().toString() + ")");
this.setStatus(GameInstanceStatus.IDLE);
} else {
LOGGER.info("Job '" + job.getName() + "' finished successfully (took " + job.getStopWatch().toString() + ")");
if (this.getStatus() != GameInstanceStatus.IDLE && !this.hasRemainingJobs()) {
try {
this.setStatus(GameInstanceStatus.LAUNCHING);
this.launchGame();
}
catch (Throwable ex) {
LOGGER.fatal("Fatal error launching game. Report this to http://bugs.mojang.com please!", ex);
}
}
}
}
}
protected abstract void launchGame() throws IOException;
@Override
public void onDownloadJobProgressChanged(DownloadJob job) {
this.updateProgressBar();
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public void addListener(GameRunnerListener listener) {
Object object = this.lock;
synchronized (object) {
this.listeners.add(listener);
}
}
}

View file

@ -0,0 +1,16 @@
package com.mojang.launcher.game.runner;
import com.mojang.launcher.game.GameInstanceStatus;
import com.mojang.launcher.updater.VersionSyncInfo;
import com.mojang.launcher.updater.download.DownloadJob;
public interface GameRunner {
public GameInstanceStatus getStatus();
public void playGame(VersionSyncInfo var1);
public boolean hasRemainingJobs();
public void addJob(DownloadJob var1);
}

View file

@ -0,0 +1,9 @@
package com.mojang.launcher.game.runner;
import com.mojang.launcher.game.GameInstanceStatus;
import com.mojang.launcher.game.runner.GameRunner;
public interface GameRunnerListener {
public void onGameInstanceChangedState(GameRunner var1, GameInstanceStatus var2);
}

View file

@ -0,0 +1,82 @@
package com.mojang.launcher.updater;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.JsonSyntaxException;
import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class DateTypeAdapter
implements JsonSerializer<Date>,
JsonDeserializer<Date> {
private final DateFormat enUsFormat = DateFormat.getDateTimeInstance(2, 2, Locale.US);
private final DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
@Override
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
if (!(json instanceof JsonPrimitive)) {
throw new JsonParseException("The date should be a string value");
}
Date date = this.deserializeToDate(json.getAsString());
if (typeOfT == Date.class) {
return date;
}
throw new IllegalArgumentException(this.getClass() + " cannot deserialize to " + typeOfT);
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
DateFormat dateFormat = this.enUsFormat;
synchronized (dateFormat) {
return new JsonPrimitive(this.serializeToString(src));
}
}
public Date deserializeToDate(String string) {
DateFormat dateFormat = this.enUsFormat;
synchronized (dateFormat) {
try {
return this.enUsFormat.parse(string);
}
catch (ParseException parseException) {
try {
return this.iso8601Format.parse(string);
}
catch (ParseException parseException2) {
try {
String cleaned = string.replace("Z", "+00:00");
cleaned = cleaned.substring(0, 22) + cleaned.substring(23);
return this.iso8601Format.parse(cleaned);
}
catch (Exception e) {
throw new JsonSyntaxException("Invalid date: " + string, e);
}
}
}
}
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public String serializeToString(Date date) {
DateFormat dateFormat = this.enUsFormat;
synchronized (dateFormat) {
String result = this.iso8601Format.format(date);
return result.substring(0, 22) + ":" + result.substring(22);
}
}
}

View file

@ -0,0 +1,32 @@
package com.mojang.launcher.updater;
public class DownloadProgress {
private final long current;
private final long total;
private final float percent;
private final String status;
public DownloadProgress(long current, long total, String status) {
this.current = current;
this.total = total;
this.percent = (float)current / (float)total;
this.status = status;
}
public long getCurrent() {
return this.current;
}
public long getTotal() {
return this.total;
}
public float getPercent() {
return this.percent;
}
public String getStatus() {
return this.status;
}
}

View file

@ -0,0 +1,80 @@
package com.mojang.launcher.updater;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ExceptionalThreadPoolExecutor
extends ThreadPoolExecutor {
private static final Logger LOGGER = LogManager.getLogger();
public ExceptionalThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new LinkedBlockingQueue<Runnable>(), new ThreadFactoryBuilder().setDaemon(true).build());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future) {
try {
Future future = (Future)((Object)r);
if (future.isDone()) {
future.get();
}
}
catch (CancellationException ce) {
t = ce;
}
catch (ExecutionException ee) {
t = ee.getCause();
}
catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
@Override
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new ExceptionalFutureTask<T>(runnable, value);
}
@Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new ExceptionalFutureTask<T>(callable);
}
public class ExceptionalFutureTask<T>
extends FutureTask<T> {
public ExceptionalFutureTask(Callable<T> callable) {
super(callable);
}
public ExceptionalFutureTask(Runnable runnable, T result) {
super(runnable, result);
}
@Override
protected void done() {
try {
this.get();
}
catch (Throwable t) {
LOGGER.error("Unhandled exception in executor " + this, t);
}
}
}
}

View file

@ -0,0 +1,29 @@
package com.mojang.launcher.updater;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.File;
import java.io.IOException;
public class FileTypeAdapter
extends TypeAdapter<File> {
@Override
public void write(JsonWriter out, File value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(value.getAbsolutePath());
}
}
@Override
public File read(JsonReader in) throws IOException {
if (in.hasNext()) {
String name = in.nextString();
return name != null ? new File(name) : null;
}
return null;
}
}

View file

@ -0,0 +1,53 @@
package com.mojang.launcher.updater;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class LowerCaseEnumTypeAdapterFactory
implements TypeAdapterFactory {
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> type) {
final Class<T> rawType = (Class<T>) type.getRawType();
if (!rawType.isEnum()) {
return null;
}
final Map<String, T> lowercaseToConstant = new HashMap<String, T>();
for (final T constant : rawType.getEnumConstants()) {
lowercaseToConstant.put(this.toLowercase(constant), constant);
}
return new TypeAdapter<T>() {
@Override
public void write(final JsonWriter out, final T value) throws IOException {
if (value == null) {
out.nullValue();
} else {
out.value(LowerCaseEnumTypeAdapterFactory.this.toLowercase(value));
}
}
@Override
public T read(final JsonReader reader) throws IOException {
if (reader.peek() == JsonToken.NULL) {
reader.nextNull();
return null;
}
return lowercaseToConstant.get(reader.nextString());
}
};
}
private String toLowercase(Object o) {
return o.toString().toLowerCase(Locale.US);
}
}

View file

@ -0,0 +1,53 @@
package com.mojang.launcher.updater;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.mojang.launcher.versions.ReleaseType;
import com.mojang.launcher.versions.ReleaseTypeFactory;
import java.util.Collections;
import java.util.Set;
public class VersionFilter<T extends ReleaseType> {
private final Set<T> types = Sets.newHashSet();
private int maxCount = 5;
public VersionFilter(ReleaseTypeFactory<T> factory) {
Iterables.addAll(this.types, factory);
}
public Set<T> getTypes() {
return this.types;
}
public /* varargs */ VersionFilter<T> onlyForTypes(T ... types) {
this.types.clear();
this.includeTypes(types);
return this;
}
public /* varargs */ VersionFilter<T> includeTypes(T ... types) {
if (types != null) {
Collections.addAll(this.types, types);
}
return this;
}
public /* varargs */ VersionFilter<T> excludeTypes(T ... types) {
if (types != null) {
for (T type : types) {
this.types.remove(type);
}
}
return this;
}
public int getMaxCount() {
return this.maxCount;
}
public VersionFilter<T> setMaxCount(int maxCount) {
this.maxCount = maxCount;
return this;
}
}

View file

@ -0,0 +1,47 @@
package com.mojang.launcher.updater;
import com.mojang.launcher.events.RefreshedVersionsListener;
import com.mojang.launcher.updater.VersionFilter;
import com.mojang.launcher.updater.VersionSyncInfo;
import com.mojang.launcher.updater.download.DownloadJob;
import com.mojang.launcher.versions.CompleteVersion;
import com.mojang.launcher.versions.ReleaseType;
import com.mojang.launcher.versions.Version;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
public interface VersionManager {
public void refreshVersions() throws IOException;
public List<VersionSyncInfo> getVersions();
public List<VersionSyncInfo> getVersions(VersionFilter<? extends ReleaseType> var1);
public VersionSyncInfo getVersionSyncInfo(Version var1);
public VersionSyncInfo getVersionSyncInfo(String var1);
public VersionSyncInfo getVersionSyncInfo(Version var1, Version var2);
public List<VersionSyncInfo> getInstalledVersions();
public CompleteVersion getLatestCompleteVersion(VersionSyncInfo var1) throws IOException;
public DownloadJob downloadVersion(VersionSyncInfo var1, DownloadJob var2) throws IOException;
public DownloadJob downloadResources(DownloadJob var1, CompleteVersion var2) throws IOException;
public ThreadPoolExecutor getExecutorService();
public void addRefreshedVersionsListener(RefreshedVersionsListener var1);
public void removeRefreshedVersionsListener(RefreshedVersionsListener var1);
public VersionSyncInfo syncVersion(VersionSyncInfo var1) throws IOException;
public void installVersion(CompleteVersion var1) throws IOException;
public void uninstallVersion(CompleteVersion var1) throws IOException;
}

View file

@ -0,0 +1,73 @@
package com.mojang.launcher.updater;
import com.mojang.launcher.versions.Version;
import java.util.Date;
public class VersionSyncInfo {
private final Version localVersion;
private final Version remoteVersion;
private final boolean isInstalled;
private final boolean isUpToDate;
public VersionSyncInfo(Version localVersion, Version remoteVersion, boolean installed, boolean upToDate) {
this.localVersion = localVersion;
this.remoteVersion = remoteVersion;
this.isInstalled = installed;
this.isUpToDate = upToDate;
}
public Version getLocalVersion() {
return this.localVersion;
}
public Version getRemoteVersion() {
return this.remoteVersion;
}
public Version getLatestVersion() {
if (this.getLatestSource() == VersionSource.REMOTE) {
return this.remoteVersion;
}
return this.localVersion;
}
public VersionSource getLatestSource() {
if (this.getLocalVersion() == null) {
return VersionSource.REMOTE;
}
if (this.getRemoteVersion() == null) {
return VersionSource.LOCAL;
}
if (this.getRemoteVersion().getUpdatedTime().after(this.getLocalVersion().getUpdatedTime())) {
return VersionSource.REMOTE;
}
return VersionSource.LOCAL;
}
public boolean isInstalled() {
return this.isInstalled;
}
public boolean isOnRemote() {
return this.remoteVersion != null;
}
public boolean isUpToDate() {
return this.isUpToDate;
}
public String toString() {
return "VersionSyncInfo{localVersion=" + this.localVersion + ", remoteVersion=" + this.remoteVersion + ", isInstalled=" + this.isInstalled + ", isUpToDate=" + this.isUpToDate + '}';
}
public static enum VersionSource {
REMOTE,
LOCAL;
private VersionSource() {
}
}
}

View file

@ -0,0 +1,102 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.Downloadable;
import com.mojang.launcher.updater.download.MonitoringInputStream;
import com.mojang.launcher.updater.download.ProgressContainer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.Charset;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
public class ChecksummedDownloadable
extends Downloadable {
private String localHash;
private String expectedHash;
public ChecksummedDownloadable(Proxy proxy, URL remoteFile, File localFile, boolean forceDownload) {
super(proxy, remoteFile, localFile, forceDownload);
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
@Override
public String download() throws IOException {
HttpURLConnection connection;
File target;
int status;
InputStream inputStream;
block19 : {
++this.numAttempts;
this.ensureFileWritable(this.getTarget());
target = this.getTarget();
if (this.localHash == null && target.isFile()) {
this.localHash = ChecksummedDownloadable.getDigest(target, "SHA-1", 40);
}
if (this.expectedHash == null) {
try {
connection = this.makeConnection(new URL(this.getUrl().toString() + ".sha1"));
status = connection.getResponseCode();
if (status / 100 == 2) {
inputStream = connection.getInputStream();
try {
this.expectedHash = IOUtils.toString(inputStream, Charsets.UTF_8).trim();
break block19;
}
catch (IOException e) {
this.expectedHash = "";
break block19;
}
finally {
IOUtils.closeQuietly(inputStream);
}
}
this.expectedHash = "";
}
catch (IOException e) {
this.expectedHash = "";
}
}
}
if (this.expectedHash.length() == 0 && target.isFile()) {
return "Couldn't find a checksum so assuming our copy is good";
}
if (this.expectedHash.equalsIgnoreCase(this.localHash)) {
return "Remote checksum matches local file";
}
try {
connection = this.makeConnection(this.getUrl());
status = connection.getResponseCode();
if (status / 100 == 2) {
this.updateExpectedSize(connection);
inputStream = new MonitoringInputStream(connection.getInputStream(), this.getMonitor());
FileOutputStream outputStream = new FileOutputStream(this.getTarget());
String digest = ChecksummedDownloadable.copyAndDigest(inputStream, outputStream, "SHA", 40);
if (this.expectedHash.length() == 0) {
return "Didn't have checksum so assuming the downloaded file is good";
}
if (this.expectedHash.equalsIgnoreCase(digest)) {
return "Downloaded successfully and checksum matched";
}
throw new RuntimeException(String.format("Checksum did not match downloaded file (Checksum was %s, downloaded %s)", this.expectedHash, digest));
}
if (this.getTarget().isFile()) {
return "Couldn't connect to server (responded with " + status + ") but have local file, assuming it's good";
}
throw new RuntimeException("Server responded with " + status);
}
catch (IOException e) {
if (this.getTarget().isFile() && (this.expectedHash == null || this.expectedHash.length() == 0)) {
return "Couldn't connect to server (" + e.getClass().getSimpleName() + ": '" + e.getMessage() + "') but have local file, assuming it's good";
}
throw e;
}
}
}

View file

@ -0,0 +1,175 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.DownloadListener;
import com.mojang.launcher.updater.download.Downloadable;
import com.mojang.launcher.updater.download.ProgressContainer;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class DownloadJob {
private static final Logger LOGGER = LogManager.getLogger();
private static final int MAX_ATTEMPTS_PER_FILE = 5;
private static final int ASSUMED_AVERAGE_FILE_SIZE = 5242880;
private final Queue<Downloadable> remainingFiles = new ConcurrentLinkedQueue<Downloadable>();
private final List<Downloadable> allFiles = Collections.synchronizedList(new ArrayList());
private final List<Downloadable> failures = Collections.synchronizedList(new ArrayList());
private final List<Downloadable> successful = Collections.synchronizedList(new ArrayList());
private final DownloadListener listener;
private final String name;
private final boolean ignoreFailures;
private final AtomicInteger remainingThreads = new AtomicInteger();
private final StopWatch stopWatch = new StopWatch();
private boolean started;
public DownloadJob(String name, boolean ignoreFailures, DownloadListener listener, Collection<Downloadable> files) {
this.name = name;
this.ignoreFailures = ignoreFailures;
this.listener = listener;
if (files != null) {
this.addDownloadables(files);
}
}
public DownloadJob(String name, boolean ignoreFailures, DownloadListener listener) {
this(name, ignoreFailures, listener, null);
}
public void addDownloadables(Collection<Downloadable> downloadables) {
if (this.started) {
throw new IllegalStateException("Cannot add to download job that has already started");
}
this.allFiles.addAll(downloadables);
this.remainingFiles.addAll(downloadables);
for (Downloadable downloadable : downloadables) {
if (downloadable.getExpectedSize() == 0L) {
downloadable.getMonitor().setTotal(0x500000L);
} else {
downloadable.getMonitor().setTotal(downloadable.getExpectedSize());
}
downloadable.getMonitor().setJob(this);
}
}
public /* varargs */ void addDownloadables(Downloadable ... downloadables) {
if (this.started) {
throw new IllegalStateException("Cannot add to download job that has already started");
}
for (Downloadable downloadable : downloadables) {
this.allFiles.add(downloadable);
this.remainingFiles.add(downloadable);
if (downloadable.getExpectedSize() == 0L) {
downloadable.getMonitor().setTotal(0x500000L);
} else {
downloadable.getMonitor().setTotal(downloadable.getExpectedSize());
}
downloadable.getMonitor().setJob(this);
}
}
public void startDownloading(ThreadPoolExecutor executorService) {
if (this.started) {
throw new IllegalStateException("Cannot start download job that has already started");
}
this.started = true;
this.stopWatch.start();
if (this.allFiles.isEmpty()) {
LOGGER.info("Download job '" + this.name + "' skipped as there are no files to download");
this.listener.onDownloadJobFinished(this);
} else {
int threads = executorService.getMaximumPoolSize();
this.remainingThreads.set(threads);
LOGGER.info("Download job '" + this.name + "' started (" + threads + " threads, " + this.allFiles.size() + " files)");
for (int i = 0; i < threads; ++i) {
executorService.submit(new Runnable(){
@Override
public void run() {
DownloadJob.this.popAndDownload();
}
});
}
}
}
private void popAndDownload() {
Downloadable downloadable;
while ((downloadable = this.remainingFiles.poll()) != null) {
if (downloadable.getStartTime() == 0L) {
downloadable.setStartTime(System.currentTimeMillis());
}
if (downloadable.getNumAttempts() > 5) {
if (!this.ignoreFailures) {
this.failures.add(downloadable);
}
LOGGER.error("Gave up trying to download " + downloadable.getUrl() + " for job '" + this.name + "'");
continue;
}
try {
LOGGER.info("Attempting to download " + downloadable.getTarget() + " for job '" + this.name + "'... (try " + downloadable.getNumAttempts() + ")");
String result = downloadable.download();
this.successful.add(downloadable);
downloadable.setEndTime(System.currentTimeMillis());
downloadable.getMonitor().setCurrent(downloadable.getMonitor().getTotal());
LOGGER.info("Finished downloading " + downloadable.getTarget() + " for job '" + this.name + "'" + ": " + result);
}
catch (Throwable t) {
LOGGER.warn("Couldn't download " + downloadable.getUrl() + " for job '" + this.name + "'", t);
downloadable.getMonitor().setCurrent(downloadable.getMonitor().getTotal());
this.remainingFiles.add(downloadable);
}
}
if (this.remainingThreads.decrementAndGet() <= 0) {
this.listener.onDownloadJobFinished(this);
}
}
public boolean shouldIgnoreFailures() {
return this.ignoreFailures;
}
public boolean isStarted() {
return this.started;
}
public boolean isComplete() {
return this.started && this.remainingFiles.isEmpty() && this.remainingThreads.get() == 0;
}
public int getFailures() {
return this.failures.size();
}
public int getSuccessful() {
return this.successful.size();
}
public String getName() {
return this.name;
}
public void updateProgress() {
this.listener.onDownloadJobProgressChanged(this);
}
public List<Downloadable> getAllFiles() {
return this.allFiles;
}
public StopWatch getStopWatch() {
return this.stopWatch;
}
}

View file

@ -0,0 +1,10 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.DownloadJob;
public interface DownloadListener {
public void onDownloadJobFinished(DownloadJob var1);
public void onDownloadJobProgressChanged(DownloadJob var1);
}

View file

@ -0,0 +1,192 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.ProgressContainer;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public abstract class Downloadable {
private static final Logger LOGGER = LogManager.getLogger();
private final URL url;
private final File target;
private final boolean forceDownload;
private final Proxy proxy;
private final ProgressContainer monitor;
private long startTime;
protected int numAttempts;
private long expectedSize;
private long endTime;
public Downloadable(Proxy proxy, URL remoteFile, File localFile, boolean forceDownload) {
this.proxy = proxy;
this.url = remoteFile;
this.target = localFile;
this.forceDownload = forceDownload;
this.monitor = new ProgressContainer();
}
public ProgressContainer getMonitor() {
return this.monitor;
}
public long getExpectedSize() {
return this.expectedSize;
}
public void setExpectedSize(long expectedSize) {
this.expectedSize = expectedSize;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public static String getDigest(File file, String algorithm, int hashLength) {
DigestInputStream stream;
stream = null;
try {
int read;
stream = new DigestInputStream(new FileInputStream(file), MessageDigest.getInstance(algorithm));
byte[] buffer = new byte[65536];
while ((read = stream.read(buffer)) > 0) {
}
Downloadable.closeSilently(stream);
}
catch (Exception ignored) {
String read = null;
return read;
}
finally {
Downloadable.closeSilently(stream);
}
return String.format("%1$0" + hashLength + "x", new BigInteger(1, stream.getMessageDigest().digest()));
}
public abstract String download() throws IOException;
protected void updateExpectedSize(HttpURLConnection connection) {
if (this.expectedSize == 0L) {
this.monitor.setTotal(connection.getContentLength());
this.setExpectedSize(connection.getContentLength());
} else {
this.monitor.setTotal(this.expectedSize);
}
}
protected HttpURLConnection makeConnection(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection)url.openConnection(this.proxy);
connection.setUseCaches(false);
connection.setDefaultUseCaches(false);
connection.setRequestProperty("Cache-Control", "no-store,max-age=0,no-cache");
connection.setRequestProperty("Expires", "0");
connection.setRequestProperty("Pragma", "no-cache");
connection.setConnectTimeout(5000);
connection.setReadTimeout(30000);
return connection;
}
public URL getUrl() {
return this.url;
}
public File getTarget() {
return this.target;
}
public boolean shouldIgnoreLocal() {
return this.forceDownload;
}
public int getNumAttempts() {
return this.numAttempts;
}
public Proxy getProxy() {
return this.proxy;
}
public static void closeSilently(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
}
catch (IOException iOException) {
// empty catch block
}
}
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
public static String copyAndDigest(InputStream inputStream, OutputStream outputStream, String algorithm, int hashLength) throws IOException {
MessageDigest digest;
try {
digest = MessageDigest.getInstance(algorithm);
}
catch (NoSuchAlgorithmException e) {
Downloadable.closeSilently(inputStream);
Downloadable.closeSilently(outputStream);
throw new RuntimeException("Missing Digest." + algorithm, e);
}
byte[] buffer = new byte[65536];
try {
int read = inputStream.read(buffer);
while (read >= 1) {
digest.update(buffer, 0, read);
outputStream.write(buffer, 0, read);
read = inputStream.read(buffer);
}
}
finally {
Downloadable.closeSilently(inputStream);
Downloadable.closeSilently(outputStream);
}
return String.format("%1$0" + hashLength + "x", new BigInteger(1, digest.digest()));
}
protected void ensureFileWritable(File target) {
if (target.getParentFile() != null && !target.getParentFile().isDirectory()) {
LOGGER.info("Making directory " + target.getParentFile());
if (!target.getParentFile().mkdirs() && !target.getParentFile().isDirectory()) {
throw new RuntimeException("Could not create directory " + target.getParentFile());
}
}
if (target.isFile() && !target.canWrite()) {
throw new RuntimeException("Do not have write permissions for " + target + " - aborting!");
}
}
public long getStartTime() {
return this.startTime;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public String getStatus() {
return "Downloading " + this.getTarget().getName();
}
public long getEndTime() {
return this.endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
}

View file

@ -0,0 +1,75 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.Downloadable;
import com.mojang.launcher.updater.download.MonitoringInputStream;
import com.mojang.launcher.updater.download.ProgressContainer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URL;
public class EtagDownloadable
extends Downloadable {
public EtagDownloadable(Proxy proxy, URL remoteFile, File localFile, boolean forceDownload) {
super(proxy, remoteFile, localFile, forceDownload);
}
@Override
public String download() throws IOException {
++this.numAttempts;
this.ensureFileWritable(this.getTarget());
try {
HttpURLConnection connection = this.makeConnection(this.getUrl());
int status = connection.getResponseCode();
if (status == 304) {
return "Used own copy as it matched etag";
}
if (status / 100 == 2) {
this.updateExpectedSize(connection);
MonitoringInputStream inputStream = new MonitoringInputStream(connection.getInputStream(), this.getMonitor());
FileOutputStream outputStream = new FileOutputStream(this.getTarget());
String md5 = EtagDownloadable.copyAndDigest(inputStream, outputStream, "MD5", 32);
String etag = EtagDownloadable.getEtag(connection.getHeaderField("ETag"));
if (etag.contains("-")) {
return "Didn't have etag so assuming our copy is good";
}
if (etag.equalsIgnoreCase(md5)) {
return "Downloaded successfully and etag matched";
}
throw new RuntimeException(String.format("E-tag did not match downloaded MD5 (ETag was %s, downloaded %s)", etag, md5));
}
if (this.getTarget().isFile()) {
return "Couldn't connect to server (responded with " + status + ") but have local file, assuming it's good";
}
throw new RuntimeException("Server responded with " + status);
}
catch (IOException e) {
if (this.getTarget().isFile()) {
return "Couldn't connect to server (" + e.getClass().getSimpleName() + ": '" + e.getMessage() + "') but have local file, assuming it's good";
}
throw e;
}
}
@Override
protected HttpURLConnection makeConnection(URL url) throws IOException {
HttpURLConnection connection = super.makeConnection(url);
if (!this.shouldIgnoreLocal() && this.getTarget().isFile()) {
connection.setRequestProperty("If-None-Match", EtagDownloadable.getDigest(this.getTarget(), "MD5", 32));
}
return connection;
}
public static String getEtag(String etag) {
if (etag == null) {
etag = "-";
} else if (etag.startsWith("\"") && etag.endsWith("\"")) {
etag = etag.substring(1, etag.length() - 1);
}
return etag;
}
}

View file

@ -0,0 +1,53 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.ProgressContainer;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class MonitoringInputStream
extends FilterInputStream {
private final ProgressContainer monitor;
public MonitoringInputStream(InputStream in, ProgressContainer monitor) {
super(in);
this.monitor = monitor;
}
@Override
public int read() throws IOException {
int result = this.in.read();
if (result >= 0) {
this.monitor.addProgress(1L);
}
return result;
}
@Override
public int read(byte[] buffer) throws IOException {
int size = this.in.read(buffer);
if (size >= 0) {
this.monitor.addProgress(size);
}
return size;
}
@Override
public int read(byte[] buffer, int off, int len) throws IOException {
int size = this.in.read(buffer, off, len);
if (size > 0) {
this.monitor.addProgress(size);
}
return size;
}
@Override
public long skip(long size) throws IOException {
long skipped = super.skip(size);
if (skipped > 0L) {
this.monitor.addProgress(skipped);
}
return skipped;
}
}

View file

@ -0,0 +1,61 @@
package com.mojang.launcher.updater.download;
import com.mojang.launcher.updater.download.DownloadJob;
public class ProgressContainer {
private long total;
private long current;
private DownloadJob job;
public DownloadJob getJob() {
return this.job;
}
public void setJob(DownloadJob job) {
this.job = job;
if (job != null) {
job.updateProgress();
}
}
public long getTotal() {
return this.total;
}
public void setTotal(long total) {
this.total = total;
if (this.job != null) {
this.job.updateProgress();
}
}
public long getCurrent() {
return this.current;
}
public void setCurrent(long current) {
this.current = current;
if (current > this.total) {
this.total = current;
}
if (this.job != null) {
this.job.updateProgress();
}
}
public void addProgress(long amount) {
this.setCurrent(this.getCurrent() + amount);
}
public float getProgress() {
if (this.total == 0L) {
return 0.0f;
}
return (float)this.current / (float)this.total;
}
public String toString() {
return "ProgressContainer{current=" + this.current + ", total=" + this.total + '}';
}
}

View file

@ -0,0 +1,143 @@
package com.mojang.launcher.updater.download.assets;
import com.mojang.launcher.updater.download.Downloadable;
import com.mojang.launcher.updater.download.MonitoringInputStream;
import com.mojang.launcher.updater.download.ProgressContainer;
import com.mojang.launcher.updater.download.assets.AssetIndex;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.zip.GZIPInputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class AssetDownloadable
extends Downloadable {
private static final Logger LOGGER = LogManager.getLogger();
private final String name;
private final AssetIndex.AssetObject asset;
private final String urlBase;
private final File destination;
private Status status = Status.DOWNLOADING;
public AssetDownloadable(Proxy proxy, String name, AssetIndex.AssetObject asset, String urlBase, File destination) throws MalformedURLException {
super(proxy, new URL(urlBase + AssetDownloadable.createPathFromHash(asset.getHash())), new File(destination, AssetDownloadable.createPathFromHash(asset.getHash())), false);
this.name = name;
this.asset = asset;
this.urlBase = urlBase;
this.destination = destination;
}
protected static String createPathFromHash(String hash) {
return hash.substring(0, 2) + "/" + hash;
}
@Override
public String download() throws IOException {
HttpURLConnection connection;
this.status = Status.DOWNLOADING;
++this.numAttempts;
File localAsset = this.getTarget();
File localCompressed = this.asset.hasCompressedAlternative() ? new File(this.destination, AssetDownloadable.createPathFromHash(this.asset.getCompressedHash())) : null;
URL remoteAsset = this.getUrl();
URL remoteCompressed = this.asset.hasCompressedAlternative() ? new URL(this.urlBase + AssetDownloadable.createPathFromHash(this.asset.getCompressedHash())) : null;
this.ensureFileWritable(localAsset);
if (localCompressed != null) {
this.ensureFileWritable(localCompressed);
}
if (localAsset.isFile()) {
if (FileUtils.sizeOf(localAsset) == this.asset.getSize()) {
return "Have local file and it's the same size; assuming it's okay!";
}
LOGGER.warn("Had local file but it was the wrong size... had {} but expected {}", FileUtils.sizeOf(localAsset), this.asset.getSize());
FileUtils.deleteQuietly(localAsset);
this.status = Status.DOWNLOADING;
}
if (localCompressed != null && localCompressed.isFile()) {
String localCompressedHash = AssetDownloadable.getDigest(localCompressed, "SHA", 40);
if (localCompressedHash.equalsIgnoreCase(this.asset.getCompressedHash())) {
return this.decompressAsset(localAsset, localCompressed);
}
LOGGER.warn("Had local compressed but it was the wrong hash... expected {} but had {}", this.asset.getCompressedHash(), localCompressedHash);
FileUtils.deleteQuietly(localCompressed);
}
if (remoteCompressed != null && localCompressed != null) {
connection = this.makeConnection(remoteCompressed);
int status = connection.getResponseCode();
if (status / 100 == 2) {
this.updateExpectedSize(connection);
MonitoringInputStream inputStream = new MonitoringInputStream(connection.getInputStream(), this.getMonitor());
FileOutputStream outputStream = new FileOutputStream(localCompressed);
String hash = AssetDownloadable.copyAndDigest(inputStream, outputStream, "SHA", 40);
if (hash.equalsIgnoreCase(this.asset.getCompressedHash())) {
return this.decompressAsset(localAsset, localCompressed);
}
FileUtils.deleteQuietly(localCompressed);
throw new RuntimeException(String.format("Hash did not match downloaded compressed asset (Expected %s, downloaded %s)", this.asset.getCompressedHash(), hash));
}
throw new RuntimeException("Server responded with " + status);
}
connection = this.makeConnection(remoteAsset);
int status = connection.getResponseCode();
if (status / 100 == 2) {
this.updateExpectedSize(connection);
MonitoringInputStream inputStream = new MonitoringInputStream(connection.getInputStream(), this.getMonitor());
FileOutputStream outputStream = new FileOutputStream(localAsset);
String hash = AssetDownloadable.copyAndDigest(inputStream, outputStream, "SHA", 40);
if (hash.equalsIgnoreCase(this.asset.getHash())) {
return "Downloaded asset and hash matched successfully";
}
FileUtils.deleteQuietly(localAsset);
throw new RuntimeException(String.format("Hash did not match downloaded asset (Expected %s, downloaded %s)", this.asset.getHash(), hash));
}
throw new RuntimeException("Server responded with " + status);
}
@Override
public String getStatus() {
return this.status.name + " " + this.name;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
protected String decompressAsset(File localAsset, File localCompressed) throws IOException {
String hash;
this.status = Status.EXTRACTING;
FileOutputStream outputStream = FileUtils.openOutputStream(localAsset);
GZIPInputStream inputStream = new GZIPInputStream(FileUtils.openInputStream(localCompressed));
try {
hash = AssetDownloadable.copyAndDigest(inputStream, outputStream, "SHA", 40);
}
finally {
IOUtils.closeQuietly(outputStream);
IOUtils.closeQuietly(inputStream);
}
this.status = Status.DOWNLOADING;
if (hash.equalsIgnoreCase(this.asset.getHash())) {
return "Had local compressed asset, unpacked successfully and hash matched";
}
FileUtils.deleteQuietly(localAsset);
throw new RuntimeException("Had local compressed asset but unpacked hash did not match (expected " + this.asset.getHash() + " but had " + hash + ")");
}
private static enum Status {
DOWNLOADING("Downloading"),
EXTRACTING("Extracting");
private final String name;
private Status(String name) {
this.name = name;
}
}
}

View file

@ -0,0 +1,98 @@
package com.mojang.launcher.updater.download.assets;
import com.google.common.collect.Maps;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class AssetIndex {
public static final String DEFAULT_ASSET_NAME = "legacy";
private Map<String, AssetObject> objects = new LinkedHashMap<String, AssetObject>();
private boolean virtual;
public Map<String, AssetObject> getFileMap() {
return this.objects;
}
public Map<AssetObject, String> getUniqueObjects() {
HashMap<AssetObject, String> result = Maps.newHashMap();
for (Map.Entry<String, AssetObject> objectEntry : this.objects.entrySet()) {
result.put(objectEntry.getValue(), objectEntry.getKey());
}
return result;
}
public boolean isVirtual() {
return this.virtual;
}
public class AssetObject {
private String hash;
private long size;
private boolean reconstruct;
private String compressedHash;
private long compressedSize;
public String getHash() {
return this.hash;
}
public long getSize() {
return this.size;
}
public boolean shouldReconstruct() {
return this.reconstruct;
}
public boolean hasCompressedAlternative() {
return this.compressedHash != null;
}
public String getCompressedHash() {
return this.compressedHash;
}
public long getCompressedSize() {
return this.compressedSize;
}
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || this.getClass() != o.getClass()) {
return false;
}
AssetObject that = (AssetObject)o;
if (this.compressedSize != that.compressedSize) {
return false;
}
if (this.reconstruct != that.reconstruct) {
return false;
}
if (this.size != that.size) {
return false;
}
if (this.compressedHash != null ? !this.compressedHash.equals(that.compressedHash) : that.compressedHash != null) {
return false;
}
if (this.hash != null ? !this.hash.equals(that.hash) : that.hash != null) {
return false;
}
return true;
}
public int hashCode() {
int result = this.hash != null ? this.hash.hashCode() : 0;
result = 31 * result + (int)(this.size ^ this.size >>> 32);
result = 31 * result + (this.reconstruct ? 1 : 0);
result = 31 * result + (this.compressedHash != null ? this.compressedHash.hashCode() : 0);
result = 31 * result + (int)(this.compressedSize ^ this.compressedSize >>> 32);
return result;
}
}
}

View file

@ -0,0 +1,108 @@
package com.mojang.launcher.versions;
import com.mojang.launcher.OperatingSystem;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CompatibilityRule {
private Action action = Action.ALLOW;
private OSRestriction os;
public CompatibilityRule() {
}
public CompatibilityRule(CompatibilityRule compatibilityRule) {
this.action = compatibilityRule.action;
if (compatibilityRule.os != null) {
this.os = new OSRestriction(compatibilityRule.os);
}
}
public Action getAppliedAction() {
if (this.os != null && !this.os.isCurrentOperatingSystem()) {
return null;
}
return this.action;
}
public Action getAction() {
return this.action;
}
public OSRestriction getOs() {
return this.os;
}
public String toString() {
return "Rule{action=" + (Object)((Object)this.action) + ", os=" + this.os + '}';
}
public static enum Action {
ALLOW,
DISALLOW;
private Action() {
}
}
public class OSRestriction {
private OperatingSystem name;
private String version;
private String arch;
public OSRestriction() {
}
public OperatingSystem getName() {
return this.name;
}
public String getVersion() {
return this.version;
}
public String getArch() {
return this.arch;
}
public OSRestriction(OSRestriction osRestriction) {
this.name = osRestriction.name;
this.version = osRestriction.version;
this.arch = osRestriction.arch;
}
public boolean isCurrentOperatingSystem() {
if (this.name != null && this.name != OperatingSystem.getCurrentPlatform()) {
return false;
}
if (this.version != null) {
try {
final Pattern pattern = Pattern.compile(this.version);
final Matcher matcher = pattern.matcher(System.getProperty("os.version"));
if (!matcher.matches()) {
return false;
}
} catch (Throwable t) {
}
}
if (this.arch != null) {
try {
final Pattern pattern = Pattern.compile(this.arch);
final Matcher matcher = pattern.matcher(System.getProperty("os.arch"));
if (!matcher.matches()) {
return false;
}
} catch (Throwable t2) {
}
}
return true;
}
public String toString() {
return "OSRestriction{name=" + (Object)((Object)this.name) + ", version='" + this.version + '\'' + ", arch='" + this.arch + '\'' + '}';
}
}
}

View file

@ -0,0 +1,31 @@
package com.mojang.launcher.versions;
import com.mojang.launcher.versions.ReleaseType;
import com.mojang.launcher.versions.Version;
import java.util.Date;
public interface CompleteVersion
extends Version {
@Override
public String getId();
@Override
public ReleaseType getType();
@Override
public Date getUpdatedTime();
@Override
public Date getReleaseTime();
public int getMinimumLauncherVersion();
public boolean appliesToCurrentEnvironment();
public String getIncompatibilityReason();
public boolean isSynced();
public void setSynced(boolean var1);
}

View file

@ -0,0 +1,39 @@
package com.mojang.launcher.versions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ExtractRules {
private List<String> exclude = new ArrayList<String>();
public ExtractRules() {
}
public /* varargs */ ExtractRules(String ... exclude) {
if (exclude != null) {
Collections.addAll(this.exclude, exclude);
}
}
public ExtractRules(ExtractRules rules) {
for (String exclude : rules.exclude) {
this.exclude.add(exclude);
}
}
public List<String> getExcludes() {
return this.exclude;
}
public boolean shouldExtract(String path) {
if (this.exclude != null) {
for (String rule : this.exclude) {
if (!path.startsWith(rule)) continue;
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,6 @@
package com.mojang.launcher.versions;
public interface ReleaseType {
public String getName();
}

View file

@ -0,0 +1,28 @@
package com.mojang.launcher.versions;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.mojang.launcher.versions.ReleaseType;
import com.mojang.launcher.versions.ReleaseTypeFactory;
import java.io.IOException;
public class ReleaseTypeAdapterFactory<T extends ReleaseType>
extends TypeAdapter<T> {
private final ReleaseTypeFactory<T> factory;
public ReleaseTypeAdapterFactory(ReleaseTypeFactory<T> factory) {
this.factory = factory;
}
@Override
public void write(JsonWriter out, T value) throws IOException {
out.value(value.getName());
}
@Override
public T read(JsonReader in) throws IOException {
return this.factory.getTypeByName(in.nextString());
}
}

View file

@ -0,0 +1,13 @@
package com.mojang.launcher.versions;
import com.mojang.launcher.versions.ReleaseType;
public interface ReleaseTypeFactory<T extends ReleaseType>
extends Iterable<T> {
public T getTypeByName(String var1);
public T[] getAllTypes();
public Class<T> getTypeClass();
}

View file

@ -0,0 +1,15 @@
package com.mojang.launcher.versions;
import com.mojang.launcher.versions.ReleaseType;
import java.util.Date;
public interface Version {
public String getId();
public ReleaseType getType();
public Date getUpdatedTime();
public Date getReleaseTime();
}

View file

@ -0,0 +1,81 @@
package com.mojang.util;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
@Plugin(name="Queue", category="Core", elementType="appender", printObject=true)
public class QueueLogAppender
extends AbstractAppender {
private static final int MAX_CAPACITY = 250;
private static final Map<String, BlockingQueue<String>> QUEUES = new HashMap<String, BlockingQueue<String>>();
private static final ReadWriteLock QUEUE_LOCK = new ReentrantReadWriteLock();
private final BlockingQueue<String> queue;
public QueueLogAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, BlockingQueue<String> queue) {
super(name, filter, layout, ignoreExceptions);
this.queue = queue;
}
@Override
public void append(LogEvent event) {
if (this.queue.size() >= 250) {
this.queue.clear();
}
this.queue.add(this.getLayout().toSerializable(event).toString());
}
@PluginFactory
public static QueueLogAppender createAppender(@PluginAttribute(value="name") String name, @PluginAttribute(value="ignoreExceptions") String ignore, @PluginElement(value="Layout") Layout<? extends Serializable> layout, @PluginElement(value="Filters") Filter filter, @PluginAttribute(value="target") String target) {
boolean ignoreExceptions = Boolean.parseBoolean(ignore);
if (name == null) {
LOGGER.error("No name provided for QueueLogAppender");
return null;
}
if (target == null) {
target = name;
}
QUEUE_LOCK.writeLock().lock();
BlockingQueue<String> queue = QUEUES.get(target);
if (queue == null) {
queue = new LinkedBlockingQueue<String>();
QUEUES.put(target, queue);
}
QUEUE_LOCK.writeLock().unlock();
if (layout == null) {
layout = PatternLayout.createLayout(null, null, null, null, false, false, null, null);
}
return new QueueLogAppender(name, filter, layout, ignoreExceptions, queue);
}
public static String getNextLogEvent(String queueName) {
QUEUE_LOCK.readLock().lock();
BlockingQueue<String> queue = QUEUES.get(queueName);
QUEUE_LOCK.readLock().unlock();
if (queue != null) {
try {
return queue.take();
}
catch (InterruptedException ignored) {
// empty catch block
}
}
return null;
}
}

View file

@ -0,0 +1,29 @@
package com.mojang.util;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.UUID;
public class UUIDTypeAdapter
extends TypeAdapter<UUID> {
@Override
public void write(JsonWriter out, UUID value) throws IOException {
out.value(UUIDTypeAdapter.fromUUID(value));
}
@Override
public UUID read(JsonReader in) throws IOException {
return UUIDTypeAdapter.fromString(in.nextString());
}
public static String fromUUID(UUID value) {
return value.toString().replace("-", "");
}
public static UUID fromString(String input) {
return UUID.fromString(input.replaceFirst("(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})", "$1-$2-$3-$4-$5"));
}
}

View file

@ -0,0 +1,20 @@
package net.minecraft.hopper;
public class Crash {
private int id;
private String jira_issue;
private String title;
public int getId() {
return this.id;
}
public String getJira_issue() {
return this.jira_issue;
}
public String getTitle() {
return this.title;
}
}

View file

@ -0,0 +1,59 @@
package net.minecraft.hopper;
import com.google.gson.Gson;
import java.io.IOException;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.hopper.PublishRequest;
import net.minecraft.hopper.PublishResponse;
import net.minecraft.hopper.Report;
import net.minecraft.hopper.Response;
import net.minecraft.hopper.SubmitRequest;
import net.minecraft.hopper.SubmitResponse;
import net.minecraft.hopper.Util;
public final class HopperService {
private static final String BASE_URL = "http://hopper.minecraft.net/crashes/";
private static final URL ROUTE_SUBMIT = Util.constantURL("http://hopper.minecraft.net/crashes/submit_report/");
private static final URL ROUTE_PUBLISH = Util.constantURL("http://hopper.minecraft.net/crashes/publish_report/");
private static final String[] INTERESTING_SYSTEM_PROPERTY_KEYS = new String[]{"os.version", "os.name", "os.arch", "java.version", "java.vendor", "sun.arch.data.model"};
private static final Gson GSON = new Gson();
public static SubmitResponse submitReport(Proxy proxy, String report, String product, String version) throws IOException {
return HopperService.submitReport(proxy, report, product, version, null);
}
public static SubmitResponse submitReport(Proxy proxy, String report, String product, String version, Map<String, String> env) throws IOException {
HashMap<String, String> environment = new HashMap<String, String>();
if (env != null) {
environment.putAll(env);
}
for (String key : INTERESTING_SYSTEM_PROPERTY_KEYS) {
String value = System.getProperty(key);
if (value == null) continue;
environment.put(key, value);
}
SubmitRequest request = new SubmitRequest(report, product, version, environment);
return HopperService.makeRequest(proxy, ROUTE_SUBMIT, request, SubmitResponse.class);
}
public static PublishResponse publishReport(Proxy proxy, Report report) throws IOException {
PublishRequest request = new PublishRequest(report);
return HopperService.makeRequest(proxy, ROUTE_PUBLISH, request, PublishResponse.class);
}
private static <T extends Response> T makeRequest(Proxy proxy, URL url, Object input, Class<T> classOfT) throws IOException {
String jsonResult = Util.performPost(url, GSON.toJson(input), proxy, "application/json", true);
Response result = (Response)GSON.fromJson(jsonResult, classOfT);
if (result == null) {
return null;
}
if (result.getError() != null) {
throw new IOException(result.getError());
}
return (T)result;
}
}

View file

@ -0,0 +1,20 @@
package net.minecraft.hopper;
public class Problem {
private String title;
private String description;
private String url;
public String getTitle() {
return this.title;
}
public String getDescription() {
return this.description;
}
public String getUrl() {
return this.url;
}
}

View file

@ -0,0 +1,14 @@
package net.minecraft.hopper;
import net.minecraft.hopper.Report;
public class PublishRequest {
private String token;
private int report_id;
public PublishRequest(Report report) {
this.report_id = report.getId();
this.token = report.getToken();
}
}

View file

@ -0,0 +1,26 @@
package net.minecraft.hopper;
import net.minecraft.hopper.Crash;
import net.minecraft.hopper.Problem;
import net.minecraft.hopper.Report;
import net.minecraft.hopper.Response;
public class PublishResponse
extends Response {
private Report report;
private Crash crash;
private Problem problem;
public Report getReport() {
return this.report;
}
public Crash getCrash() {
return this.crash;
}
public Problem getProblem() {
return this.problem;
}
}

View file

@ -0,0 +1,24 @@
package net.minecraft.hopper;
public class Report {
private int id;
private boolean published;
private String token;
public int getId() {
return this.id;
}
public boolean isPublished() {
return this.published;
}
public String getToken() {
return this.token;
}
public boolean canBePublished() {
return this.getToken() != null;
}
}

View file

@ -0,0 +1,10 @@
package net.minecraft.hopper;
public class Response {
private String error;
public String getError() {
return this.error;
}
}

Some files were not shown because too many files have changed in this diff Show more