1717
1818package com.lambda.network
1919
20+ import com.lambda.Lambda.LOG
2021import com.lambda.Lambda.mc
2122import com.lambda.context.SafeContext
2223import com.lambda.core.Loadable
2324import com.lambda.event.events.WorldEvent
2425import com.lambda.event.listener.SafeListener.Companion.listen
2526import com.lambda.graphics.texture.TextureUtils
27+ import com.lambda.module.modules.client.Network.cdn
2628import com.lambda.network.api.v1.endpoints.getCape
29+ import com.lambda.network.api.v1.endpoints.getCapes
2730import com.lambda.network.api.v1.endpoints.setCape
2831import com.lambda.sound.SoundManager.toIdentifier
2932import com.lambda.threading.runIO
33+ import com.lambda.threading.runSafe
34+ import com.lambda.util.FileUtils.createIfNotExists
35+ import com.lambda.util.FileUtils.downloadCompare
3036import com.lambda.util.FileUtils.downloadIfNotPresent
37+ import com.lambda.util.FileUtils.ifNotExists
38+ import com.lambda.util.FileUtils.isOlderThan
3139import com.lambda.util.FolderRegister.capes
3240import com.lambda.util.extension.resolveFile
41+ import kotlinx.coroutines.runBlocking
3342import net.minecraft.client.texture.NativeImage.read
3443import net.minecraft.client.texture.NativeImageBackedTexture
44+ import java.util.LinkedList
3545import java.util.UUID
3646import java.util.concurrent.ConcurrentHashMap
47+ import kotlin.concurrent.fixedRateTimer
3748import kotlin.io.path.extension
3849import kotlin.io.path.inputStream
3950import kotlin.io.path.nameWithoutExtension
4051import kotlin.io.path.walk
52+ import kotlin.time.Duration.Companion.hours
53+ import kotlin.time.Duration.Companion.seconds
4154
4255@Suppress(" JavaIoSerializableObjectMustHaveReadResolve" )
4356object CapeManager : ConcurrentHashMap<UUID, String>(), Loadable {
44- /* *
45- * We want to cache images to reduce cloudflare requests and save money
46- */
57+ // We want to cache images to reduce class B requests
4758 private val images = capes.walk()
4859 .filter { it.extension == " png" }
4960 .associate { it.nameWithoutExtension to NativeImageBackedTexture (read(it.inputStream())) }
5061 .onEach { (key, value) -> mc.textureManager.registerTexture(key.toIdentifier(), value) }
5162
63+ private val fetchQueue = LinkedList <UUID >()
64+
65+ // We want to cache the cape list to reduce class B requests
66+ val capeList = runBlocking {
67+ capes.resolveFile(" capes.txt" )
68+ .isOlderThan(24 .hours) {
69+ it.downloadIfNotPresent(" $cdn /capes.txt" )
70+ .onFailure { err -> LOG .error(" Could not download the cape list: $err " ) }
71+ }
72+ .ifNotExists {
73+ it.downloadCompare(" $cdn /capes.txt" , - 1 )
74+ .onFailure { err -> LOG .error(" Could not download the cape list: $err " ) }
75+ }
76+ .createIfNotExists()
77+ .readText()
78+ .split(" \n " )
79+ }
80+
5281 /* *
5382 * Sets the current player's cape
5483 *
5584 * @param block Lambda called once the coroutine completes, it contains the throwable if any
5685 */
5786 fun updateCape (cape : String , block : (Throwable ? ) -> Unit = {}) = runIO {
5887 setCape(cape).getOrThrow()
88+
89+ runSafe { fetchCape(player.uuid) }
5990 }.invokeOnCompletion { block(it) }
6091
6192 /* *
@@ -75,11 +106,24 @@ object CapeManager : ConcurrentHashMap<UUID, String>(), Loadable {
75106 put(uuid, cape.id)
76107 }.invokeOnCompletion { block(it) }
77108
78- override fun load () = " Loaded ${images.size} cached capes"
109+ override fun load () = " Loaded ${images.size} cached capes and ${capeList.size} remote capes "
79110
80111 init {
81- listen<WorldEvent .Player .Join >(alwaysListen = true ) {
82- fetchCape(it.uuid)
112+ fixedRateTimer(
113+ daemon = true ,
114+ name = " Cape-fetcher" ,
115+ period = 15 .seconds.inWholeMilliseconds,
116+ ) {
117+ if (fetchQueue.isEmpty()) return @fixedRateTimer
118+
119+ runBlocking {
120+ getCapes(fetchQueue)
121+ .onSuccess { it.forEach { cape -> put(cape.uuid, cape.id) } }
122+
123+ fetchQueue.clear()
124+ }
83125 }
126+
127+ listen<WorldEvent .Player .Join >(alwaysListen = true ) { fetchQueue.push(it.uuid) }
84128 }
85129}
0 commit comments