mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2025-02-13 08:14:23 +01:00
Add Valhalla implementation
This commit is contained in:
parent
89ecc9f916
commit
77b8357f03
4 changed files with 207 additions and 10 deletions
12
build.gradle
12
build.gradle
|
@ -39,11 +39,18 @@ sourceSets {
|
|||
ext.refMap = 'hdskins.mixin.refmap.json'
|
||||
}
|
||||
main {
|
||||
compileClasspath += hdskins.output
|
||||
compileClasspath += hdskins.output + hdskins.compileClasspath
|
||||
ext.refMap = 'minelp.mixin.refmap.json'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// use the same version as httpclient
|
||||
hdskinsCompile('org.apache.httpcomponents:httpmime:4.3.2'){
|
||||
transitive = false
|
||||
}
|
||||
}
|
||||
|
||||
litemod.json {
|
||||
mcversion = '1.12.r2'
|
||||
author = 'Verdana, Rene_Z, Mumfrey, Killjoy1221'
|
||||
|
@ -64,6 +71,9 @@ litemod.json {
|
|||
jar {
|
||||
from sourceSets.hdskins.output
|
||||
from litemod
|
||||
|
||||
// TODO relocate. LiteLoader excludes apache libs from classloading
|
||||
from {configurations.hdskinsCompile.collect{it.isDirectory() ? it : zipTree(it)}}
|
||||
}
|
||||
sourceJar {
|
||||
// add hdskins sources
|
||||
|
|
|
@ -223,7 +223,6 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PreviewTexture getPreviewTexture(ResourceLocation skinResource, GameProfile profile, Type type, ResourceLocation def, @Nullable final SkinAvailableCallback callback) {
|
||||
TextureManager textureManager = Minecraft.getMinecraft().getTextureManager();
|
||||
MinecraftProfileTexture url = INSTANCE.getGatewayServer().getPreviewTexture(type, profile).orElse(null);
|
||||
|
|
|
@ -1,21 +1,55 @@
|
|||
package com.voxelmodpack.hdskins.skins;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.minecraft.MinecraftProfileTexture;
|
||||
import com.mojang.authlib.yggdrasil.response.MinecraftTexturesPayload;
|
||||
import com.mojang.util.UUIDTypeAdapter;
|
||||
import com.voxelmodpack.hdskins.HDSkinManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.Session;
|
||||
import org.apache.http.HttpHeaders;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ValhallaSkinServer implements SkinServer {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final String baseURL;
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(UUID.class, new UUIDTypeAdapter())
|
||||
.create();
|
||||
|
||||
private String accessToken;
|
||||
|
||||
public ValhallaSkinServer(String baseURL) {
|
||||
this.baseURL = baseURL;
|
||||
|
@ -23,23 +57,175 @@ public class ValhallaSkinServer implements SkinServer {
|
|||
|
||||
@Override
|
||||
public Optional<MinecraftTexturesPayload> loadProfileData(GameProfile profile) {
|
||||
|
||||
try (CloseableHttpClient client = HttpClients.createSystem();
|
||||
CloseableHttpResponse response = client.execute(new HttpGet(getTexturesURI(profile)))) {
|
||||
|
||||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
|
||||
return Optional.of(readJson(response.getEntity().getContent(), MinecraftTexturesPayload.class));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MinecraftProfileTexture> getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile) {
|
||||
return null;
|
||||
public CompletableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable URI image,
|
||||
MinecraftProfileTexture.Type type, Map<String, String> metadata) {
|
||||
return CallableFutures.asyncFailableFuture(() -> {
|
||||
try (CloseableHttpClient client = HttpClients.createSystem()) {
|
||||
authorize(client, session);
|
||||
|
||||
GameProfile profile = session.getProfile();
|
||||
|
||||
if (image == null) {
|
||||
return resetSkin(client, profile, type);
|
||||
}
|
||||
switch (image.getScheme()) {
|
||||
case "file":
|
||||
return uploadFile(client, new File(image), profile, type, metadata);
|
||||
case "http":
|
||||
case "https":
|
||||
return uploadUrl(client, image, profile, type, metadata);
|
||||
default:
|
||||
throw new IOException("Unsupported URI scheme: " + image.getScheme());
|
||||
}
|
||||
}
|
||||
}, HDSkinManager.skinUploadExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType) {
|
||||
return null;
|
||||
private SkinUploadResponse resetSkin(CloseableHttpClient client, GameProfile profile, MinecraftProfileTexture.Type type) throws IOException {
|
||||
return upload(client, RequestBuilder.delete()
|
||||
.setUri(buildUserTextureUri(profile, type))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse uploadFile(CloseableHttpClient client, File file, GameProfile profile, MinecraftProfileTexture.Type type,
|
||||
Map<String, String> metadata) throws IOException {
|
||||
MultipartEntityBuilder b = MultipartEntityBuilder.create();
|
||||
b.addBinaryBody("file", file, ContentType.create("image/png"), file.getName());
|
||||
metadata.forEach(b::addTextBody);
|
||||
|
||||
return upload(client, RequestBuilder.put()
|
||||
.setUri(buildUserTextureUri(profile, type))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.setEntity(b.build())
|
||||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse uploadUrl(CloseableHttpClient client, URI uri, GameProfile profile, MinecraftProfileTexture.Type type,
|
||||
Map<String, String> metadata) throws IOException {
|
||||
|
||||
return upload(client, RequestBuilder.post()
|
||||
.setUri(buildUserTextureUri(profile, type))
|
||||
.addHeader(HttpHeaders.AUTHORIZATION, this.accessToken)
|
||||
.addParameter("file", uri.toString())
|
||||
.addParameters(metadata.entrySet().stream()
|
||||
.map(entry -> new BasicNameValuePair(entry.getKey(), entry.getValue()))
|
||||
.toArray(NameValuePair[]::new))
|
||||
.build());
|
||||
}
|
||||
|
||||
private SkinUploadResponse upload(CloseableHttpClient client, HttpUriRequest request) throws IOException {
|
||||
try (CloseableHttpResponse response = client.execute(request)) {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
JsonObject json = readJson(response.getEntity().getContent(), JsonObject.class);
|
||||
|
||||
return new SkinUploadResponse(code == HttpStatus.SC_OK, json.get("message").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void authorize(CloseableHttpClient client, Session session) throws IOException, AuthenticationException {
|
||||
if (accessToken != null) {
|
||||
return;
|
||||
}
|
||||
GameProfile profile = session.getProfile();
|
||||
String token = session.getToken();
|
||||
AuthHandshake handshake = authHandshake(client, profile.getName());
|
||||
|
||||
if (handshake.offline) {
|
||||
return;
|
||||
}
|
||||
|
||||
// join the session server
|
||||
Minecraft.getMinecraft().getSessionService().joinServer(profile, token, handshake.serverId);
|
||||
|
||||
AuthResponse response = authResponse(client, profile.getName(), handshake.verifyToken);
|
||||
if (!response.userId.equals(profile.getId())) {
|
||||
throw new IOException("UUID mismatch!"); // probably won't ever throw
|
||||
}
|
||||
this.accessToken = response.accessToken;
|
||||
|
||||
}
|
||||
|
||||
private <T> T readJson(InputStream in, Class<T> cl) throws IOException {
|
||||
try (Reader r = new InputStreamReader(in)) {
|
||||
return gson.fromJson(r, cl);
|
||||
}
|
||||
}
|
||||
|
||||
private AuthHandshake authHandshake(CloseableHttpClient client, String name) throws IOException {
|
||||
try (CloseableHttpResponse resp = client.execute(RequestBuilder.post()
|
||||
.setUri(getHandshakeURI())
|
||||
.addParameter("name", name)
|
||||
.build())) {
|
||||
return readJson(resp.getEntity().getContent(), AuthHandshake.class);
|
||||
}
|
||||
}
|
||||
|
||||
private AuthResponse authResponse(CloseableHttpClient client, String name, long verifyToken) throws IOException {
|
||||
try (CloseableHttpResponse resp = client.execute(RequestBuilder.post()
|
||||
.setUri(getResponseURI())
|
||||
.addParameter("name", name)
|
||||
.addParameter("verifyToken", String.valueOf(verifyToken))
|
||||
.build())) {
|
||||
return readJson(resp.getEntity().getContent(), AuthResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
private URI buildUserTextureUri(GameProfile profile, MinecraftProfileTexture.Type textureType) {
|
||||
String user = UUIDTypeAdapter.fromUUID(profile.getId());
|
||||
String skinType = textureType.name().toLowerCase(Locale.US);
|
||||
return URI.create(String.format("%s/user/%s/%s", this.baseURL, user, skinType));
|
||||
}
|
||||
|
||||
private URI getTexturesURI(GameProfile profile) {
|
||||
Preconditions.checkNotNull(profile.getId(), "profile id required for skins");
|
||||
return URI.create(String.format("%s/user/%s", this.baseURL, UUIDTypeAdapter.fromUUID(profile.getId())));
|
||||
}
|
||||
|
||||
private URI getHandshakeURI() {
|
||||
return URI.create(String.format("%s/auth/handshake", this.baseURL));
|
||||
}
|
||||
|
||||
private URI getResponseURI() {
|
||||
return URI.create(String.format("%s/auth/response", this.baseURL));
|
||||
}
|
||||
|
||||
static ValhallaSkinServer from(String server) {
|
||||
Matcher matcher = Pattern.compile("^valhalla:(.*)$").matcher(server);
|
||||
if (matcher.find())
|
||||
if (matcher.find()) {
|
||||
return new ValhallaSkinServer(matcher.group(1));
|
||||
}
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
static class AuthHandshake {
|
||||
|
||||
boolean offline;
|
||||
String serverId;
|
||||
long verifyToken;
|
||||
}
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
static class AuthResponse {
|
||||
|
||||
String accessToken;
|
||||
UUID userId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@ import javax.annotation.Nullable;
|
|||
* Uploader for Multipart form data
|
||||
*
|
||||
* @author Adam Mummery-Smith
|
||||
* @deprecated Use httpmime multipart upload
|
||||
*/
|
||||
@Deprecated
|
||||
public class ThreadMultipartPostUpload {
|
||||
protected final Map<String, ?> sourceData;
|
||||
|
||||
|
|
Loading…
Reference in a new issue