From 8e8192d66f14551f43c755dbeb8ee30972744550 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:26:43 -0400 Subject: [PATCH 1/9] fix: parse trimmed uuids --- .../config/serializer/GameProfileSerializer.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt b/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt index 48691d3cc..a489d30c8 100644 --- a/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt +++ b/common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt @@ -18,7 +18,7 @@ object GameProfileSerializer : JsonSerializer, JsonDeserializer, JsonDeserializer Date: Thu, 22 Aug 2024 12:55:34 -0400 Subject: [PATCH 2/9] make api calls to retrieve texture info --- .../main/kotlin/com/lambda/http/Extensions.kt | 5 -- .../main/kotlin/com/lambda/http/Request.kt | 13 ++-- .../module/modules/combat/FakePlayer.kt | 72 ++++++++++++++++--- .../src/main/resources/lambda.accesswidener | 2 + 4 files changed, 75 insertions(+), 17 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/http/Extensions.kt b/common/src/main/kotlin/com/lambda/http/Extensions.kt index a347b5381..97712c5b9 100644 --- a/common/src/main/kotlin/com/lambda/http/Extensions.kt +++ b/common/src/main/kotlin/com/lambda/http/Extensions.kt @@ -27,8 +27,3 @@ fun tryOrDefault(default: T, block: () -> T): T = try { } catch (e: Exception) { default } - -/** - * Try-catch block wrapped with null - */ -fun tryOrNull(block: () -> T): T? = tryOrDefault(null, block) diff --git a/common/src/main/kotlin/com/lambda/http/Request.kt b/common/src/main/kotlin/com/lambda/http/Request.kt index f975fa0ff..dc5ea22ce 100644 --- a/common/src/main/kotlin/com/lambda/http/Request.kt +++ b/common/src/main/kotlin/com/lambda/http/Request.kt @@ -92,10 +92,9 @@ data class Request( connection.outputStream.use { it.write(parameters.toJson().toByteArray()) } - } else { - connection.connect() - } + } else connection.connect() }.onFailure { + println("Failed to execute HTTP request: $it") return Response( connection = connection, data = null, @@ -121,9 +120,15 @@ data class Request( ) } + var error: Throwable? = null + val data = runCatching { Lambda.gson.fromJson(connection.inputStream.bufferedReader().readText(), Success::class.java) } + .onFailure { error = it } + .getOrNull() + return Response( connection = connection, - data = tryOrNull { Lambda.gson.fromJson(connection.inputStream.bufferedReader().readText(), Success::class.java) }, + data = data, + error = error, ) } } diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 20b5269f0..f734e4909 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -1,9 +1,21 @@ package com.lambda.module.modules.combat +import com.google.gson.annotations.SerializedName +import com.lambda.context.SafeContext +import com.lambda.http.Method +import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag +import com.lambda.threading.runSafe +import com.lambda.threading.runSafeConcurrent +import com.lambda.threading.runSafeGameConcurrent import com.mojang.authlib.GameProfile +import com.mojang.authlib.minecraft.MinecraftProfileTexture +import com.mojang.authlib.properties.Property import net.minecraft.client.network.OtherClientPlayerEntity +import net.minecraft.client.network.PlayerListEntry +import net.minecraft.client.texture.PlayerSkinProvider +import net.minecraft.client.world.ClientWorld import net.minecraft.entity.Entity import java.util.* @@ -14,23 +26,67 @@ object FakePlayer : Module( ) { private val playerName by setting("Name", "Steve") - private val uuid = UUID.fromString("41C82C87-7AfB-4024-BA57-13D2C99CAE77") private var fakePlayer: OtherClientPlayerEntity? = null init { onEnable { - fakePlayer = OtherClientPlayerEntity(world, GameProfile(uuid, playerName)) - .apply { - copyFrom(player) + // Avoid multiple api requests + if (fakePlayer?.gameProfile?.name == playerName) + return@onEnable spawnPlayer(fakePlayer!!.gameProfile) - id = -2024 - 4 - 20 - } + runSafeConcurrent { + val profile = + request("https://api.mojang.com/users/profiles/minecraft/$playerName") { + method(Method.GET) + }.json() + .data ?: return@runSafeConcurrent spawnPlayer(GameProfile(UUID.randomUUID(), playerName)) - world.addEntity(fakePlayer) + val properties = + request("https://sessionserver.mojang.com/session/minecraft/profile/${profile.id}") { + method(Method.GET) + }.json() + .data ?: return@runSafeConcurrent spawnPlayer(profile) + + val textureProperty = properties.textureProperty ?: return@runSafeConcurrent spawnPlayer(profile) + val textures = mc.sessionService.unpackTextures(textureProperty) + + // Fetch and cache the skin textures + mc.skinProvider.fetchSkinTextures(profile.id, textures) + + // Hack the game profile to include the skin textures + profile.properties.put("textures", textureProperty) + + spawnPlayer(profile) + } } onDisable { - fakePlayer?.setRemoved(Entity.RemovalReason.DISCARDED) + deletePlayer() } } + + private fun SafeContext.spawnPlayer(profile: GameProfile) { + fakePlayer = OtherClientPlayerEntity(world, profile) + .apply { + copyFrom(player) + + playerListEntry = PlayerListEntry(profile, false) + id = -2024 - 4 - 20 + } + + world.addEntity(fakePlayer) + } + + private fun SafeContext.deletePlayer() { + fakePlayer?.setRemoved(Entity.RemovalReason.DISCARDED) + } + + private data class PlayerSkinProviderKey( + @SerializedName("id") val id: String, + @SerializedName("name") val name: String, + @SerializedName("properties") val properties: List + ) { + val textureProperty: Property? + get() = properties.firstOrNull { it.name == "textures" } + } } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index f4a2a1a28..831123244 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -23,6 +23,7 @@ accessible method net/minecraft/entity/passive/AbstractHorseEntity setHorseFlag accessible method net/minecraft/entity/passive/AbstractHorseEntity updateSaddle ()V accessible field net/minecraft/entity/LivingEntity lastAttackedTicks I accessible method net/minecraft/entity/Entity setFlag (IZ)V +accessible field net/minecraft/client/network/AbstractClientPlayerEntity playerListEntry Lnet/minecraft/client/network/PlayerListEntry; # Camera accessible method net/minecraft/client/render/Camera setPos (DDD)V @@ -62,3 +63,4 @@ accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket nonce accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; +accessible method net/minecraft/client/texture/PlayerSkinProvider fetchSkinTextures (Ljava/util/UUID;Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;)Ljava/util/concurrent/CompletableFuture; From 86a7969caefa2c1eec9c6c1566effe0c40643226 Mon Sep 17 00:00:00 2001 From: pothemagicdragon Date: Sat, 24 Aug 2024 11:03:12 -0600 Subject: [PATCH 3/9] Fixes fetching player skins for FakePlayer.kt --- .../module/modules/combat/FakePlayer.kt | 41 ++++--------------- .../src/main/resources/lambda.accesswidener | 3 +- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index f734e4909..4dc66dd18 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -1,21 +1,14 @@ package com.lambda.module.modules.combat -import com.google.gson.annotations.SerializedName import com.lambda.context.SafeContext import com.lambda.http.Method import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag -import com.lambda.threading.runSafe import com.lambda.threading.runSafeConcurrent -import com.lambda.threading.runSafeGameConcurrent import com.mojang.authlib.GameProfile -import com.mojang.authlib.minecraft.MinecraftProfileTexture -import com.mojang.authlib.properties.Property import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry -import net.minecraft.client.texture.PlayerSkinProvider -import net.minecraft.client.world.ClientWorld import net.minecraft.entity.Entity import java.util.* @@ -35,27 +28,18 @@ object FakePlayer : Module( return@onEnable spawnPlayer(fakePlayer!!.gameProfile) runSafeConcurrent { - val profile = + var profile = GameProfile(UUID.randomUUID(), playerName) + + profile = request("https://api.mojang.com/users/profiles/minecraft/$playerName") { method(Method.GET) }.json() - .data ?: return@runSafeConcurrent spawnPlayer(GameProfile(UUID.randomUUID(), playerName)) - - val properties = - request("https://sessionserver.mojang.com/session/minecraft/profile/${profile.id}") { - method(Method.GET) - }.json() - .data ?: return@runSafeConcurrent spawnPlayer(profile) - - val textureProperty = properties.textureProperty ?: return@runSafeConcurrent spawnPlayer(profile) - val textures = mc.sessionService.unpackTextures(textureProperty) + .data ?: profile - // Fetch and cache the skin textures - mc.skinProvider.fetchSkinTextures(profile.id, textures) - - // Hack the game profile to include the skin textures - profile.properties.put("textures", textureProperty) + profile = mc.sessionService.fetchProfile(profile.id, true)?.profile ?: profile + // This is the cache that mc pulls profile data from when it fetches skins. + mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) spawnPlayer(profile) } } @@ -77,16 +61,7 @@ object FakePlayer : Module( world.addEntity(fakePlayer) } - private fun SafeContext.deletePlayer() { + private fun deletePlayer() { fakePlayer?.setRemoved(Entity.RemovalReason.DISCARDED) } - - private data class PlayerSkinProviderKey( - @SerializedName("id") val id: String, - @SerializedName("name") val name: String, - @SerializedName("properties") val properties: List - ) { - val textureProperty: Property? - get() = properties.firstOrNull { it.name == "textures" } - } } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 831123244..6738ec9a4 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -48,6 +48,7 @@ accessible field net/minecraft/text/Style font Lnet/minecraft/util/Identifier; accessible method net/minecraft/text/Style (Lnet/minecraft/text/TextColor;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Ljava/lang/Boolean;Lnet/minecraft/text/ClickEvent;Lnet/minecraft/text/HoverEvent;Ljava/lang/String;Lnet/minecraft/util/Identifier;)V # Network +accessible field net/minecraft/client/network/ClientPlayNetworkHandler playerListEntries Ljava/util/Map; accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket entityId I accessible field net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket type Lnet/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler; accessible class net/minecraft/network/packet/c2s/play/PlayerInteractEntityC2SPacket$InteractTypeHandler @@ -63,4 +64,4 @@ accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket nonce accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; -accessible method net/minecraft/client/texture/PlayerSkinProvider fetchSkinTextures (Ljava/util/UUID;Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;)Ljava/util/concurrent/CompletableFuture; +accessible method net/minecraft/client/texture/PlayerSkinProvider fetchSkinTextures (Ljava/util/UUID;Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;)Ljava/util/concurrent/CompletableFuture; \ No newline at end of file From d3387df134bb9b84b92d4995664ecea95778a8fd Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:55:25 -0400 Subject: [PATCH 4/9] indent --- .../main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 4dc66dd18..fbbfd139f 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -36,7 +36,7 @@ object FakePlayer : Module( }.json() .data ?: profile - profile = mc.sessionService.fetchProfile(profile.id, true)?.profile ?: profile + profile = mc.sessionService.fetchProfile(profile.id, true)?.profile ?: profile // This is the cache that mc pulls profile data from when it fetches skins. mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) From ca4455c3f0ef1a3fe75e2c031056515f89243a0f Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:58:51 -0400 Subject: [PATCH 5/9] edge case with duplicate uuid --- .../kotlin/com/lambda/module/modules/combat/FakePlayer.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index fbbfd139f..71d792f11 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -6,6 +6,7 @@ import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafeConcurrent +import com.lambda.util.Communication.warn import com.mojang.authlib.GameProfile import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry @@ -38,6 +39,11 @@ object FakePlayer : Module( profile = mc.sessionService.fetchProfile(profile.id, true)?.profile ?: profile + if (mc.networkHandler?.playerListEntries?.get(profile.id) != null) { + warn("A player with the name $playerName is already in the world.") + return@runSafeConcurrent + } + // This is the cache that mc pulls profile data from when it fetches skins. mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) spawnPlayer(profile) From c4ef889f1ed7c1323fc03869435a192922d0ca35 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 24 Aug 2024 14:24:33 -0400 Subject: [PATCH 6/9] removed unusedaw --- common/src/main/resources/lambda.accesswidener | 1 - 1 file changed, 1 deletion(-) diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index 6738ec9a4..0e9417f35 100644 --- a/common/src/main/resources/lambda.accesswidener +++ b/common/src/main/resources/lambda.accesswidener @@ -64,4 +64,3 @@ accessible field net/minecraft/network/packet/c2s/login/LoginKeyC2SPacket nonce accessible field net/minecraft/world/explosion/Explosion behavior Lnet/minecraft/world/explosion/ExplosionBehavior; accessible field net/minecraft/structure/StructureTemplate blockInfoLists Ljava/util/List; accessible method net/minecraft/item/BlockItem getPlacementState (Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/block/BlockState; -accessible method net/minecraft/client/texture/PlayerSkinProvider fetchSkinTextures (Ljava/util/UUID;Lcom/mojang/authlib/minecraft/MinecraftProfileTextures;)Ljava/util/concurrent/CompletableFuture; \ No newline at end of file From d97ba53ef695bd24eed1153c4438b54a95cf33e4 Mon Sep 17 00:00:00 2001 From: Edouard127 <46357922+Edouard127@users.noreply.github.com> Date: Sat, 24 Aug 2024 14:32:13 -0400 Subject: [PATCH 7/9] nil uuid instead of random --- .../main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 71d792f11..50e9f2ca2 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -29,7 +29,7 @@ object FakePlayer : Module( return@onEnable spawnPlayer(fakePlayer!!.gameProfile) runSafeConcurrent { - var profile = GameProfile(UUID.randomUUID(), playerName) + var profile = GameProfile(UUID(0, 0), playerName) profile = request("https://api.mojang.com/users/profiles/minecraft/$playerName") { From b5547f1be6cb0e6d56c62fa780cba14a9d66b4bf Mon Sep 17 00:00:00 2001 From: pothemagicdragon Date: Sat, 24 Aug 2024 12:41:28 -0600 Subject: [PATCH 8/9] Allows fake player to match player that exists in the world --- .../com/lambda/module/modules/combat/FakePlayer.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 50e9f2ca2..9bdb6d5a3 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -6,7 +6,6 @@ import com.lambda.http.request import com.lambda.module.Module import com.lambda.module.tag.ModuleTag import com.lambda.threading.runSafeConcurrent -import com.lambda.util.Communication.warn import com.mojang.authlib.GameProfile import net.minecraft.client.network.OtherClientPlayerEntity import net.minecraft.client.network.PlayerListEntry @@ -39,14 +38,14 @@ object FakePlayer : Module( profile = mc.sessionService.fetchProfile(profile.id, true)?.profile ?: profile - if (mc.networkHandler?.playerListEntries?.get(profile.id) != null) { - warn("A player with the name $playerName is already in the world.") - return@runSafeConcurrent + val safeUUIDProfile = GameProfile(UUID(0, 0), profile.name) + profile.properties.entries().forEach { + safeUUIDProfile.properties.put(it.key, it.value) } // This is the cache that mc pulls profile data from when it fetches skins. - mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) - spawnPlayer(profile) + mc.networkHandler?.playerListEntries?.put(safeUUIDProfile.id, PlayerListEntry(safeUUIDProfile, false)) + spawnPlayer(safeUUIDProfile) } } From 56a086f12452b0ef2c0e703ea029f7f6044a3c36 Mon Sep 17 00:00:00 2001 From: pothemagicdragon Date: Sat, 24 Aug 2024 13:16:56 -0600 Subject: [PATCH 9/9] Same functionality but prettier --- .../lambda/module/modules/combat/FakePlayer.kt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt index 9bdb6d5a3..c3fdb7c48 100644 --- a/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt +++ b/common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt @@ -28,24 +28,20 @@ object FakePlayer : Module( return@onEnable spawnPlayer(fakePlayer!!.gameProfile) runSafeConcurrent { - var profile = GameProfile(UUID(0, 0), playerName) - - profile = + val uuid = request("https://api.mojang.com/users/profiles/minecraft/$playerName") { method(Method.GET) - }.json() - .data ?: profile + }.json().data?.id ?: UUID(0, 0) - profile = mc.sessionService.fetchProfile(profile.id, true)?.profile ?: profile + val fetchedProperties = mc.sessionService.fetchProfile(uuid, true)?.profile?.properties - val safeUUIDProfile = GameProfile(UUID(0, 0), profile.name) - profile.properties.entries().forEach { - safeUUIDProfile.properties.put(it.key, it.value) + val profile = GameProfile(UUID(0, 0), playerName).apply { + fetchedProperties?.forEach { key, value -> properties.put(key, value) } } // This is the cache that mc pulls profile data from when it fetches skins. - mc.networkHandler?.playerListEntries?.put(safeUUIDProfile.id, PlayerListEntry(safeUUIDProfile, false)) - spawnPlayer(safeUUIDProfile) + mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false)) + spawnPlayer(profile) } }