Skip to content

Commit 103b695

Browse files
committed
Simplified the fake player
1 parent 3c6a8a9 commit 103b695

File tree

3 files changed

+51
-85
lines changed

3 files changed

+51
-85
lines changed

common/src/main/kotlin/com/lambda/config/serializer/GameProfileSerializer.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,15 @@ object GameProfileSerializer : JsonSerializer<GameProfile>, JsonDeserializer<Gam
4444
typeOfT: Type?,
4545
context: JsonDeserializationContext?,
4646
): GameProfile {
47-
val id = json?.asJsonObject?.get("id")?.asString
47+
val name = json?.asJsonObject?.get("name")?.asString ?: "nil"
48+
val id = json?.asJsonObject?.get("id")?.asString ?: "00000000-0000-0000-0000-000000000000"
4849
val parsedId =
49-
if (id?.length == 32) id.replaceFirst(
50+
if (id.length == 32) id.replaceFirst(
5051
"(\\w{8})(\\w{4})(\\w{4})(\\w{4})(\\w{12})".toRegex(),
5152
"$1-$2-$3-$4-$5"
5253
)
5354
else id
5455

55-
return GameProfile(
56-
UUID.fromString(parsedId),
57-
json?.asJsonObject?.get("name")?.asString
58-
)
56+
return GameProfile(UUID.fromString(parsedId), name)
5957
}
6058
}

common/src/main/kotlin/com/lambda/module/modules/combat/FakePlayer.kt

Lines changed: 40 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -17,113 +17,78 @@
1717

1818
package com.lambda.module.modules.combat
1919

20-
import com.github.kittinunf.fuel.Fuel
21-
import com.github.kittinunf.fuel.gson.responseObject
20+
import com.lambda.context.SafeContext
2221
import com.lambda.event.events.ConnectionEvent
2322
import com.lambda.event.events.TickEvent
2423
import com.lambda.event.listener.SafeListener.Companion.listen
24+
import com.lambda.event.listener.SafeListener.Companion.listenConcurrently
2525
import com.lambda.module.Module
2626
import com.lambda.module.tag.ModuleTag
27+
import com.lambda.network.LambdaHttp
2728
import com.lambda.threading.onShutdown
28-
import com.lambda.threading.runGameScheduled
29-
import com.lambda.threading.runSafe
29+
import com.lambda.util.Timer
3030
import com.lambda.util.player.spawnFakePlayer
3131
import com.mojang.authlib.GameProfile
32+
import io.ktor.client.call.*
33+
import io.ktor.client.request.*
3234
import net.minecraft.client.network.OtherClientPlayerEntity
3335
import net.minecraft.client.network.PlayerListEntry
3436
import net.minecraft.entity.Entity
3537
import java.util.*
36-
import kotlin.concurrent.fixedRateTimer
38+
import kotlin.time.Duration.Companion.seconds
3739

