Skip to content

Commit 80e17da

Browse files
committed
2 parents 47f078d + e1c8535 commit 80e17da

File tree

8 files changed

+136
-19
lines changed

8 files changed

+136
-19
lines changed

src/main/kotlin/org/polyfrost/polyplus/client/PolyPlusClient.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import kotlinx.coroutines.SupervisorJob
1414
import kotlinx.coroutines.launch
1515
import kotlinx.serialization.json.Json
1616
import org.apache.logging.log4j.LogManager
17+
import org.polyfrost.oneconfig.api.event.v1.EventManager
1718
import org.polyfrost.oneconfig.utils.v1.dsl.addDefaultCommand
1819
import org.polyfrost.polyplus.PolyPlusConstants
20+
import org.polyfrost.polyplus.client.cosmetics.ApplyCosmetics
1921
import org.polyfrost.polyplus.client.cosmetics.CosmeticManager
2022
import org.polyfrost.polyplus.client.discord.DiscordPresence
2123
import org.polyfrost.polyplus.client.network.http.PolyAuthorization
@@ -47,6 +49,13 @@ object PolyPlusClient {
4749

4850
fun initialize() {
4951
PolyPlusConfig.preload()
52+
53+
listOf(
54+
ApplyCosmetics
55+
).forEach {
56+
EventManager.INSTANCE.register(it)
57+
}
58+
5059
DiscordPresence.initialize()
5160
PolyConnection.initialize {
5261
LOGGER.info("Connected to PolyPlus WebSocket server.")
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.polyfrost.polyplus.client.cosmetics
2+
3+
import dev.deftu.omnicore.api.client.player
4+
import net.minecraft.network.play.server.S38PacketPlayerListItem
5+
import net.minecraftforge.event.world.WorldEvent
6+
import org.apache.logging.log4j.LogManager
7+
import org.polyfrost.oneconfig.api.event.v1.events.PacketEvent
8+
import org.polyfrost.oneconfig.api.event.v1.invoke.impl.Subscribe
9+
import org.polyfrost.polyplus.client.network.http.PolyCosmetics
10+
import org.polyfrost.polyplus.client.network.websocket.ClientboundPacket
11+
import org.polyfrost.polyplus.client.network.websocket.PolyConnection
12+
import org.polyfrost.polyplus.client.network.websocket.ServerboundPacket
13+
import org.polyfrost.polyplus.events.WebSocketMessage
14+
import org.polyfrost.polyplus.utils.Batcher
15+
import java.time.Duration
16+
import java.util.UUID
17+
import kotlin.collections.component1
18+
import kotlin.collections.component2
19+
import kotlin.collections.iterator
20+
21+
object ApplyCosmetics {
22+
private val LOGGER = LogManager.getLogger()
23+
private val BATCHER = Batcher(Duration.ofMillis(200), HashSet<String>()) { players ->
24+
PolyConnection.sendPacket(ServerboundPacket.GetActiveCosmetics(players.toList()))
25+
}
26+
27+
@Subscribe
28+
fun onWorldLoad(event: WorldEvent.Load) {
29+
PolyCosmetics.reset()
30+
}
31+
32+
@Subscribe
33+
fun onPlayerList(event: PacketEvent.Receive) {
34+
// Not sure how to cleanly make this version agnostic atm.
35+
val packet = try { event.getPacket<S38PacketPlayerListItem>() ?: return } catch (e: Exception) { return }
36+
when (packet.action) {
37+
S38PacketPlayerListItem.Action.ADD_PLAYER -> {
38+
packet.entries.forEach {
39+
// mojang doesnt use uuidv2 so if it is, its a bot and wont have a cape.
40+
if (it.profile.id.version() != 2) BATCHER.add(it.profile.id.toString())
41+
}
42+
}
43+
44+
S38PacketPlayerListItem.Action.REMOVE_PLAYER -> {
45+
for (entry in packet.entries) {
46+
PolyCosmetics.removeFromCache(entry.profile.id)
47+
}
48+
}
49+
50+
else -> return
51+
}
52+
}
53+
54+
@Subscribe
55+
fun onRecieveCosmetics(event: WebSocketMessage) {
56+
val cosmeticInfo = event.packet as? ClientboundPacket.CosmeticsInfo ?: return
57+
// todo: have a map of type to valid ids? or ask ty to include type in the returned info. for now theyre all capes.
58+
for ((uuid, active) in cosmeticInfo.all) {
59+
active.forEach {
60+
PolyCosmetics.cacheActive(UUID.fromString(uuid), "cape", it)
61+
LOGGER.info("Cached cosmetic for player $uuid: cape -> $it")
62+
}
63+
}
64+
}
65+
}

src/main/kotlin/org/polyfrost/polyplus/client/network/http/PolyCosmetics.kt

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,22 @@ import kotlinx.coroutines.async
88
import org.apache.logging.log4j.LogManager
99
import org.polyfrost.polyplus.client.PolyPlusClient
1010
import org.polyfrost.polyplus.client.PolyPlusConfig
11+
import org.polyfrost.polyplus.client.network.http.responses.Cosmetic
1112
import org.polyfrost.polyplus.client.network.http.responses.CosmeticList
1213
import org.polyfrost.polyplus.client.network.http.responses.PlayerCosmetics
1314
import java.util.UUID
1415

1516
object PolyCosmetics {
1617
private val LOGGER = LogManager.getLogger()
18+
private var OWNED: List<Cosmetic> = emptyList()
1719
private val CACHE = HashMap<UUID, HashMap<String, Int>>()
1820

1921
suspend fun updateOwned() {
20-
val cosmetics = PolyPlusClient.HTTP
22+
val playerCosmetics = PolyPlusClient.HTTP
2123
.getBodyAuthorized<PlayerCosmetics>("${PolyPlusConfig.apiUrl}/cosmetics/player")
2224
.onFailure { LOGGER.error("Failed to fetch owned cosmetics", it) }
2325
.getOrElse { return LOGGER.warn("Could not fetch owned cosmetics for player $playerUuid") }
24-
.owned
25-
for (cosmetic in cosmetics) {
26-
CACHE[playerUuid] = CACHE.getOrPut(playerUuid) {
27-
HashMap()
28-
}.apply {
29-
set(cosmetic.type, cosmetic.id)
30-
}
31-
}
26+
OWNED = playerCosmetics.owned
3227
}
3328

3429
fun getAll(): Deferred<Result<CosmeticList>> = PolyPlusClient.SCOPE.async {
@@ -42,10 +37,19 @@ object PolyCosmetics {
4237
}
4338

4439
fun reset() {
40+
LOGGER.info("Resetting cosmetics cache: Size before reset: ${CACHE.size}")
4541
CACHE.clear()
4642
}
4743

4844
fun getFor(uuid: UUID): HashMap<String, Int>? {
4945
return CACHE[uuid]
5046
}
47+
48+
fun cacheActive(uuid: UUID, type: String, id: Int) {
49+
CACHE.getOrPut(uuid) { HashMap() }[type] = id
50+
}
51+
52+
fun removeFromCache(uuid: UUID) {
53+
CACHE.remove(uuid)
54+
}
5155
}

src/main/kotlin/org/polyfrost/polyplus/client/network/http/responses/ActiveCosmetics.kt

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/main/kotlin/org/polyfrost/polyplus/client/network/http/responses/PlayerCosmetics.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ import kotlinx.serialization.SerialName
44
import kotlinx.serialization.Serializable
55

66
@Serializable
7-
data class PlayerCosmetics(val active: ActiveCosmetics, @SerialName("cosmetics") val owned: List<Cosmetic>)
7+
data class PlayerCosmetics(val active: HashMap<String, Int?>, @SerialName("cosmetics") val owned: List<Cosmetic>)

src/main/kotlin/org/polyfrost/polyplus/client/network/websocket/PolyConnection.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@ import kotlinx.coroutines.channels.Channel
1010
import kotlinx.coroutines.launch
1111
import kotlinx.serialization.serializer
1212
import org.apache.logging.log4j.LogManager
13+
import org.polyfrost.oneconfig.api.event.v1.EventManager
1314
import org.polyfrost.polyplus.client.PolyPlusClient
1415
import org.polyfrost.polyplus.client.PolyPlusConfig
16+
import org.polyfrost.polyplus.client.cosmetics.CosmeticManager
17+
import org.polyfrost.polyplus.client.network.http.PolyCosmetics
18+
import org.polyfrost.polyplus.events.WebSocketMessage
19+
import java.util.UUID
1520

1621
object PolyConnection {
1722
private val LOGGER = LogManager.getLogger()
@@ -99,12 +104,15 @@ object PolyConnection {
99104
}
100105

101106
private fun process(scope: CoroutineScope, message: String) {
102-
println("Received message: $message")
103107
val packet = PolyPlusClient.JSON.decodeFromString<ClientboundPacket>(message)
104-
println("Decoded packet: $packet")
108+
if (packet is ClientboundPacket.Error) {
109+
LOGGER.error("Error packet received: ${packet.message}")
110+
}
111+
112+
EventManager.INSTANCE.post(WebSocketMessage(packet))
105113
}
106114

107115
private inline fun <reified T : ServerboundPacket> T.string(): String {
108-
return PolyPlusClient.JSON.encodeToString(serializer(), this)
116+
return PolyPlusClient.JSON.encodeToString(ServerboundPacket.serializer(), this)
109117
}
110118
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.polyfrost.polyplus.events
2+
3+
import org.polyfrost.oneconfig.api.event.v1.events.Event
4+
import org.polyfrost.polyplus.client.network.websocket.ClientboundPacket
5+
6+
class WebSocketMessage(val packet: ClientboundPacket) : Event
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.polyfrost.polyplus.utils
2+
3+
import kotlinx.atomicfu.locks.ReentrantLock
4+
import kotlinx.atomicfu.locks.withLock
5+
import kotlinx.coroutines.CoroutineScope
6+
import kotlinx.coroutines.Job
7+
import kotlinx.coroutines.launch
8+
import org.polyfrost.polyplus.client.PolyPlusClient
9+
import java.time.Duration
10+
11+
class Batcher<T, C: MutableCollection<T>>(val delay: Duration, val set: C, val onBatch: suspend CoroutineScope.(C) -> Unit) {
12+
private val lock = ReentrantLock()
13+
private var job: Job? = null
14+
15+
fun add(item: T) {
16+
if (job == null) {
17+
job = PolyPlusClient.SCOPE.launch {
18+
kotlinx.coroutines.delay(delay.toMillis())
19+
lock.withLock {
20+
onBatch(set)
21+
set.clear()
22+
}
23+
job = null
24+
}
25+
}
26+
27+
lock.withLock {
28+
set.add(item)
29+
}
30+
}
31+
}

0 commit comments

Comments
 (0)