diff --git a/src/main/java/net/flashii/mcexts/ProfileResultCache.java b/src/main/java/net/flashii/mcexts/ProfileResultCache.java new file mode 100644 index 0000000..1555817 --- /dev/null +++ b/src/main/java/net/flashii/mcexts/ProfileResultCache.java @@ -0,0 +1,19 @@ +package net.flashii.mcexts; + +import com.mojang.authlib.yggdrasil.ProfileResult; + +public class ProfileResultCache { + private static final long CACHE_TIMEOUT = 21600000; + + public long readTime; + public ProfileResult value; + + public ProfileResultCache(ProfileResult value) { + this.readTime = System.currentTimeMillis(); + this.value = value; + } + + public boolean needsRefresh() { + return readTime < System.currentTimeMillis() - CACHE_TIMEOUT; + } +} diff --git a/src/main/java/net/flashii/mcexts/RPC.java b/src/main/java/net/flashii/mcexts/RPC.java index 7c6089c..35e0e8c 100644 --- a/src/main/java/net/flashii/mcexts/RPC.java +++ b/src/main/java/net/flashii/mcexts/RPC.java @@ -4,7 +4,6 @@ import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import java.io.OutputStream; -import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; diff --git a/src/main/java/net/flashii/mcexts/mixin/PlayerListEntryMixin.java b/src/main/java/net/flashii/mcexts/mixin/PlayerListEntryMixin.java new file mode 100644 index 0000000..56b1a1d --- /dev/null +++ b/src/main/java/net/flashii/mcexts/mixin/PlayerListEntryMixin.java @@ -0,0 +1,19 @@ +package net.flashii.mcexts.mixin; + +import net.minecraft.client.network.PlayerListEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(PlayerListEntry.class) +public abstract class PlayerListEntryMixin { + @ModifyVariable( + method = "texturesSupplier(Lcom/mojang/authlib/GameProfile;)Ljava/util/function/Supplier;", + at = @At("STORE"), + ordinal = 0 + ) + private static boolean texturesSupplier(boolean bl) { + // always enable BL + return true; + } +} diff --git a/src/main/java/net/flashii/mcexts/mixin/PlayerListHudMixin.java b/src/main/java/net/flashii/mcexts/mixin/PlayerListHudMixin.java index 8acf596..8f15471 100644 --- a/src/main/java/net/flashii/mcexts/mixin/PlayerListHudMixin.java +++ b/src/main/java/net/flashii/mcexts/mixin/PlayerListHudMixin.java @@ -1,6 +1,5 @@ package net.flashii.mcexts.mixin; -import net.flashii.mcexts.Tools; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.hud.PlayerListHud; import org.spongepowered.asm.mixin.Mixin; @@ -17,8 +16,6 @@ public abstract class PlayerListHudMixin { ) ) public boolean isInSinglePlayer(MinecraftClient client) { - Tools.Log.info("[FII MIXIN] Redirected isInSingleplayer call in PlayerListHud.render!!!"); - // always enable BL return true; } diff --git a/src/main/java/net/flashii/mcexts/mixin/PlayerSkinProviderMixin.java b/src/main/java/net/flashii/mcexts/mixin/PlayerSkinProviderMixin.java deleted file mode 100644 index cc3ea29..0000000 --- a/src/main/java/net/flashii/mcexts/mixin/PlayerSkinProviderMixin.java +++ /dev/null @@ -1,45 +0,0 @@ -package net.flashii.mcexts.mixin; - -import com.mojang.authlib.GameProfile; -import java.util.concurrent.CompletableFuture; -import net.minecraft.client.texture.PlayerSkinProvider; -import net.minecraft.client.util.SkinTextures; -import net.flashii.mcexts.Tools; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(PlayerSkinProvider.class) -public abstract class PlayerSkinProviderMixin { - @Inject( - method = "fetchSkinTextures(Lcom/mojang/authlib/GameProfile;)Ljava/util/concurrent/CompletableFuture;", - at = @At("RETURN"), - cancellable = true - ) - private void fetchSkinTextures(GameProfile profile, CallbackInfoReturnable> cir) { - Tools.Log.info("[FII MIXIN] Intercepted PlayerSkinProvider.fetchSkinTextures!!!"); - - CompletableFuture future = cir.getReturnValue(); - cir.setReturnValue(future.thenApply(st -> { - Tools.Log.info( - "[FII MIXIN] future texture:{} textureUrl:{} capeTexture:{} elytraTexture:{} model:{} secure:{}", - st.texture(), - st.textureUrl(), - st.capeTexture(), - st.elytraTexture(), - st.model(), - st.secure() - ); - return new SkinTextures( - st.texture(), - st.textureUrl(), - st.capeTexture(), - st.elytraTexture(), - st.model(), - st.secure() - ); - })); - cir.cancel(); - } -} diff --git a/src/main/java/net/flashii/mcexts/mixin/TextureUrlCheckerMixin.java b/src/main/java/net/flashii/mcexts/mixin/TextureUrlCheckerMixin.java index ce03238..30b7723 100644 --- a/src/main/java/net/flashii/mcexts/mixin/TextureUrlCheckerMixin.java +++ b/src/main/java/net/flashii/mcexts/mixin/TextureUrlCheckerMixin.java @@ -2,7 +2,6 @@ package net.flashii.mcexts.mixin; import com.mojang.authlib.yggdrasil.TextureUrlChecker; import net.flashii.mcexts.URLs; -import net.flashii.mcexts.Tools; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -17,8 +16,6 @@ public abstract class TextureUrlCheckerMixin { remap = false ) private static void isAllowedTextureDomain(String url, CallbackInfoReturnable cir) { - Tools.Log.info("[FII MIXIN] Intercepted TextureUrlChecker.isAllowedTextureDomain!!!"); - if(url == null || url.startsWith(URLs.getTexturesHostPrefix())) { cir.setReturnValue(true); cir.cancel(); diff --git a/src/main/java/net/flashii/mcexts/mixin/YggdrasilEnvironmentMixin.java b/src/main/java/net/flashii/mcexts/mixin/YggdrasilEnvironmentMixin.java index e45eb29..229ee75 100644 --- a/src/main/java/net/flashii/mcexts/mixin/YggdrasilEnvironmentMixin.java +++ b/src/main/java/net/flashii/mcexts/mixin/YggdrasilEnvironmentMixin.java @@ -14,8 +14,10 @@ public abstract class YggdrasilEnvironmentMixin { at = @At( value = "NEW", target = "com/mojang/authlib/Environment", - ordinal = 0 - ) + ordinal = 0, + remap = false + ), + remap = false ) private Environment init(String sessionHost, String servicesHost, String name) { return new Environment(URLs.getSessionHost(), servicesHost, name); diff --git a/src/main/java/net/flashii/mcexts/mixin/YggdrasilMinecraftSessionServiceMixin.java b/src/main/java/net/flashii/mcexts/mixin/YggdrasilMinecraftSessionServiceMixin.java index 2b4b5ec..cf5d79f 100644 --- a/src/main/java/net/flashii/mcexts/mixin/YggdrasilMinecraftSessionServiceMixin.java +++ b/src/main/java/net/flashii/mcexts/mixin/YggdrasilMinecraftSessionServiceMixin.java @@ -1,23 +1,82 @@ package net.flashii.mcexts.mixin; +import com.google.common.collect.Iterables; import com.mojang.authlib.GameProfile; import com.mojang.authlib.SignatureState; -import com.mojang.authlib.minecraft.MinecraftProfileTextures; +import com.mojang.authlib.minecraft.client.ObjectMapper; import com.mojang.authlib.properties.Property; import com.mojang.authlib.yggdrasil.ProfileResult; import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; +import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse; +import com.mojang.util.UndashedUuid; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.HashMap; import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import net.minecraft.client.util.SkinTextures; +import net.flashii.mcexts.ProfileResultCache; import net.flashii.mcexts.Tools; +import net.flashii.mcexts.URLs; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(YggdrasilMinecraftSessionService.class) public abstract class YggdrasilMinecraftSessionServiceMixin { + private static ObjectMapper objectMapper = ObjectMapper.create(); + private static HashMap profileCache = new HashMap<>(); + + private static ProfileResult getProfileFromMince(UUID profileId, boolean allowCache) { + if(allowCache && profileCache.containsKey(profileId)) { + ProfileResultCache cachedProfile = profileCache.get(profileId); + if(!cachedProfile.needsRefresh()) + return cachedProfile.value; + + Tools.Log.info("[FII MIXIN] Cache expired!!!!!!!"); + } else { + Tools.Log.info("[FII MIXIN] Forcing new fetch!!!!!!!"); + } + + ProfileResult profileResult = null; + try { + // this should probably have caching idk at what frequency this will get called + URI url; + try { + url = new URI(String.format("%s/session/minecraft/profile/%s", URLs.getSessionHost(), UndashedUuid.toString(profileId))); + } catch(URISyntaxException ex) { + Tools.Log.info("[FII MIXIN] INCORRECTLY FORMATTED PROFILE URL!!!!!!!!!!"); + return profileResult; + } + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(url).GET().build(); + HttpResponse response; + try { + response = client.send(request, HttpResponse.BodyHandlers.ofString()); + } catch(IOException | InterruptedException ex) { + Tools.Log.info("[FII MIXIN] FAILED TO SEND PROFILE REQUEST!!!!!!!!!!"); + return profileResult; + } + + if(response.statusCode() != 200) { + Tools.Log.info("[FII MIXIN] NON-200 PROFILE RESPONSE CODE!!!!!!!!!!"); + return profileResult; + } + + MinecraftProfilePropertiesResponse profileResponse = objectMapper.readValue(response.body(), MinecraftProfilePropertiesResponse.class); + if(profileResponse != null) + profileResult = new ProfileResult(profileResponse.toProfile()); + } finally { + profileCache.put(profileId, new ProfileResultCache(profileResult)); + } + + return profileResult; + } + @Inject( method = "getPackedTextures(Lcom/mojang/authlib/GameProfile;)Lcom/mojang/authlib/properties/Property;", at = @At("RETURN"), @@ -25,27 +84,22 @@ public abstract class YggdrasilMinecraftSessionServiceMixin { remap = false ) private void getPackedTextures(GameProfile profile, CallbackInfoReturnable cir) { - Tools.Log.info("[FII MIXIN] Intercepted YggdrasilMinecraftSessionService.getPackedTextures!!!"); - Tools.Log.info("[FII MIXIN] {}", profile); - + boolean replace = false; Property prop = cir.getReturnValue(); - if(prop == null) - Tools.Log.info("[FII MIXIN] prop is null"); - else - Tools.Log.info("[FII MIXIN] name:{} value:{} signature:{}", prop.name(), prop.value(), prop.signature()); - } + if(prop == null) { + ProfileResult result = getProfileFromMince(profile.getId(), true); + if(result == null) { + Tools.Log.info("[FII MIXIN] PROFILE RESULT IS NULL!!!!!!!!"); + } else { + replace = true; + prop = (Property)Iterables.getFirst(result.profile().getProperties().get("textures"), (Object)null); + } + } - @Inject( - method = "unpackTextures(Lcom/mojang/authlib/properties/Property;)Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;", - at = @At("RETURN"), - cancellable = true, - remap = false - ) - private void unpackTextures(Property packedTextures, CallbackInfoReturnable cir) { - Tools.Log.info("[FII MIXIN] Intercepted YggdrasilMinecraftSessionService.unpackTextures!!!"); - - MinecraftProfileTextures textures = cir.getReturnValue(); - Tools.Log.info("[FII MIXIN] skin:{} cape:{} elytra:{} signature:{}", textures.skin(), textures.cape(), textures.elytra(), textures.signatureState()); + if(replace) { + cir.setReturnValue(prop); + cir.cancel(); + } } @Inject( @@ -54,9 +108,7 @@ public abstract class YggdrasilMinecraftSessionServiceMixin { cancellable = true, remap = false ) - private void getSecurePropertyValue(Property property, CallbackInfoReturnable cir) { - Tools.Log.info("[FII MIXIN] Intercepted YggdrasilMinecraftSessionService.getSecurePropertyValue!!!"); - + public void getSecurePropertyValue(Property property, CallbackInfoReturnable cir) { cir.setReturnValue(property.value()); cir.cancel(); } @@ -68,34 +120,21 @@ public abstract class YggdrasilMinecraftSessionServiceMixin { remap = false ) private void getPropertySignatureState(Property property, CallbackInfoReturnable cir) { - Tools.Log.info("[FII MIXIN] Intercepted YggdrasilMinecraftSessionService.getPropertySignatureState!!!"); - cir.setReturnValue(SignatureState.SIGNED); cir.cancel(); } @Inject( - method = "fetchProfileUncached(Ljava/util/UUID;Z)Lcom/mojang/authlib/yggdrasil/ProfileResult;", - at = @At("RETURN"), + method = "fetchProfile(Ljava/util/UUID;Z)Lcom/mojang/authlib/yggdrasil/ProfileResult;", + at = @At("HEAD"), cancellable = true, remap = false ) - private void fetchProfileUncached(UUID profileId, boolean requireSecure, CallbackInfoReturnable cir) { - Tools.Log.info("[FII MIXIN] Intercepted YggdrasilMinecraftSessionService.fetchProfileUncached!!!"); - Tools.Log.info("[FII MIXIN] {}", cir.getReturnValue().profile()); - } - - @ModifyArg( - method = "fetchProfile", - at = @At( - value = "INVOKE", - target = "Lcom/mojang/authlib/yggdrasil/YggdrasilMinecraftSessionService;fetchProfileUncached(Ljava/util/UUID;Z)Lcom/mojang/authlib/yggdrasil/ProfileResult;" - ), - index = 1, - remap = false - ) - private boolean modifyFetchProfileUncachedArg(boolean requireSecure) { - Tools.Log.info("[FII MIXIN] Intercepted requireSecure argument for YggdrasilMinecraftSessionService.fetchProfileUncached in fetchProfile!!!"); - return false; + public void fetchProfile(UUID profileId, boolean requireSecure, CallbackInfoReturnable cir) { + ProfileResult result = getProfileFromMince(profileId, !requireSecure); + if(result != null) { + cir.setReturnValue(result); + cir.cancel(); + } } } diff --git a/src/main/resources/flashii-extensions.mixins.json b/src/main/resources/flashii-extensions.mixins.json index e904762..90b7abd 100644 --- a/src/main/resources/flashii-extensions.mixins.json +++ b/src/main/resources/flashii-extensions.mixins.json @@ -3,7 +3,7 @@ "package": "net.flashii.mcexts.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ - "PlayerSkinProviderMixin", + "PlayerListEntryMixin", "PlayerListHudMixin", "TextureUrlCheckerMixin", "YggdrasilEnvironmentMixin",