3840
object FakePlayer : Module(
3941
name = "FakePlayer",
4042
description = "Spawns a fake player",
41-
defaultTags = setOf(ModuleTag.COMBAT, ModuleTag.RENDER)
43+
defaultTags = setOf(ModuleTag.COMBAT),
4244
) {
4345
private val playerName by setting("Name", "Steve")
44-
private val fetchKey get() = playerName.lowercase() // Nicknames aren't case-sensitive
4546

46-
private var fakePlayer: OtherClientPlayerEntity? = null; set(value) {
47-
runSafe {
48-
field?.let {
49-
world.removeEntity(it.id, Entity.RemovalReason.DISCARDED)
50-
}
51-
value?.let {
52-
world.addEntity(it)
53-
}
54-
}
47+
private var fakePlayer_field: OtherClientPlayerEntity? = null
48+
private var SafeContext.fakePlayer
49+
get() = fakePlayer_field
50+
set(value) { fakePlayer_field = value; value?.let { world.addEntity(it) } }
5551

56-
field = value
57-
}
52+
private val nilProfile: GameProfile
53+
get() = GameProfile(UUID(0, 0), playerName)
5854

59-
private val nilUuid = UUID(0, 0)
60-
private val cachedProfiles = hashMapOf<String, GameProfile>()
55+
private val cachedProfiles = mutableMapOf<String, GameProfile>()
56+
private val fetchTimer = Timer()
6157

6258
init {
6359
listen<TickEvent.Pre> {
64-
fakePlayer = cachedProfiles[fetchKey]?.let { cached ->
65-
// Keep fetched fake player
66-
fakePlayer?.gameProfile?.also { profile ->
67-
if (profile is FetchedGameProfile && profile.name == cached.name) return@let fakePlayer
68-
}
69-
70-
// Spawn fetched fake player
71-
spawnFakePlayer(
72-
profile = cached,
73-
reference = fakePlayer ?: player,
74-
addToWorld = false
75-
)
76-
} ?: fakePlayer?.takeIf { it.gameProfile.name == playerName } ?: spawnFakePlayer(
77-
// Spawn offline fake player while fetching
78-
profile = GameProfile(nilUuid, playerName),
79-
reference = fakePlayer ?: player,
80-
addToWorld = false
81-
)
60+
fakePlayer = cachedProfiles[playerName]
61+
?.let { spawnFakePlayer(it, fakePlayer ?: player, addToWorld = false) }
62+
?.takeUnless { it == fakePlayer?.gameProfile }
63+
?: fakePlayer?.takeIf { playerName == it.gameProfile.name }
64+
?: spawnFakePlayer(nilProfile, fakePlayer ?: player, addToWorld = false)
8265
}
8366

84-
fixedRateTimer(
85-
name = "FakePlayer profile fetcher",
86-
daemon = true,
87-
initialDelay = 0L,
88-
period = 2000L
89-
) {
90-
cachedProfiles[fetchKey] ?: runSafe {
91-
val (requestedProfile, _) =
92-
Fuel.get("https://api.mojang.com/users/profiles/minecraft/$fetchKey")
93-
.responseObject<GameProfile>().third
67+
listenConcurrently<TickEvent.Pre>(priority = 1000) {
68+
if (!fetchTimer.timePassed(2.seconds)) return@listenConcurrently
9469

95-
val uuid = requestedProfile?.id ?: nilUuid
96-
97-
val fetchedProperties = mc.sessionService.fetchProfile(uuid, true)?.profile?.properties
70+
cachedProfiles.getOrPut(playerName) { fetchProfile(playerName) }
71+
}
9872

99-
val profile = FetchedGameProfile(nilUuid, playerName).apply {
100-
fetchedProperties?.forEach { key, value -> properties.put(key, value) }
101-
}
73+
listen<ConnectionEvent.Connect.Pre> { disable() }
74+
onShutdown { disable() }
75+
onDisable { fakePlayer?.discard(); fakePlayer = null }
76+
}
10277

103-
runGameScheduled {
104-
// This is the cache that mc pulls profile data from when it fetches skins.
105-
mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false))
106-
cachedProfiles[fetchKey] = profile
107-
}
108-
}
109-
}
78+
suspend fun SafeContext.fetchProfile(key: String): GameProfile {
79+
val requestedProfile = LambdaHttp.get("https://api.mojang.com/users/profiles/minecraft/$key")
80+
.body<GameProfile>()
11081

111-
onDisable {
112-
fakePlayer = null
113-
}
82+
// Fetch the skin properties from mojang
83+
val properties = mc.sessionService.fetchProfile(requestedProfile.id, true)?.profile?.properties
11484

115-
onShutdown {
116-
disable()
117-
}
85+
// We use the nil profile to avoid the nil username if something wrong happens
86+
// Check the GameProfile deserializer you'll understand
87+
val profile = nilProfile
88+
properties?.let { profile.properties.putAll(it) }
11889

119-
listen<ConnectionEvent.Connect.Pre> {
120-
disable()
121-
}
90+
mc.networkHandler?.playerListEntries?.put(profile.id, PlayerListEntry(profile, false))
12291

123-
listen<ConnectionEvent.Disconnect> {
124-
disable()
125-
}
92+
return profile
12693
}
127-
128-
private class FetchedGameProfile(id: UUID, name: String) : GameProfile(id, name)
12994
}

common/src/main/kotlin/com/lambda/util/player/PlayerUtils.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ fun SafeContext.copyPlayer(entity: ClientPlayerEntity) =
1111
ClientPlayerEntity(mc, world, mc.networkHandler, null, null, entity.isSneaking, entity.isSprinting).apply {
1212
setPos(entity.x, entity.y, entity.z)
1313
setExperience(entity.experienceProgress, entity.totalExperience, entity.experienceLevel)
14+
health = entity.health
15+
absorptionAmount = entity.absorptionAmount
1416
pitch = entity.pitch
1517
yaw = entity.yaw
1618
headYaw = entity.headYaw
@@ -29,13 +31,14 @@ fun SafeContext.spawnFakePlayer(
2931
addToWorld: Boolean = true
3032
): OtherClientPlayerEntity {
3133
val entity = OtherClientPlayerEntity(world, profile).apply {
32-
copyFrom(reference)
34+
copyFrom(reference)
3335

34-
playerListEntry = PlayerListEntry(profile, false)
35-
id = -2024 - 4 - 20
36-
}
36+
playerListEntry = PlayerListEntry(profile, false)
37+
id = -2024 - 4 - 20
38+
}
3739

3840
if (addToWorld) world.addEntity(entity)
3941

4042
return entity
4143
}
44+

0 commit comments

Comments
 (0)