Added proper alex model support to HDSkins. Skin servers should remember what the "model" parameter was when saving a skin and include it in their responses.

This commit is contained in:
Sollace 2018-06-29 23:33:05 +02:00
parent 8a31d6c48f
commit 1e52830c91
13 changed files with 113 additions and 64 deletions

View file

@ -231,7 +231,7 @@ public final class HDSkinManager implements IResourceManagerReloadListener {
return null;
IImageBuffer buffer = new ImageBufferDownloadHD();
PreviewTexture skinTexture = new PreviewTexture(url.getUrl(), def, type == Type.SKIN ? new IImageBuffer() {
PreviewTexture skinTexture = new PreviewTexture(url.getMetadata("model"), url.getUrl(), def, type == Type.SKIN ? new IImageBuffer() {
@Override
@Nullable
public BufferedImage parseUserSkin(BufferedImage image) {

View file

@ -10,8 +10,12 @@ public class PreviewTexture extends ThreadDownloadImageData {
private boolean uploaded;
public PreviewTexture(String url, ResourceLocation fallbackTexture, @Nullable IImageBuffer imageBuffer) {
private String model;
public PreviewTexture(String model, String url, ResourceLocation fallbackTexture, @Nullable IImageBuffer imageBuffer) {
super(null, url, fallbackTexture, imageBuffer);
this.model = model;
}
public boolean isTextureUploaded() {
@ -23,4 +27,12 @@ public class PreviewTexture extends ThreadDownloadImageData {
super.deleteGlTexture();
this.uploaded = true;
}
public boolean hasModel() {
return model != null;
}
public boolean usesThinArms() {
return "thin".equals(model);
}
}

View file

@ -16,6 +16,7 @@ import net.minecraft.client.resources.SkinManager;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumHandSide;
import net.minecraft.util.ResourceLocation;
@ -49,9 +50,10 @@ public class EntityPlayerModel extends EntityLivingBase {
private DynamicTexture localElytraTexture;
private TextureManager textureManager;
public final GameProfile profile;
public boolean isSwinging = false;
protected boolean remoteSkin = false;
protected boolean hasLocalTexture = false;
protected boolean previewThinArms = false;
public EntityPlayerModel(GameProfile profile) {
super(null);
@ -59,8 +61,8 @@ public class EntityPlayerModel extends EntityLivingBase {
this.textureManager = Minecraft.getMinecraft().getTextureManager();
this.remoteSkinResource = new ResourceLocation("skins/preview_" + this.profile.getName() + ".png");
this.remoteElytraResource = new ResourceLocation("elytras/preview_" + this.profile.getName() + ".png");
this.localSkinResource = NO_SKIN;
this.localElytraResource = NO_ELYTRA;
this.localSkinResource = getBlankSkin();
this.localElytraResource = getBlankElytra();
this.textureManager.deleteTexture(this.remoteSkinResource);
this.textureManager.deleteTexture(this.remoteElytraResource);
}
@ -74,8 +76,8 @@ public class EntityPlayerModel extends EntityLivingBase {
this.textureManager.deleteTexture(this.remoteElytraResource);
}
this.remoteSkinTexture = HDSkinManager.getPreviewTexture(this.remoteSkinResource, this.profile, Type.SKIN, NO_SKIN, listener);
this.remoteElytraTexture = HDSkinManager.getPreviewTexture(this.remoteElytraResource, this.profile, Type.ELYTRA, NO_ELYTRA, null);
this.remoteSkinTexture = HDSkinManager.getPreviewTexture(this.remoteSkinResource, this.profile, Type.SKIN, getBlankSkin(), listener);
this.remoteElytraTexture = HDSkinManager.getPreviewTexture(this.remoteElytraResource, this.profile, Type.ELYTRA, getBlankElytra(), null);
}
@ -94,7 +96,7 @@ public class EntityPlayerModel extends EntityLivingBase {
bufferedImage = new ImageBufferDownloadHD().parseUserSkin(image);
assert bufferedImage != null;
} catch (IOException var4) {
this.localSkinResource = NO_SKIN;
this.localSkinResource = getBlankSkin();
var4.printStackTrace();
return;
}
@ -113,7 +115,7 @@ public class EntityPlayerModel extends EntityLivingBase {
try {
bufferedImage = ImageIO.read(skinTextureFile);
} catch (IOException var4) {
this.localElytraResource = NO_ELYTRA;
this.localElytraResource = getBlankElytra();
var4.printStackTrace();
return;
}
@ -125,6 +127,14 @@ public class EntityPlayerModel extends EntityLivingBase {
}
}
protected ResourceLocation getBlankSkin() {
return NO_SKIN;
}
protected ResourceLocation getBlankElytra() {
return NO_ELYTRA;
}
public boolean isUsingLocalTexture() {
return !this.remoteSkin && this.hasLocalTexture;
}
@ -137,13 +147,13 @@ public class EntityPlayerModel extends EntityLivingBase {
if (this.localSkinTexture != null) {
this.textureManager.deleteTexture(this.localSkinResource);
this.localSkinTexture = null;
this.localSkinResource = NO_SKIN;
this.localSkinResource = getBlankSkin();
this.hasLocalTexture = false;
}
if (this.localElytraTexture != null) {
this.textureManager.deleteTexture(this.localElytraResource);
this.localElytraTexture = null;
this.localElytraResource = NO_ELYTRA;
this.localElytraResource = getBlankElytra();
this.hasLocalTexture = false;
}
}
@ -157,21 +167,36 @@ public class EntityPlayerModel extends EntityLivingBase {
return this.remoteSkin && this.remoteElytraTexture != null ? this.remoteElytraResource : localElytraResource;
}
public void swingArm() {
if (!this.isSwinging || this.swingProgressInt >= 4 || this.swingProgressInt < 0) {
public void setPreviewThinArms(boolean thinArms) {
previewThinArms = thinArms;
}
public boolean usesThinSkin() {
if (isTextureSetupComplete() && remoteSkinTexture.hasModel()) {
return remoteSkinTexture.usesThinArms();
}
return previewThinArms;
}
@Override
public void swingArm(EnumHand hand) {
super.swingArm(hand);
if (!this.isSwingInProgress || this.swingProgressInt >= 4 || this.swingProgressInt < 0) {
this.swingProgressInt = -1;
this.isSwinging = true;
this.isSwingInProgress = true;
this.swingingHand = hand;
}
}
public void updateModel() {
this.prevSwingProgress = this.swingProgress;
if (this.isSwinging) {
if (this.isSwingInProgress) {
++this.swingProgressInt;
if (this.swingProgressInt >= 8) {
this.swingProgressInt = 0;
this.isSwinging = false;
this.isSwingInProgress = false;
}
} else {
this.swingProgressInt = 0;

View file

@ -27,6 +27,7 @@ import net.minecraft.client.resources.I18n;
import net.minecraft.init.Items;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Session;
import net.minecraft.util.math.MathHelper;
@ -63,6 +64,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
private GuiButton btnClear;
private GuiButton btnBack;
private GuiButton btnModeSkin;
private GuiButton btnModeSkinnySkin;
private GuiButton btnModeElytra;
protected EntityPlayerModel localPlayer;
@ -89,6 +91,7 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
private static GuiSkins instance;
private MinecraftProfileTexture.Type textureType = SKIN;
private boolean thinArmType = false;
public GuiSkins() {
Minecraft minecraft = Minecraft.getMinecraft();
@ -181,12 +184,18 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
ItemStack skin = new ItemStack(Items.LEATHER_LEGGINGS);
Items.LEATHER_LEGGINGS.setColor(skin, 0x3c5dcb);
this.buttonList.add(this.btnModeElytra = new GuiItemStackButton(5, 2, 24, new ItemStack(Items.ELYTRA)));
this.buttonList.add(this.btnModeSkin = new GuiItemStackButton(4, 2, 2, skin));
skin = new ItemStack(Items.LEATHER_LEGGINGS);
Items.LEATHER_LEGGINGS.setColor(skin, 0xfff500);
this.buttonList.add(this.btnModeSkinnySkin = new GuiItemStackButton(6, 2, 24, skin));
this.buttonList.add(this.btnModeElytra = new GuiItemStackButton(5, 2, 46, new ItemStack(Items.ELYTRA)));
this.btnUpload.enabled = false;
this.btnBrowse.enabled = !this.mc.isFullScreen();
(this.textureType == SKIN ? this.btnModeSkin : this.btnModeElytra).enabled = false;
this.btnModeSkin.enabled = this.thinArmType || this.textureType != SKIN;
this.btnModeSkinnySkin.enabled = !this.thinArmType || this.textureType != SKIN;
this.btnModeElytra.enabled = this.textureType == SKIN;
}
@ -282,15 +291,24 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
this.mc.displayGuiScreen(new GuiMainMenu());
}
if (guiButton.id == this.btnModeSkin.id || guiButton.id == this.btnModeElytra.id) {
if (guiButton.id == this.btnModeSkin.id || guiButton.id == this.btnModeElytra.id || guiButton.id == this.btnModeSkinnySkin.id) {
ItemStack stack;
if (guiButton.id == this.btnModeSkin.id) {
this.thinArmType = false;
this.textureType = SKIN;
this.btnModeElytra.enabled = true;
this.btnModeSkinnySkin.enabled = true;
stack = ItemStack.EMPTY;
} else if (guiButton.id == this.btnModeSkinnySkin.id) {
this.thinArmType = true;
this.textureType = SKIN;
this.btnModeSkin.enabled = true;
this.btnModeElytra.enabled = true;
stack = ItemStack.EMPTY;
} else {
this.textureType = ELYTRA;
this.btnModeSkin.enabled = true;
this.btnModeSkinnySkin.enabled = true;
stack = new ItemStack(Items.ELYTRA);
}
guiButton.enabled = false;
@ -301,6 +319,9 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
// put on or take off the elytra
this.localPlayer.setItemStackToSlot(EntityEquipmentSlot.CHEST, stack);
this.remotePlayer.setItemStackToSlot(EntityEquipmentSlot.CHEST, stack);
this.localPlayer.setPreviewThinArms(thinArmType);
this.remotePlayer.setPreviewThinArms(thinArmType);
}
}
@ -317,8 +338,8 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
int bottom = this.height - 40;
int mid = this.width / 2;
if ((mouseX > 30 && mouseX < mid - 30 || mouseX > mid + 30 && mouseX < this.width - 30) && mouseY > top && mouseY < bottom) {
this.localPlayer.swingArm();
this.remotePlayer.swingArm();
this.localPlayer.swingArm(EnumHand.MAIN_HAND);
this.remotePlayer.swingArm(EnumHand.MAIN_HAND);
}
}
@ -527,11 +548,13 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
Gui.drawRect(40, this.height / 2 - 12, this.width / 2 - 40, this.height / 2 + 12, 0xB0000000);
this.fontRenderer.drawStringWithShadow(this.skinMessage, (int) (xPos1 - opacity), this.height / 2 - 4, 0xffffff);
}
if (this.btnModeSkin.isMouseOver() || this.btnModeElytra.isMouseOver()) {
if (this.btnModeSkin.isMouseOver() || this.btnModeElytra.isMouseOver() || this.btnModeSkinnySkin.isMouseOver()) {
int y = Math.max(mouseY, 16);
String text;
if (this.btnModeSkin.isMouseOver()) {
text = "hdskins.mode.skin";
} else if (this.btnModeSkinnySkin.isMouseOver()) {
text = "hdskins.mode.skinny";
} else {
text = "hdskins.mode.elytra";
}
@ -643,14 +666,14 @@ public class GuiSkins extends GuiScreen implements FutureCallback<SkinUploadResp
private void clearUploadedSkin(Session session) {
this.uploadingSkin = true;
this.skinUploadMessage = I18n.format("hdskins.request");
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, null, this.textureType), this);
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, null, this.textureType, this.thinArmType), this);
}
private void uploadSkin(Session session, @Nullable File skinFile) {
this.uploadingSkin = true;
this.skinUploadMessage = I18n.format("hdskins.upload");
Path path = skinFile == null ? null : skinFile.toPath();
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, path, this.textureType), this);
Futures.addCallback(HDSkinManager.INSTANCE.getGatewayServer().uploadSkin(session, path, this.textureType, this.thinArmType), this);
}
private void setUploadError(@Nullable String error) {

View file

@ -27,7 +27,7 @@ public class RenderPlayerModel<M extends EntityPlayerModel> extends RenderLiving
protected final ResourceLocation TEXTURE_ELYTRA = new ResourceLocation("textures/entity/elytra.png");
private static final ModelPlayer FAT = new ModelPlayer(0, false);
//private static final ModelPlayer THIN = new ModelPlayer(0, true);
private static final ModelPlayer THIN = new ModelPlayer(0, true);
public RenderPlayerModel(RenderManager renderer) {
super(renderer, FAT, 0.0F);
@ -83,7 +83,7 @@ public class RenderPlayerModel<M extends EntityPlayerModel> extends RenderLiving
}
public ModelPlayer getEntityModel(M entity) {
return FAT;
return entity.usesThinSkin() ? THIN : FAT;
}
@Override

View file

@ -88,7 +88,7 @@ public class LegacySkinServer implements SkinServer {
}
@Override
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type) {
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinSkinType) {
if (Strings.isNullOrEmpty(this.gateway))
return Futures.immediateFailedFuture(new NullPointerException("gateway url is blank"));
@ -96,27 +96,28 @@ public class LegacySkinServer implements SkinServer {
return HDSkinManager.skinUploadExecutor.submit(() -> {
verifyServerConnection(session, SERVER_ID);
Map<String, ?> data = image == null ? getClearData(session, type) : getUploadData(session, type, image);
Map<String, ?> data = image == null ? getClearData(session, type) : getUploadData(session, type, (thinSkinType ? "thin" : "default"), image);
ThreadMultipartPostUpload upload = new ThreadMultipartPostUpload(this.gateway, data);
String response = upload.uploadMultipart();
return new SkinUploadResponse(response.equalsIgnoreCase("OK"), response);
});
}
private static Map<String, ?> getData(Session session, MinecraftProfileTexture.Type type, String param, Object val) {
private static Map<String, ?> getData(Session session, MinecraftProfileTexture.Type type, String model, String param, Object val) {
return ImmutableMap.of(
"user", session.getUsername(),
"uuid", session.getPlayerID(),
"type", type.toString().toLowerCase(Locale.US),
"model", model,
param, val);
}
private static Map<String, ?> getClearData(Session session, MinecraftProfileTexture.Type type) {
return getData(session, type, "clear", "1");
return getData(session, type, "default", "clear", "1");
}
private static Map<String, ?> getUploadData(Session session, MinecraftProfileTexture.Type type, Path skinFile) {
return getData(session, type, type.toString().toLowerCase(Locale.US), skinFile);
private static Map<String, ?> getUploadData(Session session, MinecraftProfileTexture.Type type, String model, Path skinFile) {
return getData(session, type, model, type.toString().toLowerCase(Locale.US), skinFile);
}
private static String getPath(String address, MinecraftProfileTexture.Type type, GameProfile profile) {

View file

@ -20,7 +20,7 @@ public interface SkinServer {
Optional<MinecraftProfileTexture> getPreviewTexture(MinecraftProfileTexture.Type type, GameProfile profile);
ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type);
ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType);
static SkinServer from(String server) {
int i = server.indexOf(':');

View file

@ -32,7 +32,7 @@ public class ValhallaSkinServer implements SkinServer {
}
@Override
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type) {
public ListenableFuture<SkinUploadResponse> uploadSkin(Session session, @Nullable Path image, MinecraftProfileTexture.Type type, boolean thinArmType) {
return null;
}

View file

@ -17,6 +17,7 @@ hdskins.upload=Uploading skin please wait...
hdskins.local=Local Skin
hdskins.server=Server Skin
hdskins.mode.skin=Skin
hdskins.mode.skin=Skin (Steve)
hdskins.mode.skinny=Skin (Alex)
hdskins.mode.elytra=Elytra

View file

@ -65,7 +65,14 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
* @param resource A texture resource
*/
public Pony getPony(ResourceLocation resource, boolean slim) {
return poniesCache.computeIfAbsent(resource, res -> new Pony(res, slim));
Pony pony = poniesCache.computeIfAbsent(resource, res -> new Pony(res, slim));
if (pony.usesThinArms() != slim) {
pony = new Pony(resource, slim);
poniesCache.put(resource, pony);
}
return pony;
}
/**

View file

@ -1,12 +1,8 @@
package com.minelittlepony.hdskins.gui;
import java.io.File;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.minecraft.MinecraftProfileTexture.Type;
import com.voxelmodpack.hdskins.gui.EntityPlayerModel;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
/**
@ -20,25 +16,8 @@ public class EntityPonyModel extends EntityPlayerModel {
super(profile);
}
public void setLocalTexture(File skinTextureFile, Type type) {
super.setLocalTexture(skinTextureFile, type);
}
public ResourceLocation getSkinTexture() {
ResourceLocation skin = super.getSkinTexture();
if (skin == NO_SKIN) {
// We're a pony, might as well look like one.
@Override
protected ResourceLocation getBlankSkin() {
return NO_SKIN_PONY;
}
return skin;
}
public void swingArm() {
super.swingArm();
// Fixes the preview model swinging the wrong arm.
// Who's maintaining HDSkins anyway?
swingingHand = EnumHand.MAIN_HAND;
}
}

View file

@ -1,7 +1,6 @@
package com.minelittlepony.hdskins.gui;
import com.minelittlepony.MineLittlePony;
import com.minelittlepony.PonyManager;
import com.minelittlepony.model.ModelWrapper;
import com.minelittlepony.model.components.PonyElytra;
import com.minelittlepony.pony.data.Pony;
@ -40,9 +39,7 @@ public class RenderPonyModel extends RenderPlayerModel<EntityPonyModel> {
return super.getEntityModel(playermodel);
}
// TODO: We can't find out whether to use thin arms just by the texture.
// Maybe a trigger pixel for thin arms? #FutureThoughts
Pony thePony = MineLittlePony.getInstance().getManager().getPony(loc, PonyManager.isSlimSkin(playermodel.profile.getId()));
Pony thePony = MineLittlePony.getInstance().getManager().getPony(loc, playermodel.usesThinSkin());
if (thePony.getRace(false).isHuman()) {
return super.getEntityModel(playermodel);

View file

@ -136,6 +136,10 @@ public class Pony {
return metadata.getRace().getEffectiveRace(MineLittlePony.getConfig().getEffectivePonyLevel(ignorePony));
}
public boolean usesThinArms() {
return smallArms;
}
public ResourceLocation getTexture() {
return texture;
}