Move authentication code to MCAuthLib

This commit is contained in:
Steveice10 2014-03-30 10:48:44 -07:00
parent fdbb8168c5
commit 91d8f0f57c
76 changed files with 54 additions and 1889 deletions

View file

@ -7,7 +7,6 @@
--------
MCProtocolLib is a simple library for communicating with a Minecraft client/server. It aims to allow people to make custom bots, clients, or servers for Minecraft easily.
The library is split into two packages, org.spacehq.mc.auth and org.spacehq.mc.protocol. The auth package contains some classes to work with Mojang's auth servers and the protocol package contains the protocol library.
<b>Example Code</b>

View file

@ -9,7 +9,11 @@ import org.spacehq.mc.protocol.ServerLoginHandler;
import org.spacehq.mc.protocol.data.game.values.entity.player.GameMode;
import org.spacehq.mc.protocol.data.game.values.setting.Difficulty;
import org.spacehq.mc.protocol.data.game.values.world.WorldType;
import org.spacehq.mc.protocol.data.message.*;
import org.spacehq.mc.protocol.data.message.ChatColor;
import org.spacehq.mc.protocol.data.message.ChatFormat;
import org.spacehq.mc.protocol.data.message.Message;
import org.spacehq.mc.protocol.data.message.MessageStyle;
import org.spacehq.mc.protocol.data.message.TextMessage;
import org.spacehq.mc.protocol.data.status.PlayerInfo;
import org.spacehq.mc.protocol.data.status.ServerStatusInfo;
import org.spacehq.mc.protocol.data.status.VersionInfo;
@ -24,6 +28,7 @@ import org.spacehq.packetlib.Server;
import org.spacehq.packetlib.Session;
import org.spacehq.packetlib.event.server.ServerAdapter;
import org.spacehq.packetlib.event.server.SessionAddedEvent;
import org.spacehq.packetlib.event.server.SessionRemovedEvent;
import org.spacehq.packetlib.event.session.DisconnectedEvent;
import org.spacehq.packetlib.event.session.PacketReceivedEvent;
import org.spacehq.packetlib.event.session.SessionAdapter;
@ -78,6 +83,15 @@ public class Test {
}
});
}
@Override
public void sessionRemoved(SessionRemovedEvent event) {
MinecraftProtocol protocol = (MinecraftProtocol) event.getSession().getPacketProtocol();
if(protocol.getMode() == ProtocolMode.GAME) {
System.out.println("Closing server.");
event.getServer().close();
}
}
});
server.bind();

13
pom.xml
View file

@ -76,22 +76,15 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
<groupId>org.spacehq</groupId>
<artifactId>mcauthlib</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<!-- Resources -->
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View file

