古明地兔 发表于 2024-3-4 12:41:18

如何优雅地换皮肤给榜一大哥看?

欢迎大家来到兔兔子的直播间~
刷一个嘉年华可以换皮肤哦~

想必跟我一样的擦边主播在此时都会有一个问题:
如何优雅地换皮肤给榜一大哥看?
这个问题会有如下几个选项:
1,原地脱去皮肤,穿上新的皮肤,但是容易被超管封直播间
2,“暂时离开”直播间,在别处换好了皮肤之后再回来,但是可能会损失一些现成的流量
3,圈到圆子了就跑路,不换皮肤了。什么,退钱?退你妈!
但是这些选项显然都有一些代价,那么是否有又便捷又优雅的换皮肤方式呢?

当然有!直接原地把现在穿着的皮肤改为新皮肤就好了!

要给玩家更换皮肤,在服务端需要有以下步骤:
1,获取材质
2,给指定玩家更换材质
3,通过nms使更换生效

第一步和第二步都非常简单,通过bukkitApi即可实现,第三步使用nms包即可实现。但是由于众所周知的原因,nms包的调用非常麻烦,而且对跨版本有一定的局限性,即便可以通过nmsPorxy或是Class.forName()处理起来也并不简单,而且容易发生意料之外的问题。

大概在20年写的通过nms使更换生效的代码,应该没人会想自己处理原生的nms代码吧?



我们可以使用一个魔法道具——SkinsRestorerApi,让别人去帮忙处理屎山。
以下内容均针对于群组服。

override fun onEnable(){
    skinsRestorerApi = SkinsRestorerProvider.get();
    getProxy().getPluginManager().registerListener(this,this);
    getProxy().registerChannel("BungeeCord");
}

@EventHandler
fun onPluginMessageReceived(e: PluginMessageEvent){
   
}如此,相当于直播间的公告栏
那么如何通过公告栏告诉观众:刷一个嘉年华主播就换皮肤呢

fun skinProperty(value: String, signature: String): SkinProperty {
    return SkinProperty.of(value, signature)
}

fun skinChangeHandle(player: ProxiedPlayer, skin: SkinProperty) {
    proxy.scheduler.runAsync(
      this
    ) {
      skinsRestorerApi.getSkinApplier<ProxiedPlayer>(ProxiedPlayer::class.java).applySkin(player, skin)
    }
}

// 移除皮肤最好考虑一下是否直接换成mc的默认皮肤
fun removeSkin(player: ProxiedPlayer) {
    skinsRestorerApi.playerStorage.removeSkinIdOfPlayer(player.uniqueId)
    proxy.scheduler.runAsync(this) {
      // 如果有相应的正版皮肤则给正版皮肤
      try {
            val optionalSkin = skinsRestorerApi.playerStorage
                .getSkinForPlayer(player.uniqueId, player.displayName)
            if (optionalSkin.isPresent) {
                skinChangeHandle(player, optionalSkin.get())
            }
      } catch () {}
    }
}
然后在onPluginMessageReceived中接收榜一大哥的礼物,随后调用skinChangeHandle()即可

那么,榜一大哥要如何刷礼物呢?

fun setSkin(skin: ISkinsEntry){
    this.setTextures(skin.texture,skin.signature)
}

fun setTextures(textures: String, signature: String){
    this.rawPlayer?.let {
      Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, Runnable {
            val b = ByteArrayOutputStream()
            val o = DataOutputStream(b)
            try {
                o.writeUTF("skin")
                o.writeUTF(it.uniqueId)
                o.writeUTF(textures)
                o.writeUTF(signature)
                o.flush()
                // BungeeCord君 我要进来咯!
                it.sendPluginMessage(plugin, "BungeeCord", b.toByteArray())
            } catch () {}
      }, 20L)
    }
}
这里给20个tick的Later是为了预防在PlayerJoinEvent中使用,可能会导致意料之外的错误发生。

由于众所周知的原因,咱们擦边主播会有一部分人守不住底线,脱掉自己皮肤的情况

proxy.scheduler.runAsync(
    this
) {
    skinsRestorerApi.getSkinApplier<ProxiedPlayer>(ProxiedPlayer::class.java)
      .applySkin(player, SkinProperty.of("", ""))//将皮肤的材质和签名都设置为"",即会换成mc的默认皮肤
}

这是我前几天折腾一个即时、动态更换玩家皮肤的需求时的解决方案,由于利益相关,暂时不能抛出完整代码。
使用BC处理是因为SkinsRestorerApi目前要求jdk17(SkinsRestorerApi降版本的话太难用了,那我还不如nmsProxy呢),而这个需求要求的mc版本是1.16.5,需要使用jdk16,于是我选择曲线救国在BC使用SkinsRestorerApi处理玩家皮肤,而子服(paper)只负责告诉bc需要将哪位玩家的皮肤更换为什么材质。

虽然我觉得应该不会有其他人遇到这么奇葩的需求了,但是万一呢。其实主要是想水一帖
因此稍微整理了一下发出来,如果你也遇到了这么奇葩的需求,希望这篇帖子能帮到你。





BrianLing 发表于 2024-3-4 12:54:54

好耶,打call[贴吧_吐舌]

jxhdamjr 发表于 2024-3-4 12:55:03

差一点就点走了
页: [1]
查看完整版本: 如何优雅地换皮肤给榜一大哥看?