Skip to content

Commit 3707ef4

Browse files
committed
feat: cape support in player limb (bone tag 'cape_')
1 parent ddf5a9f commit 3707ef4

File tree

7 files changed

+107
-36
lines changed

7 files changed

+107
-36
lines changed

api/src/main/java/kr/toxicity/model/api/bone/BoneTags.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package kr.toxicity.model.api.bone;
88

9+
import kr.toxicity.model.api.BetterModel;
10+
import kr.toxicity.model.api.data.renderer.RenderSource;
911
import kr.toxicity.model.api.player.PlayerLimb;
1012
import kr.toxicity.model.api.util.TransformedItemStack;
1113
import org.bukkit.entity.ItemDisplay;
@@ -123,6 +125,25 @@ public enum BoneTags implements BoneTag {
123125
* Player left foreleg
124126
*/
125127
PLAYER_LEFT_FORELEG(PlayerLimb.LEFT_FORELEG.getItemMapper(), new String[] { "plfl" }),
128+
/**
129+
* Cape
130+
*/
131+
CAPE(new BoneItemMapper() {
132+
@Override
133+
public @NotNull TransformedItemStack apply(@NotNull RenderSource<?> renderSource, @NotNull TransformedItemStack transformedItemStack) {
134+
var manager = BetterModel.plugin().skinManager();
135+
if (manager.supported() && renderSource instanceof RenderSource.Profiled profiled) {
136+
var cape = manager.getOrRequest(profiled.profile()).cape();
137+
if (cape != null) return cape;
138+
}
139+
return TransformedItemStack.empty();
140+
}
141+
142+
@Override
143+
public @NotNull ItemDisplay.ItemDisplayTransform transform() {
144+
return ItemDisplay.ItemDisplayTransform.FIXED;
145+
}
146+
}, new String[] { "cape" })
126147
;
127148

128149
BoneTags(@NotNull String[] tags) {

api/src/main/java/kr/toxicity/model/api/skin/SkinData.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import kr.toxicity.model.api.util.TransformedItemStack;
1010
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
1112

1213
/**
1314
* Skin data of player.
@@ -73,4 +74,9 @@ public interface SkinData {
7374
* @return right foreleg
7475
*/
7576
@NotNull TransformedItemStack rightForeLeg();
77+
/**
78+
* Gets cape
79+
* @return cape
80+
*/
81+
@Nullable TransformedItemStack cape();
7682
}

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ tasks {
3535
downloadPlugins {
3636
hangar("ViaVersion", "5.5.1")
3737
hangar("ViaBackwards", "5.5.1")
38-
hangar("Skript", "2.12.2")
38+
hangar("Skript", "2.13.0")
3939
}
4040
}
4141
build {

core/src/main/kotlin/kr/toxicity/model/manager/CommandManagerImpl.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ object CommandManagerImpl : CommandManager, GlobalManager {
6565
withAliases("t")
6666
withArguments(
6767
modelKeys,
68-
StringArgument("animation").suggestNullable { BetterModel.modelOrNull(it.previousArgs["model"] as String)?.animations()?.keys }
68+
StringArgument("animation").suggestNullable {
69+
it.previousArgs.mapNullableString("model") { model -> BetterModel.modelOrNull(model) }?.animations()?.keys
70+
}
6971
)
7072
withOptionalArguments(
7173
playerArgs,
@@ -92,7 +94,9 @@ object CommandManagerImpl : CommandManager, GlobalManager {
9294
withAliases("p")
9395
withArguments(
9496
limbKeys,
95-
StringArgument("animation").suggestNullable { BetterModel.limbOrNull(it.previousArgs["limb"] as String)?.animations()?.keys }
97+
StringArgument("animation").suggestNullable {
98+
it.previousArgs.mapNullableString("limb") { model -> BetterModel.limbOrNull(model) }?.animations()?.keys
99+
}
96100
)
97101
withOptionalArguments(
98102
StringArgument("loop_type").suggest(AnimationIterator.Type.entries.map { it.name.lowercase() }),

core/src/main/kotlin/kr/toxicity/model/manager/SkinManagerImpl.kt

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ package kr.toxicity.model.manager
88

99
import com.github.benmanes.caffeine.cache.Caffeine
1010
import com.github.benmanes.caffeine.cache.RemovalCause
11+
import com.google.gson.Gson
1112
import com.google.gson.JsonParser
13+
import com.google.gson.annotations.SerializedName
1214
import kr.toxicity.library.dynamicuv.*
1315
import kr.toxicity.model.api.BetterModel
1416
import kr.toxicity.model.api.event.CreatePlayerSkinEvent
@@ -559,6 +561,25 @@ object SkinManagerImpl : SkinManager, GlobalManager {
559561
)
560562
)
561563
)
564+
private val CAPE = UVModel(
565+
{ uvNamespace },
566+
"cape"
567+
).addElement(
568+
UVElement(
569+
ElementVector(10f, 16f, 1f).div(DIV_FACTOR),
570+
ElementVector(0f, -8f, 0.5f).div(DIV_FACTOR),
571+
UVSpace(10, 16, 1),
572+
UVElement.ColorType.RGB,
573+
mapOf(
574+
UVFace.NORTH to UVPos(12, 1),
575+
UVFace.SOUTH to UVPos(1, 1),
576+
UVFace.EAST to UVPos(11, 1),
577+
UVFace.WEST to UVPos(0, 1),
578+
UVFace.UP to UVPos(1, 0),
579+
UVFace.DOWN to UVPos(11, 0)
580+
)
581+
)
582+
)
562583

563584
private fun UVModel.asItem(image: BufferedImage): TransformedItemStack {
564585
val data = write(image)
@@ -597,10 +618,13 @@ object SkinManagerImpl : SkinManager, GlobalManager {
597618
SLIM_LEFT_FOREARM.write()
598619
SLIM_RIGHT_ARM.write()
599620
SLIM_RIGHT_FOREARM.write()
621+
CAPE.write()
600622

601623
block(UVByteBuilder.emptyImage(uvNamespace, "one_pixel"))
602624
}
603625

626+
private val gson = Gson()
627+
604628
private val profileCache = Caffeine.newBuilder()
605629
.expireAfterAccess(5, TimeUnit.MINUTES)
606630
.removalListener<UUID, SkinDataImpl> { key, value, cause ->
@@ -612,7 +636,7 @@ object SkinManagerImpl : SkinManager, GlobalManager {
612636

613637
private val fallback by lazy {
614638
PLUGIN.getResource("fallback_skin.png")!!.use {
615-
SkinDataImpl(false, ImageIO.read(it))
639+
SkinDataImpl(false, ImageIO.read(it), null)
616640
}
617641
}
618642

@@ -646,6 +670,21 @@ object SkinManagerImpl : SkinManager, GlobalManager {
646670
}.getOrDefault(false)
647671
}
648672

673+
private data class Skin(
674+
val textures: SkinTextures
675+
)
676+
677+
private data class SkinTextures(
678+
@SerializedName("SKIN") val skin: SkinUrl,
679+
@SerializedName("CAPE") val cape: SkinUrl?
680+
)
681+
682+
private data class SkinUrl(
683+
val url: String
684+
) {
685+
fun toURI(): URI = URI.create(url)
686+
}
687+
649688
override fun getOrRequest(profile: SkinProfile): SkinData {
650689
return profileCache.get(profile.id) { id ->
651690
skinProvider.provide(profile).thenApply { provided ->
@@ -655,24 +694,22 @@ object SkinManagerImpl : SkinManager, GlobalManager {
655694
}
656695
}.thenCompose { selected ->
657696
httpClient {
658-
sendAsync(HttpRequest.newBuilder()
659-
.uri(
660-
URI.create(
661-
JsonParser.parseString(String(Base64.getDecoder().decode(selected.textures.first().value)))
662-
.asJsonObject
663-
.getAsJsonObject("textures")
664-
.getAsJsonObject("SKIN")
665-
.getAsJsonPrimitive("url")
666-
.asString
667-
))
668-
.GET()
669-
.build(),
670-
HttpResponse.BodyHandlers.ofInputStream()
671-
).thenAccept {
672-
it.body().use { stream ->
697+
gson.fromJson(
698+
String(Base64.getDecoder().decode(selected.textures.first().value)),
699+
Skin::class.java
700+
).textures.run {
701+
fun SkinUrl.toFuture() = sendAsync(HttpRequest.newBuilder()
702+
.uri(toURI())
703+
.GET()
704+
.build(), HttpResponse.BodyHandlers.ofInputStream())
705+
.thenComposeAsync { request ->
706+
CompletableFuture.supplyAsync { request.body().use { ImageIO.read(it) } }
707+
}
708+
skin.toFuture().thenCombine(cape?.toFuture() ?: CompletableFuture.completedFuture(null)) { skin, cape ->
673709
profileCache.put(id, SkinDataImpl(
674710
isSlim(selected),
675-
ImageIO.read(stream).convertLegacy(),
711+
skin.convertLegacy(),
712+
cape,
676713
selected
677714
))
678715
BetterModel.registryOrNull(id)?.trackers()?.forEach { tracker ->
@@ -730,22 +767,24 @@ object SkinManagerImpl : SkinManager, GlobalManager {
730767

731768
private class SkinDataImpl(
732769
private val isSlim: Boolean,
733-
private val image: BufferedImage,
770+
private val skinImage: BufferedImage,
771+
private val capeImage: BufferedImage?,
734772
val original: SkinProfile? = null
735773
) : SkinData {
736774

737-
private val head = HEAD.asItem(image)
738-
private val hip = HIP.asItem(image)
739-
private val waist = WAIST.asItem(image)
740-
private val chest = CHEST.asItem(image)
741-
private val leftArm = (if (isSlim) SLIM_LEFT_ARM else LEFT_ARM).asItem(image)
742-
private val leftForeArm = (if (isSlim) SLIM_LEFT_FOREARM else LEFT_FOREARM).asItem(image)
743-
private val rightArm = (if (isSlim) SLIM_RIGHT_ARM else RIGHT_ARM).asItem(image)
744-
private val rightForeArm = (if (isSlim) SLIM_RIGHT_FOREARM else RIGHT_FOREARM).asItem(image)
745-
private val leftLeg = LEFT_LEG.asItem(image)
746-
private val leftForeLeg = LEFT_FORELEG.asItem(image)
747-
private val rightLeg = RIGHT_LEG.asItem(image)
748-
private val rightForeLeg = RIGHT_FORELEG.asItem(image)
775+
private val head = HEAD.asItem(skinImage)
776+
private val hip = HIP.asItem(skinImage)
777+
private val waist = WAIST.asItem(skinImage)
778+
private val chest = CHEST.asItem(skinImage)
779+
private val leftArm = (if (isSlim) SLIM_LEFT_ARM else LEFT_ARM).asItem(skinImage)
780+
private val leftForeArm = (if (isSlim) SLIM_LEFT_FOREARM else LEFT_FOREARM).asItem(skinImage)
781+
private val rightArm = (if (isSlim) SLIM_RIGHT_ARM else RIGHT_ARM).asItem(skinImage)
782+
private val rightForeArm = (if (isSlim) SLIM_RIGHT_FOREARM else RIGHT_FOREARM).asItem(skinImage)
783+
private val leftLeg = LEFT_LEG.asItem(skinImage)
784+
private val leftForeLeg = LEFT_FORELEG.asItem(skinImage)
785+
private val rightLeg = RIGHT_LEG.asItem(skinImage)
786+
private val rightForeLeg = RIGHT_FORELEG.asItem(skinImage)
787+
private val cape = capeImage?.let { CAPE.asItem(it) }
749788

750789
override fun head(): TransformedItemStack = head
751790
override fun hip(): TransformedItemStack = hip
@@ -759,8 +798,9 @@ object SkinManagerImpl : SkinManager, GlobalManager {
759798
override fun leftForeLeg(): TransformedItemStack = leftForeLeg
760799
override fun rightLeg(): TransformedItemStack = rightLeg
761800
override fun rightForeLeg(): TransformedItemStack = rightForeLeg
801+
override fun cape(): TransformedItemStack? = cape
762802

763-
fun refresh() = SkinDataImpl(isSlim, image, original)
803+
fun refresh() = SkinDataImpl(isSlim, skinImage, capeImage, original)
764804
}
765805

766806
override fun reload(pipeline: ReloadPipeline, zipper: PackZipper) {

core/src/main/resources/steve.bbmodel

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

test-plugin/src/main/resources/knight.bbmodel

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)