From cee1291e98032682220bcb5b72441c64468bd614 Mon Sep 17 00:00:00 2001 From: citrons Date: Sun, 5 Nov 2023 15:53:46 -0600 Subject: reimplement skin loading, asynchronously --- .../mondecitronne/homunculus/EntityHomunculus.java | 8 ++- .../homunculus/GameProfileFetcher.java | 75 +++++++++++++++++++-- .../com/mondecitronne/homunculus/Homunculus.java | 25 +++++-- .../homunculus/client/RenderHomunculus.java | 24 +------ .../homunculus/proxy/ClientProxy.java | 8 ++- .../homunculus/proxy/ServerProxy.java | 15 +++++ .../homunculus/skin/FallbackSkin.java | 2 +- .../mondecitronne/homunculus/skin/PlayerSkin.java | 18 +++-- .../textures/entity/homunculus_fallback.png | Bin 0 -> 2700 bytes 9 files changed, 130 insertions(+), 45 deletions(-) create mode 100644 src/main/java/com/mondecitronne/homunculus/proxy/ServerProxy.java create mode 100644 src/main/resources/assets/homunculus/textures/entity/homunculus_fallback.png diff --git a/src/main/java/com/mondecitronne/homunculus/EntityHomunculus.java b/src/main/java/com/mondecitronne/homunculus/EntityHomunculus.java index d070f08..d74b905 100644 --- a/src/main/java/com/mondecitronne/homunculus/EntityHomunculus.java +++ b/src/main/java/com/mondecitronne/homunculus/EntityHomunculus.java @@ -34,10 +34,11 @@ public class EntityHomunculus extends EntityLiving { } private boolean isPlayerProfileUpdated(GameProfile a, GameProfile b) { - if (b == null) { + if (b == null || b.getName() == null) { return a != null; } else { - return !a.getName().toLowerCase().equals(b.getName().toLowerCase()) || (a.getId() != null && !b.getId().equals(a.getId())); + return !a.getName().toLowerCase().equals(b.getName().toLowerCase()) || + (b.getId() != null && a.getId() != null && !b.getId().equals(a.getId())); } } @@ -55,6 +56,7 @@ public class EntityHomunculus extends EntityLiving { GameProfile sourceProfile = NBTUtil.readGameProfileFromNBT(sourceNBT); if (sourceProfile == null || StringUtil.isNullOrEmpty(sourceProfile.getName())) { skin = null; + break; } if (!(skin instanceof PlayerSkin) || isPlayerProfileUpdated(((PlayerSkin) skin).getPlayerProfile(), sourceProfile)) { skin = new PlayerSkin(sourceProfile, getEntityWorld().isRemote); @@ -73,6 +75,7 @@ public class EntityHomunculus extends EntityLiving { } else { skin = null; } + break; default: skin = null; break; @@ -121,6 +124,7 @@ public class EntityHomunculus extends EntityLiving { NBTTagCompound sourceCompound = new NBTTagCompound(); sourceCompound.setTag("Name", profileCompound.getTag("Name")); if (profileCompound.hasKey("Id")) { + // save ID so that if a player changes their name, the skin is invalidated rather than pulling the skin of whoever takes the name sourceCompound.setTag("Id", profileCompound.getTag("Id")); } sourceCompound.setString("Type", "player"); diff --git a/src/main/java/com/mondecitronne/homunculus/GameProfileFetcher.java b/src/main/java/com/mondecitronne/homunculus/GameProfileFetcher.java index 8591635..0651d34 100644 --- a/src/main/java/com/mondecitronne/homunculus/GameProfileFetcher.java +++ b/src/main/java/com/mondecitronne/homunculus/GameProfileFetcher.java @@ -1,26 +1,87 @@ package com.mondecitronne.homunculus; +import java.io.File; +import java.net.Proxy; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; +import com.google.common.collect.Maps; +import com.mojang.authlib.Agent; import com.mojang.authlib.GameProfile; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; public class GameProfileFetcher { + private static MinecraftSessionService sessionService; + private static GameProfileRepository profileRepo; + // there could be contention if thread pool has more than one thread + private static final ExecutorService THREAD_POOL = new ThreadPoolExecutor(0, 1, 1L, TimeUnit.MINUTES, new LinkedBlockingQueue()); + + private static final Map fetchersByName = Maps.newHashMap(); + private static final Map fetchersById = Maps.newHashMap(); + @Nullable private GameProfile profile; - public static void initProfileCache() { + public static void init(File dataDir) { + YggdrasilAuthenticationService auth = new YggdrasilAuthenticationService(Proxy.NO_PROXY, UUID.randomUUID().toString()); + sessionService = auth.createMinecraftSessionService(); + profileRepo = auth.createProfileRepository(); } - public GameProfileFetcher(GameProfile profileIn) { + private GameProfileFetcher(GameProfile profileIn) { dispatchFetchProfile(profileIn); } - @Nullable - public GameProfile getGameProfile() { - synchronized (this) { - return profile; + public static GameProfileFetcher fetchProfile(GameProfile profile) { + if (profile.getId() != null && fetchersById.containsKey(profile.getId())) { + // if the profile specifies an ID, ensure that the fetcher with the correct ID returns, as it will reject a profile with the wrong ID + return fetchersById.get(profile.getId()); + } else if (fetchersByName.containsKey(profile.getName())) { + return fetchersByName.get(profile.getName()); + } else { + GameProfileFetcher fetcher = new GameProfileFetcher(profile); + fetchersByName.put(profile.getName(), fetcher); + if (profile.getId() != null) { + fetchersById.put(profile.getId(), fetcher); + } + return fetcher; } } - private static void dispatchFetchProfile(GameProfile profileIn) { + @Nullable + public synchronized GameProfile getGameProfile() { + return profile; + } + + private void dispatchFetchProfile(GameProfile profileIn) { + THREAD_POOL.submit(new Runnable() { + public void run() { + class LocalCallback implements ProfileLookupCallback { + public GameProfile fetched; + public void onProfileLookupSucceeded(GameProfile success) { + fetched = success; + } + public void onProfileLookupFailed(GameProfile failed, Exception exception) { + fetched = null; + } + }; + LocalCallback callback = new LocalCallback(); + profileRepo.findProfilesByNames(new String[] {profileIn.getName()}, Agent.MINECRAFT, callback); + + GameProfile fetched = sessionService.fillProfileProperties(callback.fetched, false); + if (profileIn.getId() == null || fetched.getId().equals(profileIn.getId())) { + synchronized (this) { + profile = fetched; + } + } + } + }); } } diff --git a/src/main/java/com/mondecitronne/homunculus/Homunculus.java b/src/main/java/com/mondecitronne/homunculus/Homunculus.java index deb97b4..a354e60 100644 --- a/src/main/java/com/mondecitronne/homunculus/Homunculus.java +++ b/src/main/java/com/mondecitronne/homunculus/Homunculus.java @@ -1,12 +1,14 @@ package com.mondecitronne.homunculus; +import java.io.File; + +import org.apache.logging.log4j.Logger; import com.mondecitronne.homunculus.proxy.Proxy; -import net.minecraftforge.fml.common.event.FMLInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; import net.minecraftforge.fml.common.Mod; -import org.apache.logging.log4j.Logger; import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; @Mod(modid = Homunculus.MODID, name = Homunculus.NAME, version = Homunculus.VERSION) public class Homunculus { @@ -14,13 +16,14 @@ public class Homunculus { public static final String NAME = "Homunculus"; public static final String VERSION = "1.0"; - @SidedProxy(clientSide = "com.mondecitronne.homunculus.proxy.ClientProxy", serverSide = "com.mondecitronne.homunculus.proxy.Proxy") + @SidedProxy(clientSide = "com.mondecitronne.homunculus.proxy.ClientProxy", serverSide = "com.mondecitronne.homunculus.proxy.ServerProxy") public static Proxy proxy; @Mod.Instance public static Homunculus instance; - static Logger logger; + public Logger logger; + File dataDir; @Mod.EventHandler public void preInit(FMLPreInitializationEvent event) { @@ -37,4 +40,14 @@ public class Homunculus { public void postInit(FMLPostInitializationEvent event) { proxy.postInit(event); } + + public File getDataDir() { + return dataDir; + } + + public void putDataDirIn(File dir) { + assert(dataDir == null); + dataDir = new File(dir, MODID + "_data"); + dataDir.mkdir(); + } } diff --git a/src/main/java/com/mondecitronne/homunculus/client/RenderHomunculus.java b/src/main/java/com/mondecitronne/homunculus/client/RenderHomunculus.java index 5523f3d..159602a 100644 --- a/src/main/java/com/mondecitronne/homunculus/client/RenderHomunculus.java +++ b/src/main/java/com/mondecitronne/homunculus/client/RenderHomunculus.java @@ -4,11 +4,9 @@ import java.util.Map; import javax.annotation.Nonnull; import com.google.common.collect.Maps; import com.mondecitronne.homunculus.EntityHomunculus; -import com.mondecitronne.homunculus.skin.Skin; import net.minecraft.client.renderer.entity.Render; import net.minecraft.client.renderer.entity.RenderLivingBase; import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.client.resources.DefaultPlayerSkin; import net.minecraftforge.fml.client.registry.IRenderFactory; import net.minecraft.client.model.ModelPlayer; import net.minecraft.util.ResourceLocation; @@ -25,33 +23,17 @@ public class RenderHomunculus extends RenderLivingBase { @Nonnull public String getModelType(EntityHomunculus entity) { - String modelType = DefaultPlayerSkin.getSkinType(entity.getUniqueID()); - Skin skin = entity.getSkin(); - if (skin != null) { - String type = skin.getModelType(); - if (type != null) { - modelType = type; - } - } - return modelType; + return "default"; } @Override protected ResourceLocation getEntityTexture(@Nonnull EntityHomunculus entity) { - ResourceLocation texture = DefaultPlayerSkin.getDefaultSkin(entity.getUniqueID()); - Skin skin = entity.getSkin(); - if (skin != null) { - ResourceLocation tex = skin.getTexture(); - if (tex != null) { - texture = tex; - } - } - return texture; + return entity.getSkin().getTexture(); } @Override public void doRender(EntityHomunculus entity, double x, double y, double z, float entityYaw, float partialTicks) { - mainModel = modelTypes.get(getModelType(entity)); + mainModel = modelTypes.get(entity.getSkin().getModelType()); super.doRender(entity, x, y, z, entityYaw, partialTicks); } diff --git a/src/main/java/com/mondecitronne/homunculus/proxy/ClientProxy.java b/src/main/java/com/mondecitronne/homunculus/proxy/ClientProxy.java index 8fbcbcb..906a08f 100644 --- a/src/main/java/com/mondecitronne/homunculus/proxy/ClientProxy.java +++ b/src/main/java/com/mondecitronne/homunculus/proxy/ClientProxy.java @@ -1,7 +1,10 @@ package com.mondecitronne.homunculus.proxy; import com.mondecitronne.homunculus.EntityHomunculus; +import com.mondecitronne.homunculus.GameProfileFetcher; +import com.mondecitronne.homunculus.Homunculus; import com.mondecitronne.homunculus.client.RenderHomunculus; +import net.minecraft.client.Minecraft; import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.common.Mod; @@ -14,9 +17,10 @@ public class ClientProxy extends Proxy { @Override public void preInit(FMLPreInitializationEvent e) { super.preInit(e); - //PlayerSkin.initProfileCache(); - RenderingRegistry.registerEntityRenderingHandler(EntityHomunculus.class, RenderHomunculus.FACTORY); + + Homunculus.instance.putDataDirIn(Minecraft.getMinecraft().gameDir); + GameProfileFetcher.init(Homunculus.instance.getDataDir()); } @SubscribeEvent diff --git a/src/main/java/com/mondecitronne/homunculus/proxy/ServerProxy.java b/src/main/java/com/mondecitronne/homunculus/proxy/ServerProxy.java new file mode 100644 index 0000000..f017f03 --- /dev/null +++ b/src/main/java/com/mondecitronne/homunculus/proxy/ServerProxy.java @@ -0,0 +1,15 @@ +package com.mondecitronne.homunculus.proxy; + +import com.mondecitronne.homunculus.GameProfileFetcher; +import com.mondecitronne.homunculus.Homunculus; +import net.minecraftforge.fml.common.FMLCommonHandler; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +public class ServerProxy extends Proxy { + @Override + public void preInit(FMLPreInitializationEvent e) { + super.preInit(e); + Homunculus.instance.putDataDirIn(FMLCommonHandler.instance().getMinecraftServerInstance().getDataDirectory()); + GameProfileFetcher.init(Homunculus.instance.getDataDir()); + } +} diff --git a/src/main/java/com/mondecitronne/homunculus/skin/FallbackSkin.java b/src/main/java/com/mondecitronne/homunculus/skin/FallbackSkin.java index 9baedf4..d36b4d6 100644 --- a/src/main/java/com/mondecitronne/homunculus/skin/FallbackSkin.java +++ b/src/main/java/com/mondecitronne/homunculus/skin/FallbackSkin.java @@ -5,7 +5,7 @@ import com.mondecitronne.homunculus.Homunculus; import net.minecraft.util.ResourceLocation; public class FallbackSkin extends Skin { - static final ResourceLocation FALLBACK_TEXTURE = new ResourceLocation(Homunculus.MODID, "entities/homunculus_fallback"); + static final ResourceLocation FALLBACK_TEXTURE = new ResourceLocation(Homunculus.MODID, "textures/entity/homunculus_fallback.png"); @Override public String getModelType() { diff --git a/src/main/java/com/mondecitronne/homunculus/skin/PlayerSkin.java b/src/main/java/com/mondecitronne/homunculus/skin/PlayerSkin.java index e460c64..7964411 100644 --- a/src/main/java/com/mondecitronne/homunculus/skin/PlayerSkin.java +++ b/src/main/java/com/mondecitronne/homunculus/skin/PlayerSkin.java @@ -13,17 +13,19 @@ public class PlayerSkin extends Skin { private final GameProfile profile; private final GameProfileFetcher profileFetcher; private final boolean isRemote; + boolean profileTexturesLoading; public PlayerSkin(GameProfile profile, boolean isRemote) { this.profile = profile; - profileFetcher = new GameProfileFetcher(this.profile); + profileFetcher = GameProfileFetcher.fetchProfile(profile); this.isRemote = isRemote; + profileTexturesLoading = false; } @Override public boolean isLoaded() { if (this.isRemote) { - return getTexture() != null; + return getTexture() != null && getModelType() != null; } else { return profileFetcher.getGameProfile() != null; } @@ -32,7 +34,7 @@ public class PlayerSkin extends Skin { public GameProfile getPlayerProfile() { GameProfile fetchProfile = profileFetcher.getGameProfile(); if (fetchProfile != null) { - return profileFetcher.getGameProfile(); + return fetchProfile; } else { return profile; } @@ -40,9 +42,13 @@ public class PlayerSkin extends Skin { protected MinecraftProfileTexture getProfileTexture() { assert(this.isRemote); - GameProfile playerProfile = getPlayerProfile(); + GameProfile playerProfile = profileFetcher.getGameProfile(); if (playerProfile != null) { Minecraft minecraft = Minecraft.getMinecraft(); + if (!profileTexturesLoading) { + minecraft.getSkinManager().loadProfileTextures(playerProfile, null, false); + profileTexturesLoading = true; + } Map map = minecraft.getSkinManager().loadSkinFromCache(playerProfile); if (map != null) { return map.get(Type.SKIN); @@ -59,8 +65,8 @@ public class PlayerSkin extends Skin { assert(this.isRemote); if (getTexture() != null) { MinecraftProfileTexture tex = getProfileTexture(); - if (tex != null && tex.getMetadata("model") != null) { - return tex.getMetadata("model"); + if (tex != null) { + return tex.getMetadata("model") != null ? tex.getMetadata("model") : "default"; } } return null; diff --git a/src/main/resources/assets/homunculus/textures/entity/homunculus_fallback.png b/src/main/resources/assets/homunculus/textures/entity/homunculus_fallback.png new file mode 100644 index 0000000..0bd2fc0 Binary files /dev/null and b/src/main/resources/assets/homunculus/textures/entity/homunculus_fallback.png differ -- cgit v1.2.3