Skip to content

Commit 8277df3

Browse files
Merge pull request #10 from SLNE-Development/feat/8-add-skin-changing-command
feat: add SkinChangeCommand with support for changing and resetting p…
2 parents 92ca31e + b563581 commit 8277df3

File tree

5 files changed

+223
-1
lines changed

5 files changed

+223
-1
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
kotlin.code.style=official
22
kotlin.stdlib.default.dependency=false
33
org.gradle.parallel=true
4-
version=1.21.10-2.0.0-SNAPSHOT
4+
version=1.21.10-2.0.2-SNAPSHOT

src/main/kotlin/dev/slne/surf/essentials/PaperCommandManager.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,6 @@ object PaperCommandManager {
6969
gameRuleCommand()
7070
executeCommandAdditions()
7171
timeCommand()
72+
skinChangeCommand()
7273
}
7374
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package dev.slne.surf.essentials.command
2+
3+
import com.destroystokyo.paper.profile.ProfileProperty
4+
import com.github.shynixn.mccoroutine.folia.entityDispatcher
5+
import com.github.shynixn.mccoroutine.folia.launch
6+
import dev.jorel.commandapi.kotlindsl.*
7+
import dev.slne.surf.essentials.plugin
8+
import dev.slne.surf.essentials.util.permission.EssentialsPermissionRegistry
9+
import dev.slne.surf.essentials.util.skin.SkinData
10+
import dev.slne.surf.essentials.util.skin.retrieveSkin
11+
import dev.slne.surf.surfapi.core.api.messages.adventure.sendText
12+
import kotlinx.coroutines.withContext
13+
import org.bukkit.entity.Player
14+
15+
fun skinChangeCommand() = commandTree("skin") {
16+
withPermission(EssentialsPermissionRegistry.SKIN_CHANGE_COMMAND)
17+
stringArgument("targetName") {
18+
playerExecutor { player, args ->
19+
val targetName: String by args
20+
21+
player.sendText {
22+
appendPrefix()
23+
info("Die Skin-Daten werden geladen...")
24+
}
25+
26+
plugin.launch {
27+
val skinData = retrieveSkin(targetName) ?: run {
28+
player.sendText {
29+
appendPrefix()
30+
error("Der Skin konnte nicht gefunden werden.")
31+
}
32+
return@launch
33+
}
34+
35+
assignSkin(player, skinData)
36+
37+
player.sendText {
38+
appendPrefix()
39+
success("Dein Skin wurde erfolgreich zu ")
40+
variableValue(targetName)
41+
success(" geändert.")
42+
}
43+
}
44+
}
45+
46+
entitySelectorArgumentOnePlayer("target") {
47+
withPermission(EssentialsPermissionRegistry.SKIN_CHANGE_COMMAND_OTHERS)
48+
anyExecutor { executor, args ->
49+
val targetName: String by args
50+
val target: Player by args
51+
52+
executor.sendText {
53+
appendPrefix()
54+
info("Die Skin-Daten von ")
55+
variableValue(targetName)
56+
info(" werden geladen...")
57+
}
58+
59+
plugin.launch {
60+
val skinData = retrieveSkin(targetName) ?: run {
61+
executor.sendText {
62+
appendPrefix()
63+
error("Der Skin von ")
64+
variableValue(targetName)
65+
error(" konnte nicht gefunden werden.")
66+
}
67+
return@launch
68+
}
69+
70+
assignSkin(target, skinData)
71+
72+
executor.sendText {
73+
appendPrefix()
74+
success("Der Skin von ")
75+
variableValue(targetName)
76+
success(" wurde geändert.")
77+
}
78+
79+
target.sendText {
80+
appendPrefix()
81+
info("Dein Skin wurde zu ")
82+
variableValue(targetName)
83+
info(" geändert.")
84+
}
85+
}
86+
}
87+
}
88+
}
89+
90+
literalArgument("reset") {
91+
playerExecutor { player, _ ->
92+
player.sendText {
93+
appendPrefix()
94+
info("Deine Skin-Daten werden zurückgesetzt...")
95+
}
96+
97+
plugin.launch {
98+
val skinData = retrieveSkin(player.name) ?: run {
99+
player.sendText {
100+
appendPrefix()
101+
error("Deine Skin-Daten konnten nicht zurückgesetzt werden.")
102+
}
103+
return@launch
104+
}
105+
106+
assignSkin(player, skinData)
107+
108+
player.sendText {
109+
appendPrefix()
110+
success("Deine Skin-Daten wurden erfolgreich zurückgesetzt.")
111+
}
112+
}
113+
}
114+
115+
entitySelectorArgumentOnePlayer("target") {
116+
withPermission(EssentialsPermissionRegistry.SKIN_CHANGE_COMMAND_OTHERS)
117+
anyExecutor { executor, args ->
118+
val target: Player by args
119+
120+
executor.sendText {
121+
appendPrefix()
122+
info("Die Skin-Daten von ")
123+
variableValue(target.name)
124+
info(" werden zurückgesetzt...")
125+
}
126+
127+
plugin.launch {
128+
val skinData = retrieveSkin(target.name) ?: run {
129+
executor.sendText {
130+
appendPrefix()
131+
error("Die Skin-Daten von ")
132+
variableValue(target.name)
133+
error(" konnten nicht zurückgesetzt werden.")
134+
}
135+
return@launch
136+
}
137+
138+
assignSkin(target, skinData)
139+
140+
executor.sendText {
141+
appendPrefix()
142+
success("Die Skin-Daten von ")
143+
variableValue(target.name)
144+
success(" wurden zurückgesetzt.")
145+
}
146+
147+
target.sendText {
148+
appendPrefix()
149+
info("Deine Skin-Daten wurden zurückgesetzt.")
150+
}
151+
}
152+
}
153+
}
154+
}
155+
}
156+
157+
private suspend fun assignSkin(player: Player, skin: SkinData) =
158+
withContext(plugin.entityDispatcher(player)) {
159+
val profile = player.playerProfile.apply {
160+
setProperty(
161+
ProfileProperty(
162+
"textures",
163+
skin.value,
164+
skin.signature
165+
)
166+
)
167+
}
168+
player.playerProfile = profile
169+
}

