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 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..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 @@ -1,9 +1,14 @@ package com.lambda.module.modules.combat +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.runSafeConcurrent import com.mojang.authlib.GameProfile import net.minecraft.client.network.OtherClientPlayerEntity +import net.minecraft.client.network.PlayerListEntry import net.minecraft.entity.Entity import java.util.* @@ -14,23 +19,50 @@ 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 uuid = + request("https://api.mojang.com/users/profiles/minecraft/$playerName") { + method(Method.GET) + }.json().data?.id ?: UUID(0, 0) + + val fetchedProperties = mc.sessionService.fetchProfile(uuid, true)?.profile?.properties + + val profile = GameProfile(UUID(0, 0), playerName).apply { + fetchedProperties?.forEach { key, value -> properties.put(key, value) } } - world.addEntity(fakePlayer) + // 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) + } } 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 deletePlayer() { + fakePlayer?.setRemoved(Entity.RemovalReason.DISCARDED) + } } diff --git a/common/src/main/resources/lambda.accesswidener b/common/src/main/resources/lambda.accesswidener index f4a2a1a28..0e9417f35 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 @@ -47,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