Skip to content
This repository has been archived by the owner on Apr 7, 2021. It is now read-only.

Commit

Permalink
Merge branch 'rewrite' of https://github.com/DragonetMC/DragonProxy i…
Browse files Browse the repository at this point in the history
…nto HEAD
  • Loading branch information
lukeeey committed Apr 29, 2020
2 parents 6428ce2 + 95caad7 commit 58288b8
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ private void fetchOurSkin() {
ImageData skinData = SkinUtils.fetchSkin(this, profile);
if (skinData == null) return;

ImageData capeData = SkinUtils.fetchUnofficialCape(profile);
ImageData capeData = SkinUtils.fetchCape(this, profile);
if(capeData == null) capeData = ImageData.EMPTY;

GameProfile.TextureModel model = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class PlayerListCache implements Cache {
private Object2LongMap<UUID> playerEntityIds = new Object2LongOpenHashMap<>();

private Object2ObjectMap<UUID, ImageData> remoteSkinCache = new Object2ObjectOpenHashMap<>();
private Object2ObjectMap<UUID, ImageData> remoteCapeCache = new Object2ObjectOpenHashMap<>();

public void updateDisplayName(GameProfile profile, String displayName) {
playerInfo.get(profile.getId()).setDisplayName(displayName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ public void translate(ProxySession session, ServerSpawnPlayerPacket packet) {
cachedPlayer.setJavaUuid(packet.getUuid());
cachedPlayer.setPosition(Vector3f.from(packet.getX(), packet.getY(), packet.getZ()));
cachedPlayer.setRotation(Vector3f.from(packet.getYaw(), packet.getPitch(), 0));
if(cachedPlayer.getProfile().getName() != null) {
cachedPlayer.getMetadata().put(EntityData.NAMETAG, cachedPlayer.getProfile().getName());
}
cachedPlayer.spawn(session);

if(session.getProxy().getConfiguration().getRemoteAuthType() == RemoteAuthType.OFFLINE) {
Expand All @@ -73,7 +76,7 @@ public void translate(ProxySession session, ServerSpawnPlayerPacket packet) {
ImageData skinData = SkinUtils.fetchSkin(session, profile);
if (skinData == null) return;

ImageData capeData = SkinUtils.fetchUnofficialCape(profile);
ImageData capeData = SkinUtils.fetchCape(session, profile);
if(capeData == null) capeData = ImageData.EMPTY;

GameProfile.TextureModel model = null;
Expand Down
94 changes: 65 additions & 29 deletions proxy/src/main/java/org/dragonet/proxy/util/SkinUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,50 +26,39 @@
import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import net.minidev.json.JSONValue;
import org.dragonet.proxy.DragonProxy;
import org.dragonet.proxy.network.session.ProxySession;
import org.dragonet.proxy.network.session.cache.PlayerListCache;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Collections;
import java.util.UUID;

@Log4j2
public class SkinUtils {
private static final String NORMAL_RESOURCE_PATCH = "ewogICAiZ2VvbWV0cnkiIDogewogICAgICAiZGVmYXVsdCIgOiAiZ2VvbWV0cnkuaHVtYW5vaWQuY3VzdG9tIgogICB9Cn0K";
private static final String SLIM_RESOURCE_PATCH = "ewogICAiZ2VvbWV0cnkiIDogewogICAgICAiZGVmYXVsdCIgOiAiZ2VvbWV0cnkuaHVtYW5vaWQuY3VzdG9tU2xpbSIKICAgfQp9";

private static final SessionService service = new SessionService();

public static ImageData STEVE_SKIN;

static {
try {
STEVE_SKIN = parseBufferedImage(ImageIO.read(DragonProxy.class.getClassLoader().getResource("skin_steve.png")));
STEVE_SKIN = parseBufferedImage(ImageIO.read(DragonProxy.class.getClassLoader().getResource("skin_steve.png")), false);
} catch (IOException e) {
e.printStackTrace();
}
}

public static SerializedSkin createSkinEntry(ImageData skinImage, GameProfile.TextureModel model, ImageData capeImage) {
// Skin Geometry is hard coded otherwise players will turn invisible if joining with custom models
String skinResourcePatch = NORMAL_RESOURCE_PATCH;
if(model == GameProfile.TextureModel.SLIM) {
skinResourcePatch = SLIM_RESOURCE_PATCH;
}

String randomId = UUID.randomUUID().toString();
return SerializedSkin.of(
randomId,
new String(Base64.getDecoder().decode(skinResourcePatch)),
convertLegacyGeometryName((model == GameProfile.TextureModel.SLIM) ? "Slim" : ""),
skinImage,
Collections.emptyList(),
capeImage,
Expand Down Expand Up @@ -104,7 +93,7 @@ public static ImageData fetchSkin(ProxySession session, GameProfile profile) {
}
if(texture != null) {
try {
ImageData skin = parseBufferedImage(ImageIO.read(new URL(texture.getURL())));
ImageData skin = parseBufferedImage(ImageIO.read(new URL(texture.getURL())), false);
playerListCache.getRemoteSkinCache().put(profile.getId(), skin); // Cache the skin
return skin;
} catch (IOException e) {
Expand All @@ -115,23 +104,50 @@ public static ImageData fetchSkin(ProxySession session, GameProfile profile) {
}

/**
* Checks if a player has an unofficial cape and if so downloads it from
* Checks if a player has a mojang cape or unofficial cape and if so downloads it from
* their servers
*/
public static ImageData fetchUnofficialCape(GameProfile profile) {
for(CapeServers server : CapeServers.values()) {
try {
URL url = new URL(server.getUrl(profile));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
public static ImageData fetchCape(ProxySession session, GameProfile profile) {
// TODO: HANDLE RATE LIMITING
PlayerListCache playerListCache = session.getPlayerListCache();

if (connection.getResponseCode() == 404) {
return null;
}
// Check if the cape is already cached
if(playerListCache.getRemoteCapeCache().containsKey(profile.getId())) {
//log.warn("Retrieving from cache: " + profile.getName());
return playerListCache.getRemoteCapeCache().get(profile.getId());
}

return parseBufferedImage(ImageIO.read(connection.getInputStream()));
} catch (IOException e) {}
GameProfile.Texture texture;
try {
texture = profile.getTexture(GameProfile.TextureType.CAPE);
} catch (PropertyException e) {
return null;
}
return null;

if(texture != null) {
try {
ImageData cape = parseBufferedImage(ImageIO.read(new URL(texture.getURL())), false);
playerListCache.getRemoteCapeCache().put(profile.getId(), cape); // Cache the cape
return cape;
} catch (IOException e) {
log.warn("Failed to fetch cape for player " + profile.getName() + ": " + e.getMessage());
}
} else {
for (CapeServers server : CapeServers.values()) {
try {
URL url = new URL(server.getUrl(profile));
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

if (connection.getResponseCode() != 404) {
log.warn(String.format("%s has cape at %s", profile.getName(), texture.getURL()));
return parseBufferedImage(ImageIO.read(connection.getInputStream()), true);
}
} catch (IOException e) {
log.warn("Failed to fetch cape for player " + profile.getName() + ": " + e.getMessage());
}
}
}
return ImageData.EMPTY;
}

@RequiredArgsConstructor
Expand All @@ -158,8 +174,22 @@ private enum CapeUrlType {
USERNAME
}

private static ImageData parseBufferedImage(BufferedImage image) {
FastByteArrayOutputStream out = new FastByteArrayOutputStream();
private static ImageData parseBufferedImage(BufferedImage image, boolean cape) {
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
int bedrockSkinSize = (imageWidth * imageHeight) * 4;

//Capes need to be 64x32, 128x64 etc otherwise they will render weird. This is an issue i had on MinecratCapes
if(cape) {
imageWidth = 64;
imageHeight = 32;
while((imageWidth < image.getWidth()) || (imageHeight < image.getHeight())) {
imageWidth *= 2;
imageHeight *= 2;
}
}

FastByteArrayOutputStream out = new FastByteArrayOutputStream(bedrockSkinSize);
for(int y = 0; y < image.getHeight(); ++y) {
for(int x = 0; x < image.getWidth(); ++x) {
Color color = new Color(image.getRGB(x, y), true);
Expand All @@ -169,7 +199,13 @@ private static ImageData parseBufferedImage(BufferedImage image) {
out.write(color.getAlpha());
}
}

image.flush();

return ImageData.of(image.getWidth(), image.getHeight(), out.array);
}

private static String convertLegacyGeometryName(String geometryModel) {
return "{\"geometry\" : {\"default\" : \"geometry.humanoid.custom" + JSONValue.escape(geometryModel) + "\"}}";
}
}

0 comments on commit 58288b8

Please sign in to comment.