@ -1,75 +0,0 @@
package org.spacehq.mc.auth;
import org.spacehq.mc.auth.properties.PropertyMap;
import java.util.UUID;
public class GameProfile {
private UUID id;
private String name;
private PropertyMap properties = new PropertyMap();
private boolean legacy;
public GameProfile(String id, String name) {
this(id == null || id.equals("") ? null : UUID.fromString(id), name);
}
public GameProfile(UUID id, String name) {
if(id == null && (name == null || name.equals(""))) {
throw new IllegalArgumentException("Name and ID cannot both be blank");
} else {
this.id = id;
this.name = name;
}
}
public UUID getId() {
return this.id;
}
public String getIdAsString() {
return this.id != null ? this.id.toString() : "";
}
public String getName() {
return this.name;
}
public PropertyMap getProperties() {
return this.properties;
}
public boolean isLegacy() {
return this.legacy;
}
public boolean isComplete() {
return this.id != null && this.name != null && !this.name.equals("");
}
@Override
public boolean equals(Object o) {
if(this == o) {
return true;
} else if(o != null && this.getClass() == o.getClass()) {
GameProfile that = (GameProfile) o;
return (this.id != null ? this.id.equals(that.id) : that.id == null) && (this.name != null ? this.name.equals(that.name) : that.name == null);
} else {
return false;
}
}
@Override
public int hashCode() {
int result = this.id != null ? this.id.hashCode() : 0;
result = 31 * result + (this.name != null ? this.name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "GameProfile{id=" + this.id + ", name=" + this.name + ", properties=" + this.properties + ", legacy=" + this.legacy + "}";
}
}

View file

@ -1,70 +0,0 @@
package org.spacehq.mc.auth;
import org.spacehq.mc.auth.exception.AuthenticationException;
import org.spacehq.mc.auth.exception.ProfileNotFoundException;
import org.spacehq.mc.auth.response.ProfileSearchResultsResponse;
import org.spacehq.mc.util.URLUtils;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class GameProfileRepository {
private static final String BASE_URL = "https://api.mojang.com/";
private static final String SEARCH_PAGE_URL = "https://api.mojang.com/profiles/page/";
private static final int MAX_FAIL_COUNT = 3;
private static final int DELAY_BETWEEN_PAGES = 100;
private static final int DELAY_BETWEEN_FAILURES = 750;
public void findProfilesByNames(String[] names, ProfileLookupCallback callback) {
Set<ProfileCriteria> criteria = new HashSet<ProfileCriteria>();
for(String name : names) {
if(name != null && !name.isEmpty()) {
criteria.add(new ProfileCriteria(name));
}
}
Set<ProfileCriteria> request = new HashSet<ProfileCriteria>(criteria);
int page = 1;
Exception error = null;
int failCount = 0;
while(failCount < MAX_FAIL_COUNT && !criteria.isEmpty()) {
try {
ProfileSearchResultsResponse response = (ProfileSearchResultsResponse) URLUtils.makeRequest(URLUtils.constantURL("https://api.mojang.com/profiles/page/" + page), request, ProfileSearchResultsResponse.class);
failCount = 0;
error = null;
if(response.getSize() != 0 && response.getProfiles().length != 0) {
for(GameProfile profile : response.getProfiles()) {
criteria.remove(new ProfileCriteria(profile.getName()));
callback.onProfileLookupSucceeded(profile);
}
page++;
try {
Thread.sleep(DELAY_BETWEEN_PAGES);
} catch(InterruptedException ignored) {
}
}
} catch(AuthenticationException e) {
error = e;
failCount++;
try {
Thread.sleep(DELAY_BETWEEN_FAILURES);
} catch(InterruptedException ignored) {
}
}
}
if(!criteria.isEmpty()) {
if(error == null) {
error = new ProfileNotFoundException("Server did not find the requested profile");
}
for(ProfileCriteria profileCriteria : criteria) {
callback.onProfileLookupFailed(new GameProfile((UUID) null, profileCriteria.getName()), error);
}
}
}
}

View file

@ -1,34 +0,0 @@
package org.spacehq.mc.auth;
public class ProfileCriteria {
private final String name;
public ProfileCriteria(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public boolean equals(Object o) {
if(this == o) {
return true;
} else if(o != null && this.getClass() == o.getClass()) {
ProfileCriteria that = (ProfileCriteria) o;
return this.name.toLowerCase().equals(that.name.toLowerCase());
} else {
return false;
}
}
public int hashCode() {
return 31 * this.name.toLowerCase().hashCode();
}
public String toString() {
return "GameProfileRepository{name=" + this.name + "}";
}
}

View file

@ -1,9 +0,0 @@
package org.spacehq.mc.auth;
public interface ProfileLookupCallback {
public void onProfileLookupSucceeded(GameProfile profile);
public void onProfileLookupFailed(GameProfile profile, Exception e);
}

View file

@ -1,27 +0,0 @@
package org.spacehq.mc.auth;
public class ProfileTexture {
private String url;
public ProfileTexture(String url) {
this.url = url;
}
public String getUrl() {
return this.url;
}
public String getHash() {
String url = this.url.endsWith("/") ? this.url.substring(0, this.url.length() - 1) : this.url;
int slash = url.lastIndexOf("/");
int dot = url.lastIndexOf(".", slash);
return url.substring(slash + 1, dot != -1 ? dot : url.length());
}
@Override
public String toString() {
return "ProfileTexture{url=" + this.url + ", hash=" + this.getHash() + "}";
}
}

View file

@ -1,8 +0,0 @@
package org.spacehq.mc.auth;
public enum ProfileTextureType {
SKIN,
CAPE;
}

View file

@ -1,142 +0,0 @@
package org.spacehq.mc.auth;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.spacehq.mc.auth.exception.*;
import org.spacehq.mc.auth.properties.Property;
import org.spacehq.mc.auth.request.JoinServerRequest;
import org.spacehq.mc.auth.response.HasJoinedResponse;
import org.spacehq.mc.auth.response.MinecraftProfilePropertiesResponse;
import org.spacehq.mc.auth.response.MinecraftTexturesPayload;
import org.spacehq.mc.auth.response.Response;
import org.spacehq.mc.auth.serialize.UUIDSerializer;
import org.spacehq.mc.util.Base64;
import org.spacehq.mc.util.IOUtils;
import org.spacehq.mc.util.URLUtils;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;
public class SessionService {
private static final String BASE_URL = "https://sessionserver.mojang.com/session/minecraft/";
private static final URL JOIN_URL = URLUtils.constantURL(BASE_URL + "join");
private static final URL CHECK_URL = URLUtils.constantURL(BASE_URL + "hasJoined");
private static final PublicKey SIGNATURE_KEY;
private static final Gson GSON = new GsonBuilder().registerTypeAdapter(UUID.class, new UUIDSerializer()).create();
static {
try {
X509EncodedKeySpec spec = new X509EncodedKeySpec(IOUtils.toByteArray(SessionService.class.getResourceAsStream("/yggdrasil_session_pubkey.der")));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
SIGNATURE_KEY = keyFactory.generatePublic(spec);
} catch(Exception var4) {
throw new ExceptionInInitializerError("Missing/invalid yggdrasil public key.");
}
}
public void joinServer(GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException {
JoinServerRequest request = new JoinServerRequest(authenticationToken, profile.getId(), serverId);
URLUtils.makeRequest(JOIN_URL, request, Response.class);
}
public GameProfile hasJoinedServer(GameProfile user, String serverId) throws AuthenticationUnavailableException {
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put("username", user.getName());
arguments.put("serverId", serverId);
URL url = URLUtils.concatenateURL(CHECK_URL, URLUtils.buildQuery(arguments));
try {
HasJoinedResponse response = URLUtils.makeRequest(url, null, HasJoinedResponse.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;
} else {
return null;
}
} catch(AuthenticationUnavailableException e) {
throw e;
} catch(AuthenticationException e) {
return null;
}
}
public Map<ProfileTextureType, ProfileTexture> getTextures(GameProfile profile, boolean requireSecure) throws PropertyException {
Property textures = profile.getProperties().get("textures");
if(textures != null) {
if(!textures.hasSignature()) {
throw new ProfileTextureException("Signature is missing from textures payload.");
}
if(!textures.isSignatureValid(SIGNATURE_KEY)) {
throw new ProfileTextureException("Textures payload has been tampered with. (signature invalid)");
}
MinecraftTexturesPayload result;
try {
String json = new String(Base64.decode(textures.getValue().getBytes("UTF-8")));
result = GSON.fromJson(json, MinecraftTexturesPayload.class);
} catch(Exception e) {
throw new ProfileTextureException("Could not decode texture payload.", e);
}
if(result.getProfileId() == null || !result.getProfileId().equals(profile.getId())) {
throw new ProfileTextureException("Decrypted textures payload was for another user. (expected id " + profile.getId() + " but was for " + result.getProfileId() + ")");
}
if(result.getProfileName() == null || !result.getProfileName().equals(profile.getName())) {
throw new ProfileTextureException("Decrypted textures payload was for another user. (expected name " + profile.getName() + " but was for " + result.getProfileName() + ")");
}
if(requireSecure) {
if(result.isPublic()) {
throw new ProfileTextureException("Decrypted textures payload was public when secure data is required.");
}
Calendar limit = Calendar.getInstance();
limit.add(5, -1);
Date validFrom = new Date(result.getTimestamp());
if(validFrom.before(limit.getTime())) {
throw new ProfileTextureException("Decrypted textures payload is too old. (" + validFrom + ", needs to be at least " + limit + ")");
}
}
return result.getTextures() == null ? new HashMap<ProfileTextureType, ProfileTexture>() : result.getTextures();
}
return new HashMap<ProfileTextureType, ProfileTexture>();
}
public GameProfile fillProfileProperties(GameProfile profile) throws ProfileException {
if(profile.getId() == null) {
return profile;
}
try {
URL url = URLUtils.constantURL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDSerializer.fromUUID(profile.getId()));
MinecraftProfilePropertiesResponse response = URLUtils.makeRequest(url, null, MinecraftProfilePropertiesResponse.class);
if(response == null) {
throw new ProfileNotFoundException("Couldn't fetch profile properties for " + profile + " as the profile does not exist.");
}
GameProfile result = new GameProfile(response.getId(), response.getName());
result.getProperties().putAll(response.getProperties());
profile.getProperties().putAll(response.getProperties());
return result;
} catch(AuthenticationException e) {
throw new ProfileLookupException("Couldn't look up profile properties for " + profile, e);
}
}
@Override
public String toString() {
return "SessionService{}";
}
}

View file

@ -1,345 +0,0 @@
package org.spacehq.mc.auth;
import org.spacehq.mc.auth.exception.AuthenticationException;
import org.spacehq.mc.auth.exception.InvalidCredentialsException;
import org.spacehq.mc.auth.exception.PropertyDeserializeException;
import org.spacehq.mc.auth.properties.Property;
import org.spacehq.mc.auth.properties.PropertyMap;
import org.spacehq.mc.auth.request.AuthenticationRequest;
import org.spacehq.mc.auth.request.RefreshRequest;
import org.spacehq.mc.auth.response.AuthenticationResponse;
import org.spacehq.mc.auth.response.RefreshResponse;
import org.spacehq.mc.auth.response.User;
import org.spacehq.mc.util.URLUtils;
import org.spacehq.mc.auth.serialize.UUIDSerializer;
import java.net.URL;
import java.util.*;
public class UserAuthentication {
private static final String BASE_URL = "https://authserver.mojang.com/";
private static final URL ROUTE_AUTHENTICATE = URLUtils.constantURL(BASE_URL + "authenticate");
private static final URL ROUTE_REFRESH = URLUtils.constantURL(BASE_URL + "refresh");
private static final String STORAGE_KEY_PROFILE_NAME = "displayName";
private static final String STORAGE_KEY_PROFILE_ID = "uuid";
private static final String STORAGE_KEY_PROFILE_PROPERTIES = "profileProperties";
private static final String STORAGE_KEY_USER_NAME = "username";
private static final String STORAGE_KEY_USER_ID = "userid";
private static final String STORAGE_KEY_USER_PROPERTIES = "userProperties";
private static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken";
private String clientToken;
private PropertyMap userProperties = new PropertyMap();
private String userId;
private String username;
private String password;
private String accessToken;
private boolean isOnline;
private List<GameProfile> profiles = new ArrayList<GameProfile>();
private GameProfile selectedProfile;
private UserType userType;
public UserAuthentication(String clientToken) {
if(clientToken == null) {
throw new IllegalArgumentException("ClientToken cannot be null.");
}
this.clientToken = clientToken;
}
public String getClientToken() {
return this.clientToken;
}
public String getUserID() {
return this.userId;
}
public String getAccessToken() {
return this.accessToken;
}
public List<GameProfile> getAvailableProfiles() {
return this.profiles;
}
public GameProfile getSelectedProfile() {
return this.selectedProfile;
}
public UserType getUserType() {
return this.isLoggedIn() ? (this.userType == null ? UserType.LEGACY : this.userType) : null;
}
public PropertyMap getUserProperties() {
return this.isLoggedIn() ? new PropertyMap(this.userProperties) : new PropertyMap();
}
public boolean isLoggedIn() {
return this.accessToken != null && !this.accessToken.equals("");
}
public boolean canPlayOnline() {
return this.isLoggedIn() && this.getSelectedProfile() != null && this.isOnline;
}
public boolean canLogIn() {
return !this.canPlayOnline() && this.username != null && !this.username.equals("") && ((this.password != null && !this.password.equals("")) || (this.accessToken != null && !this.accessToken.equals("")));
}
public void setUsername(String username) {
if(this.isLoggedIn() && this.canPlayOnline()) {
throw new IllegalStateException("Cannot change username whilst logged in & online");
} else {
this.username = username;
}
}
public void setPassword(String password) {
if(this.isLoggedIn() && this.canPlayOnline() && this.password != null && !this.password.equals("")) {
throw new IllegalStateException("Cannot set password whilst logged in & online");
} else {
this.password = password;
}
}
public void setAccessToken(String accessToken) {
if(this.isLoggedIn() && this.canPlayOnline()) {
throw new IllegalStateException("Cannot change accessToken whilst logged in & online");
} else {
this.accessToken = accessToken;
}
}
public void loadFromStorage(Map<String, Object> credentials) throws PropertyDeserializeException {
this.logout();
this.setUsername((String) credentials.get(STORAGE_KEY_USER_NAME));
if(credentials.containsKey(STORAGE_KEY_USER_ID)) {
this.userId = (String) credentials.get(STORAGE_KEY_USER_ID);
} else {
this.userId = this.username;
}
if(credentials.containsKey(STORAGE_KEY_USER_PROPERTIES)) {
try {
List<Map<String, String>> list = (List<Map<String, String>>) credentials.get(STORAGE_KEY_USER_PROPERTIES);
for(Map<String, String> propertyMap : list) {
String name = propertyMap.get("name");
String value = propertyMap.get("value");
String signature = propertyMap.get("signature");
if(signature == null) {
this.userProperties.put(name, new Property(name, value));
} else {
this.userProperties.put(name, new Property(name, value, signature));
}
}
} catch(Throwable t) {
throw new PropertyDeserializeException("Couldn't deserialize user properties", t);
}
}
if(credentials.containsKey(STORAGE_KEY_PROFILE_NAME) && credentials.containsKey(STORAGE_KEY_PROFILE_ID)) {
GameProfile profile = new GameProfile(UUIDSerializer.fromString((String) credentials.get(STORAGE_KEY_PROFILE_ID)), (String) credentials.get(STORAGE_KEY_PROFILE_NAME));
if(credentials.containsKey(STORAGE_KEY_PROFILE_PROPERTIES)) {
try {
List<Map<String, String>> list = (List<Map<String, String>>) credentials.get(STORAGE_KEY_PROFILE_PROPERTIES);
for(Map<String, String> propertyMap : list) {
String name = propertyMap.get("name");
String value = propertyMap.get("value");
String signature = propertyMap.get("signature");
if(signature == null) {
profile.getProperties().put(name, new Property(name, value));
} else {
profile.getProperties().put(name, new Property(name, value, signature));
}
}
} catch(Throwable t) {
throw new PropertyDeserializeException("Couldn't deserialize profile properties", t);
}
}
this.selectedProfile = profile;
}
this.accessToken = (String) credentials.get(STORAGE_KEY_ACCESS_TOKEN);
}
public Map<String, Object> saveForStorage() {
Map<String, Object> result = new HashMap<String, Object>();
if(this.username != null) {
result.put(STORAGE_KEY_USER_NAME, this.username);
}
if(this.getUserID() != null) {
result.put(STORAGE_KEY_USER_ID, this.userId);
}
if(!this.getUserProperties().isEmpty()) {
List<Map<String, String>> properties = new ArrayList<Map<String, String>>();
for(Property userProperty : this.getUserProperties().values()) {
Map<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);
}
GameProfile selectedProfile = this.getSelectedProfile();
if(selectedProfile != null) {
result.put(STORAGE_KEY_PROFILE_NAME, selectedProfile.getName());
result.put(STORAGE_KEY_PROFILE_ID, selectedProfile.getId());
List<Map<String, String>> properties = new ArrayList<Map<String, String>>();
for(Property profileProperty : selectedProfile.getProperties().values()) {
Map<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);
}
}
if(this.accessToken != null && !this.accessToken.equals("")) {
result.put(STORAGE_KEY_ACCESS_TOKEN, this.accessToken);
}
return result;
}
public void login() throws AuthenticationException {
if(this.username == null || this.username.equals("")) {
throw new InvalidCredentialsException("Invalid username");
} else {
if(this.accessToken != null && !this.accessToken.equals("")) {
this.loginWithToken();
} else {
if(this.password == null || this.password.equals("")) {
throw new InvalidCredentialsException("Invalid password");
}
this.loginWithPassword();
}
}
}
private void loginWithPassword() throws AuthenticationException {
if(this.username == null || this.username.equals("")) {
throw new InvalidCredentialsException("Invalid username");
} else if(this.password == null || this.password.equals("")) {
throw new InvalidCredentialsException("Invalid password");
} else {
AuthenticationRequest request = new AuthenticationRequest(this, this.username, this.password);
AuthenticationResponse response = URLUtils.makeRequest(ROUTE_AUTHENTICATE, request, AuthenticationResponse.class);
if(!response.getClientToken().equals(this.getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
} else {
if(response.getSelectedProfile() != null) {
this.userType = response.getSelectedProfile().isLegacy() ? UserType.LEGACY : UserType.MOJANG;
} else if(response.getAvailableProfiles() != null && response.getAvailableProfiles().length != 0) {
this.userType = response.getAvailableProfiles()[0].isLegacy() ? UserType.LEGACY : UserType.MOJANG;
}
if(response.getUser() != null && response.getUser().getId() != null) {
this.userId = response.getUser().getId();
} else {
this.userId = this.username;
}
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.profiles = Arrays.asList(response.getAvailableProfiles());
this.selectedProfile = response.getSelectedProfile();
this.updateProperties(response.getUser());
}
}
}
private void loginWithToken() throws AuthenticationException {
if(this.userId == null || this.userId.equals("")) {
if(this.username == null || this.username.equals("")) {
throw new InvalidCredentialsException("Invalid uuid & username");
}
this.userId = this.username;
}
if(this.accessToken == null || this.accessToken.equals("")) {
throw new InvalidCredentialsException("Invalid access token");
} else {
RefreshRequest request = new RefreshRequest(this);
RefreshResponse response = URLUtils.makeRequest(ROUTE_REFRESH, request, RefreshResponse.class);
if(!response.getClientToken().equals(this.getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
} else {
if(response.getSelectedProfile() != null) {
this.userType = response.getSelectedProfile().isLegacy() ? UserType.LEGACY : UserType.MOJANG;
} else if(response.getAvailableProfiles() != null && response.getAvailableProfiles().length != 0) {
this.userType = response.getAvailableProfiles()[0].isLegacy() ? UserType.LEGACY : UserType.MOJANG;
}
if(response.getUser() != null && response.getUser().getId() != null) {
this.userId = response.getUser().getId();
} else {
this.userId = this.username;
}
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.profiles = Arrays.asList(response.getAvailableProfiles());
this.selectedProfile = response.getSelectedProfile();
this.updateProperties(response.getUser());
}
}
}
public void logout() {
this.password = null;
this.userId = null;
this.selectedProfile = null;
this.userProperties.clear();
this.accessToken = null;
this.profiles = null;
this.isOnline = false;
this.userType = null;
}
public void selectGameProfile(GameProfile profile) throws AuthenticationException {
if(!this.isLoggedIn()) {
throw new AuthenticationException("Cannot change game profile whilst not logged in");
} else if(this.getSelectedProfile() != null) {
throw new AuthenticationException("Cannot change game profile. You must log out and back in.");
} else if(profile != null && this.profiles.contains(profile)) {
RefreshRequest request = new RefreshRequest(this, profile);
RefreshResponse response = URLUtils.makeRequest(ROUTE_REFRESH, request, RefreshResponse.class);
if(!response.getClientToken().equals(this.getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
} else {
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.selectedProfile = response.getSelectedProfile();
}
} else {
throw new IllegalArgumentException("Invalid profile '" + profile + "'");
}
}
@Override
public String toString() {
return "UserAuthentication{profiles=" + this.profiles + ", selectedProfile=" + this.getSelectedProfile() + ", username=" + this.username + ", isLoggedIn=" + this.isLoggedIn() + ", canPlayOnline=" + this.canPlayOnline() + ", accessToken=" + this.accessToken + ", clientToken=" + this.getClientToken() + "}";
}
private void updateProperties(User user) {
this.userProperties.clear();
if(user != null && user.getProperties() != null) {
this.userProperties.putAll(user.getProperties());
}
}
}

View file

@ -1,8 +0,0 @@
package org.spacehq.mc.auth;
public enum UserType {
LEGACY,
MOJANG;
}

View file

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class AuthenticationException extends Exception {
private static final long serialVersionUID = 1L;
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

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.exception;
public class AuthenticationUnavailableException extends AuthenticationException {
private static final long serialVersionUID = 1L;
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

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.exception;
public class InvalidCredentialsException extends AuthenticationException {
private static final long serialVersionUID = 1L;
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

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class ProfileException extends Exception {
private static final long serialVersionUID = 1L;
public ProfileException() {
}
public ProfileException(String message) {
super(message);
}
public ProfileException(String message, Throwable cause) {
super(message, cause);
}
public ProfileException(Throwable cause) {
super(cause);
}
}

View file

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class ProfileLookupException extends ProfileException {
private static final long serialVersionUID = 1L;
public ProfileLookupException() {
}
public ProfileLookupException(String message) {
super(message);
}
public ProfileLookupException(String message, Throwable cause) {
super(message, cause);
}
public ProfileLookupException(Throwable cause) {
super(cause);
}
}

View file

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.exception;
public class ProfileNotFoundException extends ProfileException {
private static final long serialVersionUID = 1L;
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

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class ProfileTextureException extends PropertyException {
private static final long serialVersionUID = 1L;
public ProfileTextureException() {
}
public ProfileTextureException(String message) {
super(message);
}
public ProfileTextureException(String message, Throwable cause) {
super(message, cause);
}
public ProfileTextureException(Throwable cause) {
super(cause);
}
}

View file

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class PropertyDeserializeException extends PropertyException {
private static final long serialVersionUID = 1L;
public PropertyDeserializeException() {
}
public PropertyDeserializeException(String message) {
super(message);
}
public PropertyDeserializeException(String message, Throwable cause) {
super(message, cause);
}
public PropertyDeserializeException(Throwable cause) {
super(cause);
}
}

View file

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class PropertyException extends Exception {
private static final long serialVersionUID = 1L;
public PropertyException() {
}
public PropertyException(String message) {
super(message);
}
public PropertyException(String message, Throwable cause) {
super(message, cause);
}
public PropertyException(Throwable cause) {
super(cause);
}
}

View file

@ -1,22 +0,0 @@
package org.spacehq.mc.auth.exception;
public class SignatureValidateException extends PropertyException {
private static final long serialVersionUID = 1L;
public SignatureValidateException() {
}
public SignatureValidateException(String message) {
super(message);
}
public SignatureValidateException(String message, Throwable cause) {
super(message, cause);
}
public SignatureValidateException(Throwable cause) {
super(cause);
}
}

View file

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.exception;
public class UserMigratedException extends InvalidCredentialsException {
private static final long serialVersionUID = 1L;
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

@ -1,52 +0,0 @@
package org.spacehq.mc.auth.properties;
import org.spacehq.mc.auth.exception.SignatureValidateException;
import org.spacehq.mc.util.Base64;
import java.security.PublicKey;
import java.security.Signature;
public class Property {
private String name;
private String value;
private 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 key) throws SignatureValidateException {
try {
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(key);
sig.update(this.value.getBytes());
return sig.verify(Base64.decode(this.signature.getBytes("UTF-8")));
} catch(Exception e) {
throw new SignatureValidateException("Could not validate property signature.", e);
}
}
}

View file

@ -1,16 +0,0 @@
package org.spacehq.mc.auth.properties;
import java.util.HashMap;
import java.util.Map;
public class PropertyMap extends HashMap<String, Property> {
public PropertyMap() {
super();
}
public PropertyMap(Map<String, Property> copy) {
super(copy);
}
}

View file

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.request;
public class Agent {
private String name;
private 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;
}
}

View file

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.request;
import org.spacehq.mc.auth.UserAuthentication;
@SuppressWarnings("unused")
public class AuthenticationRequest {
private Agent agent;
private String username;
private String password;
private String clientToken;
private boolean requestUser = true;
public AuthenticationRequest(UserAuthentication auth, String username, String password) {
this.agent = new Agent("Minecraft", 1);
this.username = username;
this.clientToken = auth.getClientToken();
this.password = password;
}
}

View file

@ -1,18 +0,0 @@
package org.spacehq.mc.auth.request;
import java.util.UUID;
@SuppressWarnings("unused")
public class JoinServerRequest {
private String accessToken;
private UUID selectedProfile;
private String serverId;
public JoinServerRequest(String accessToken, UUID id, String serverId) {
this.accessToken = accessToken;
this.selectedProfile = id;
this.serverId = serverId;
}
}

View file

@ -1,25 +0,0 @@
package org.spacehq.mc.auth.request;
import org.spacehq.mc.auth.GameProfile;
import org.spacehq.mc.auth.UserAuthentication;
@SuppressWarnings("unused")
public class RefreshRequest {
private String clientToken;
private String accessToken;
private GameProfile selectedProfile;
private boolean requestUser;
public RefreshRequest(UserAuthentication authService) {
this(authService, null);
}
public RefreshRequest(UserAuthentication authService, GameProfile profile) {
this.requestUser = true;
this.clientToken = authService.getClientToken();
this.accessToken = authService.getAccessToken();
this.selectedProfile = profile;
}
}

View file

@ -1,33 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.GameProfile;
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

@ -1,20 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.properties.PropertyMap;
import java.util.UUID;
public class HasJoinedResponse extends Response {
private UUID id;
private PropertyMap properties;
public UUID getId() {
return this.id;
}
public PropertyMap getProperties() {
return this.properties;
}
}

View file

@ -1,25 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.properties.PropertyMap;
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

@ -1,37 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.ProfileTexture;
import org.spacehq.mc.auth.ProfileTextureType;
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<ProfileTextureType, ProfileTexture> 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<ProfileTextureType, ProfileTexture> getTextures() {
return this.textures;
}
}

View file

@ -1,18 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.GameProfile;
public class ProfileSearchResultsResponse extends Response {
private GameProfile[] profiles;
private int size;
public GameProfile[] getProfiles() {
return this.profiles;
}
public int getSize() {
return this.size;
}
}

View file

@ -1,33 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.GameProfile;
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

@ -1,21 +0,0 @@
package org.spacehq.mc.auth.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;
}
}

View file

@ -1,18 +0,0 @@
package org.spacehq.mc.auth.response;
import org.spacehq.mc.auth.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

@ -1,33 +0,0 @@
package org.spacehq.mc.auth.serialize;
import com.google.gson.*;
import org.spacehq.mc.auth.GameProfile;
import java.lang.reflect.Type;
import java.util.UUID;
public class GameProfileSerializer implements JsonSerializer<GameProfile>, JsonDeserializer<GameProfile> {
@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"), 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

@ -1,59 +0,0 @@
package org.spacehq.mc.auth.serialize;
import com.google.gson.*;
import org.spacehq.mc.auth.properties.Property;
import org.spacehq.mc.auth.properties.PropertyMap;
import java.lang.reflect.Type;
import java.util.Map;
public class PropertyMapSerializer implements JsonSerializer<PropertyMap>, JsonDeserializer<PropertyMap> {
@Override
public PropertyMap deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
PropertyMap result = new PropertyMap();
if(json instanceof JsonObject) {
JsonObject object = (JsonObject) json;
for(Map.Entry<String, JsonElement> entry : object.entrySet()) {
if(entry.getValue() instanceof JsonArray) {
for(JsonElement element : (JsonArray) entry.getValue()) {
result.put(entry.getKey(), new Property(entry.getKey(), element.getAsString()));
}
}
}
} else if(json instanceof JsonArray) {
for(JsonElement element : (JsonArray) json) {
if((element instanceof JsonObject)) {
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()));
} else {
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

@ -1,28 +0,0 @@
package org.spacehq.mc.auth.serialize;
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 UUIDSerializer extends TypeAdapter<UUID> {
public void write(JsonWriter out, UUID value) throws IOException {
out.value(fromUUID(value));
}
public UUID read(JsonReader in) throws IOException {
return 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

@ -22,7 +22,7 @@ import org.spacehq.mc.protocol.packet.status.client.StatusPingPacket;
import org.spacehq.mc.protocol.packet.status.client.StatusQueryPacket;
import org.spacehq.mc.protocol.packet.status.server.StatusPongPacket;
import org.spacehq.mc.protocol.packet.status.server.StatusResponsePacket;
import org.spacehq.mc.util.CryptUtil;
import org.spacehq.mc.protocol.util.CryptUtil;
import org.spacehq.packetlib.event.session.ConnectedEvent;
import org.spacehq.packetlib.event.session.PacketReceivedEvent;
import org.spacehq.packetlib.event.session.PacketSentEvent;

View file

@ -18,7 +18,7 @@ import org.spacehq.mc.protocol.packet.status.client.StatusPingPacket;
import org.spacehq.mc.protocol.packet.status.client.StatusQueryPacket;
import org.spacehq.mc.protocol.packet.status.server.StatusPongPacket;
import org.spacehq.mc.protocol.packet.status.server.StatusResponsePacket;
import org.spacehq.mc.util.CryptUtil;
import org.spacehq.mc.protocol.util.CryptUtil;
import org.spacehq.packetlib.Session;
import org.spacehq.packetlib.event.session.DisconnectingEvent;
import org.spacehq.packetlib.event.session.PacketReceivedEvent;

View file

@ -4,7 +4,7 @@ import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.Face;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.entity.player.PlayerAction;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -4,7 +4,7 @@ import org.spacehq.mc.protocol.data.game.ItemStack;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.Face;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.client.window;
import org.spacehq.mc.protocol.data.game.ItemStack;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.client.window;
import org.spacehq.mc.protocol.data.game.ItemStack;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.window.*;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.client.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.entity;
import org.spacehq.mc.protocol.data.game.ItemStack;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.entity;
import org.spacehq.mc.protocol.data.game.EntityMetadata;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.entity.player;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.entity.spawn;
import org.spacehq.mc.protocol.data.game.EntityMetadata;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.entity.MobType;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -4,7 +4,7 @@ import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.entity.Art;
import org.spacehq.mc.protocol.data.game.values.entity.HangingDirection;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.entity.spawn;
import org.spacehq.mc.auth.GameProfile;
import org.spacehq.mc.auth.properties.Property;
import org.spacehq.mc.protocol.data.game.EntityMetadata;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.window;
import org.spacehq.mc.protocol.data.game.ItemStack;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.window;
import org.spacehq.mc.protocol.data.game.ItemStack;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.entity.player.BlockBreakStage;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.values.world.block.BlockChangeRecord;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.world.block.value.*;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,9 +1,9 @@
package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Chunk;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.util.NetworkChunkData;
import org.spacehq.mc.util.ParsedChunkData;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.mc.protocol.util.NetworkChunkData;
import org.spacehq.mc.protocol.util.ParsedChunkData;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,9 +1,9 @@
package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Chunk;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.util.NetworkChunkData;
import org.spacehq.mc.util.ParsedChunkData;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.mc.protocol.util.NetworkChunkData;
import org.spacehq.mc.protocol.util.ParsedChunkData;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.world.effect.*;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,7 +1,7 @@
package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -3,7 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.world;
import org.spacehq.mc.protocol.data.game.Position;
import org.spacehq.mc.protocol.data.game.values.MagicValues;
import org.spacehq.mc.protocol.data.game.values.world.block.UpdatedTileType;
import org.spacehq.mc.util.NetUtil;
import org.spacehq.mc.protocol.util.NetUtil;
import org.spacehq.opennbt.tag.builtin.CompoundTag;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;

View file

@ -1,6 +1,6 @@
package org.spacehq.mc.protocol.packet.login.client;
import org.spacehq.mc.util.CryptUtil;
import org.spacehq.mc.protocol.util.CryptUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,6 +1,6 @@
package org.spacehq.mc.protocol.packet.login.server;
import org.spacehq.mc.util.CryptUtil;
import org.spacehq.mc.protocol.util.CryptUtil;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -5,11 +5,11 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.spacehq.mc.auth.GameProfile;
import org.spacehq.mc.auth.util.Base64;
import org.spacehq.mc.protocol.data.message.Message;
import org.spacehq.mc.protocol.data.status.PlayerInfo;
import org.spacehq.mc.protocol.data.status.ServerStatusInfo;
import org.spacehq.mc.protocol.data.status.VersionInfo;
import org.spacehq.mc.util.Base64;
import org.spacehq.packetlib.io.NetInput;
import org.spacehq.packetlib.io.NetOutput;
import org.spacehq.packetlib.packet.Packet;

View file

@ -1,4 +1,4 @@
package org.spacehq.mc.util;
package org.spacehq.mc.protocol.util;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

View file

@ -1,4 +1,4 @@
package org.spacehq.mc.util;
package org.spacehq.mc.protocol.util;
import org.spacehq.mc.protocol.data.game.*;
import org.spacehq.mc.protocol.data.game.values.MagicValues;

View file

@ -1,4 +1,4 @@
package org.spacehq.mc.util;
package org.spacehq.mc.protocol.util;
public class NetworkChunkData {

View file

@ -1,4 +1,4 @@
package org.spacehq.mc.util;
package org.spacehq.mc.protocol.util;
import org.spacehq.mc.protocol.data.game.Chunk;

View file

@ -1,176 +0,0 @@
package org.spacehq.mc.util;
public class Base64 {
private final static byte EQUALS_SIGN = (byte) '=';
private final static byte WHITE_SPACE_ENC = -5;
private final static byte EQUALS_SIGN_ENC = -1;
private final static byte[] _STANDARD_ALPHABET = { (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' };
private final static byte[] _STANDARD_DECODABET = { -9, -9, -9, -9, -9, -9, -9, -9, -9, -5, -5, -9, -9, -5, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -5, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, 62, -9, -9, -9, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -9, -9, -9, -1, -9, -9, -9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -9, -9, -9, -9, -9, -9, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 };
private static byte[] getAlphabet() {
return _STANDARD_ALPHABET;
}
private static byte[] getDecodabet() {
return _STANDARD_DECODABET;
}
private static byte[] encode3to4(byte[] source, int srcOffset, int numSigBytes, byte[] destination, int destOffset) {
byte[] ALPHABET = getAlphabet();
int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);
switch(numSigBytes) {
case 3:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
return destination;
case 2:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
case 1:
destination[destOffset] = ALPHABET[(inBuff >>> 18)];
destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
destination[destOffset + 2] = EQUALS_SIGN;
destination[destOffset + 3] = EQUALS_SIGN;
return destination;
default:
return destination;
}
}
public static byte[] encode(byte[] source) {
return encode(source, 0, source.length);
}
public static byte[] encode(byte[] source, int off, int len) {
if(source == null) {
throw new NullPointerException("Cannot serialize a null array.");
}
if(off < 0) {
throw new IllegalArgumentException("Cannot have negative offset: " + off);
}
if(len < 0) {
throw new IllegalArgumentException("Cannot have length offset: " + len);
}
if(off + len > source.length) {
throw new IllegalArgumentException(String.format("Cannot have offset of %d and length of %d with array of length %d", off, len, source.length));
}
int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0);
byte[] outBuff = new byte[encLen];
int d = 0;
int e = 0;
int len2 = len - 2;
for(; d < len2; d += 3, e += 4) {
encode3to4(source, d + off, 3, outBuff, e);
}
if(d < len) {
encode3to4(source, d + off, len - d, outBuff, e);
e += 4;
}
if(e <= outBuff.length - 1) {
byte[] finalOut = new byte[e];
System.arraycopy(outBuff, 0, finalOut, 0, e);
return finalOut;
} else {
return outBuff;
}
}
private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset) {
if(source == null) {
throw new NullPointerException("Source array was null.");
}
if(destination == null) {
throw new NullPointerException("Destination array was null.");
}
if(srcOffset < 0 || srcOffset + 3 >= source.length) {
throw new IllegalArgumentException(String.format("Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset));
}
if(destOffset < 0 || destOffset + 2 >= destination.length) {
throw new IllegalArgumentException(String.format("Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset));
}
byte[] DECODABET = getDecodabet();
if(source[srcOffset + 2] == EQUALS_SIGN) {
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
} else if(source[srcOffset + 3] == EQUALS_SIGN) {
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6);
destination[destOffset] = (byte) (outBuff >>> 16);
destination[destOffset + 1] = (byte) (outBuff >>> 8);
return 2;
} else {
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) | ((DECODABET[source[srcOffset + 3]] & 0xFF));
destination[destOffset] = (byte) (outBuff >> 16);
destination[destOffset + 1] = (byte) (outBuff >> 8);
destination[destOffset + 2] = (byte) (outBuff);
return 3;
}
}
public static byte[] decode(byte[] source) throws java.io.IOException {
return decode(source, 0, source.length);
}
public static byte[] decode(byte[] source, int off, int len) throws java.io.IOException {
if(source == null) {
throw new NullPointerException("Cannot decode null source array.");
}
if(off < 0 || off + len > source.length) {
throw new IllegalArgumentException(String.format("Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len));
}
if(len == 0) {
return new byte[0];
} else if(len < 4) {
throw new IllegalArgumentException("Base64-encoded string must have at least four characters, but length specified was " + len);
}
byte[] DECODABET = getDecodabet();
int len34 = len * 3 / 4;
byte[] outBuff = new byte[len34];
int outBuffPosn = 0;
byte[] b4 = new byte[4];
int b4Posn = 0;
int i = 0;
byte sbiDecode = 0;
for(i = off; i < off + len; i++) {
sbiDecode = DECODABET[source[i] & 0xFF];
if(sbiDecode >= WHITE_SPACE_ENC) {
if(sbiDecode >= EQUALS_SIGN_ENC) {
b4[b4Posn++] = source[i];
if(b4Posn > 3) {
outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
b4Posn = 0;
if(source[i] == EQUALS_SIGN) {
break;
}
}
}
} else {
throw new java.io.IOException(String.format("Bad Base64 input character decimal %d in array position %d", source[i] & 0xFF, i));
}
}
byte[] out = new byte[outBuffPosn];
System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
return out;
}
}

View file

@ -1,44 +0,0 @@
package org.spacehq.mc.util;
import java.io.*;
public class IOUtils {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static void closeQuietly(Closeable close) {
try {
if(close != null) {
close.close();
}
} catch(IOException e) {
}
}
public static String toString(InputStream input, String encoding) throws IOException {
StringWriter writer = new StringWriter();
InputStreamReader in = encoding != null ? new InputStreamReader(input, encoding) : new InputStreamReader(input);
char[] buffer = new char[DEFAULT_BUFFER_SIZE];
int n = 0;
while(-1 != (n = in.read(buffer))) {
writer.write(buffer, 0, n);
}
in.close();
return writer.toString();
}
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte buffer[] = new byte[DEFAULT_BUFFER_SIZE];
int n = 0;
while(-1 != (n = in.read(buffer))) {
out.write(buffer, 0, n);
}
in.close();
out.close();
return out.toByteArray();
}
}

View file

@ -1,187 +0,0 @@
package org.spacehq.mc.util;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.spacehq.mc.auth.GameProfile;
import org.spacehq.mc.auth.exception.AuthenticationException;
import org.spacehq.mc.auth.exception.AuthenticationUnavailableException;
import org.spacehq.mc.auth.exception.InvalidCredentialsException;
import org.spacehq.mc.auth.exception.UserMigratedException;
import org.spacehq.mc.auth.properties.PropertyMap;
import org.spacehq.mc.auth.response.Response;
import org.spacehq.mc.auth.serialize.GameProfileSerializer;
import org.spacehq.mc.auth.serialize.PropertyMapSerializer;
import org.spacehq.mc.auth.serialize.UUIDSerializer;
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.URL;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
public class URLUtils {
private static final Gson GSON;
static {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(GameProfile.class, new GameProfileSerializer());
builder.registerTypeAdapter(PropertyMap.class, new PropertyMapSerializer());
builder.registerTypeAdapter(UUID.class, new UUIDSerializer());
GSON = builder.create();
}
public static URL constantURL(String url) {
try {
return new URL(url);
} catch(MalformedURLException e) {
throw new Error("Malformed constant url: " + url);
}
}
public static URL concatenateURL(URL url, String query) {
try {
return url.getQuery() != null && url.getQuery().length() > 0 ? new URL(url.getProtocol(), url.getHost(), url.getFile() + "&" + query) : new URL(url.getProtocol(), url.getHost(), url.getFile() + "?" + query);
} catch(MalformedURLException e) {
throw new IllegalArgumentException("Concatenated URL was malformed: " + url.toString() + ", " + query);
}
}
public static String buildQuery(Map<String, Object> query) {
if(query == null) {
return "";
} else {
StringBuilder builder = new StringBuilder();
Iterator<Entry<String, Object>> it = query.entrySet().iterator();
while(it.hasNext()) {
Entry<String, Object> entry = it.next();
if(builder.length() > 0) {
builder.append("&");
}
try {
builder.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
} catch(UnsupportedEncodingException e) {
}
if(entry.getValue() != null) {
builder.append("=");
try {
builder.append(URLEncoder.encode(entry.getValue().toString(), "UTF-8"));
} catch(UnsupportedEncodingException e) {
}
}
}
return builder.toString();
}
}
public static <T extends Response> T makeRequest(URL url, Object input, Class<T> clazz) throws AuthenticationException {
try {
String jsonString = input == null ? performGetRequest(url) : performPostRequest(url, GSON.toJson(input), "application/json");
T result = GSON.fromJson(jsonString, clazz);
if(result == null) {
return null;
} else if(result.getError() != null && !result.getError().equals("")) {
if(result.getCause() != null && result.getCause().equals("UserMigratedException")) {
throw new UserMigratedException(result.getErrorMessage());
} else if(result.getError().equals("ForbiddenOperationException")) {
throw new InvalidCredentialsException(result.getErrorMessage());
} else {
throw new AuthenticationException(result.getErrorMessage());
}
} else {
return result;
}
} catch(Exception e) {
throw new AuthenticationUnavailableException("Could not make request to auth server.", e);
}
}
private static HttpURLConnection createUrlConnection(URL url) throws IOException {
if(url == null) {
throw new IllegalArgumentException("URL cannot be null.");
}
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(15000);
connection.setReadTimeout(15000);
connection.setUseCaches(false);
return connection;
}
private static String performPostRequest(URL url, String post, String type) throws IOException {
if(url == null) {
throw new IllegalArgumentException("URL cannot be null.");
}
if(post == null) {
throw new IllegalArgumentException("Post cannot be null.");
}
if(type == null) {
throw new IllegalArgumentException("Type cannot be null.");
}
HttpURLConnection connection = createUrlConnection(url);
byte[] bytes = post.getBytes("UTF-8");
connection.setRequestProperty("Content-Type", type + "; charset=utf-8");
connection.setRequestProperty("Content-Length", "" + bytes.length);
connection.setDoOutput(true);
OutputStream outputStream = null;
try {
outputStream = connection.getOutputStream();
outputStream.write(bytes);
} finally {
IOUtils.closeQuietly(outputStream);
}
InputStream inputStream = null;
try {
inputStream = connection.getInputStream();
return IOUtils.toString(inputStream, "UTF-8");
} catch(IOException e) {
IOUtils.closeQuietly(inputStream);
inputStream = connection.getErrorStream();
if(inputStream == null) {
throw e;
}
return IOUtils.toString(inputStream, "UTF-8");
} finally {
IOUtils.closeQuietly(inputStream);
}
}
private static String performGetRequest(URL url) throws IOException {
if(url == null) {
throw new IllegalArgumentException("URL cannot be null.");
}
HttpURLConnection connection = createUrlConnection(url);
InputStream inputStream = null;
try {
inputStream = connection.getInputStream();
return IOUtils.toString(inputStream, "UTF-8");
} catch(IOException e) {
IOUtils.closeQuietly(inputStream);
inputStream = connection.getErrorStream();
if(inputStream == null) {
throw e;
}
return IOUtils.toString(inputStream, "UTF-8");
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}