src/main/kotlin/dev/slne/surf/essentials/util/permission/EssentialsPermissionRegistry.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,6 @@ object EssentialsPermissionRegistry : PermissionRegistry() {
9696
val GAMERULE_COMMAND = create("$PREFIX.gamerule.command")
9797
val EXPERIENCE_COMMAND = create("$PREFIX.experience.command")
9898
val TIME_COMMAND = create("$PREFIX.time.command")
99+
val SKIN_CHANGE_COMMAND = create("$PREFIX.skinchange.command")
100+
val SKIN_CHANGE_COMMAND_OTHERS = create("$PREFIX.skinchange.command.others")
99101
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dev.slne.surf.essentials.util.skin
2+
3+
import com.github.benmanes.caffeine.cache.Caffeine
4+
import com.google.gson.JsonParser
5+
import com.sksamuel.aedile.core.asLoadingCache
6+
import com.sksamuel.aedile.core.expireAfterWrite
7+
import dev.slne.surf.surfapi.core.api.service.PlayerLookupService
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.withContext
10+
import okhttp3.OkHttpClient
11+
import okhttp3.Request
12+
import kotlin.time.Duration.Companion.hours
13+
14+
data class SkinData(
15+
val value: String,
16+
val signature: String
17+
)
18+
19+
private val client = OkHttpClient()
20+
21+
private val skinCache =
22+
Caffeine.newBuilder().expireAfterWrite(1.hours).asLoadingCache<String, SkinData?> {
23+
fetchSkin(it)
24+
}
25+
26+
suspend fun retrieveSkin(userName: String) = skinCache.get(userName)
27+
28+
private suspend fun fetchSkin(userName: String) = withContext(Dispatchers.IO) {
29+
val uuid = PlayerLookupService.getUuid(userName) ?: return@withContext null
30+
31+
return@withContext runCatching {
32+
client.newCall(
33+
Request.Builder()
34+
.url("https://sessionserver.mojang.com/session/minecraft/profile/$uuid?unsigned=false")
35+
.build()
36+
).execute().use {
37+
if (!it.isSuccessful) {
38+
return@withContext null
39+
}
40+
41+
val properties =
42+
JsonParser.parseString(it.body.string()).asJsonObject["properties"].asJsonArray[0].asJsonObject
43+
44+
SkinData(
45+
value = properties["value"].asString,
46+
signature = properties["signature"].asString
47+
)
48+
}
49+
}.getOrNull()
50+
}

0 commit comments

Comments
 (0)