Luohuayu / CatServer

高性能和高兼容性的1.12.2/1.16.5/1.18.2版本Forge+Bukkit+Spigot服务端 (A high performance and high compatibility 1.12.2/1.16.5/1.18.2 version Forge+Bukkit+Spigot server)
https://catmc.org
GNU Lesser General Public License v3.0
1.94k stars 204 forks source link

[1.18.2] 跨世界传送 NPC 时 Citizens插件出现 ClassCastException #855

Closed i493052739 closed 2 months ago

i493052739 commented 5 months ago

运行环境 Minecraft版本(1.12.2/1.16.5/1.18.2): 1.18.2 构建版本(从/version指令获取): ecf29bc 相关模组/插件: Citizens v2.0.33-SNAPSHOT (build 3293) 截止2024年1月23日最新版 https://www.spigotmc.org/resources/citizens.13811/

描述这个BUG

复现方式: /npc create testNPC /npc sel /npc wander (开启游荡) 让 NPC 自己走入地狱门后,使用 /npc tph (传送npc回来),即报错:

[Citizens] Task #4845 for Citizens v2.0.33-SNAPSHOT (build 3293) generated an exception 

        java.lang.ClassCastException: 
        class net.minecraft.server.level.ServerPlayer
        cannot be cast to
        class net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC 
        (net.minecraft.server.level.ServerPlayer is in module minecraft@1.18.2 of loader 'TRANSFORMER' @2574a9e3; 
        net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC is in unnamed module of loader org.bukkit.plugin.java.PluginClassLoader @6eaafa73)

                at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC$PlayerNPC.getHandle(EntityHumanNPC.java:426) ~[?:?]
                at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC$PlayerNPC.getHandle(EntityHumanNPC.java:400) ~[?:?]
                at org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity.getUniqueId(CraftEntity.java:754) ~[forge-1.18.2-40.2.14-universal.jar%23137!/:?]
                at org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer.canSee(CraftPlayer.java:1266) ~[forge-1.18.2-40.2.14-universal.jar%23137!/:?]
                at net.minecraft.server.players.PlayerList.broadcastAll(PlayerList.java:704) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.level.ServerPlayerGameMode.m_9273_(ServerPlayerGameMode.java:80) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.level.ServerPlayer.m_9015_(ServerPlayer.java:1500) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.players.PlayerList.respawn(PlayerList.java:569) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer.teleport(CraftPlayer.java:808) ~[forge-1.18.2-40.2.14-universal.jar%23137!/:?]
                at net.citizensnpcs.api.npc.AbstractNPC.lambda$teleport$3(AbstractNPC.java:534) ~[?:?]
                at org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftTask.run(CraftTask.java:81) ~[forge-1.18.2-40.2.14-universal.jar%23137!/:?]
                at org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:416) ~[forge-1.18.2-40.2.14-universal.jar%23137!/:?]
                at net.minecraft.server.MinecraftServer.m_5703_(MinecraftServer.java:1198) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.dedicated.DedicatedServer.m_5703_(DedicatedServer.java:397) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.MinecraftServer.m_5705_(MinecraftServer.java:1144) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:984) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at net.minecraft.server.MinecraftServer.m_177918_(MinecraftServer.java:344) ~[server-1.18.2-20220404.173914-srg.jar%23132!/:?]
                at java.lang.Thread.run(Thread.java:833) [?:?]

并且出现此错误后,此后的所有 npc 相关指令都会报同样错误

image

i493052739 commented 5 months ago

PlayerList 在 respawn 方法里是直接: ServerPlayer serverplayer = new ServerPlayer(this.f11195, serverlevel1, p11237.m36316());

但最终调用进去到 Citizens EntityHumanNPC 的 getHandle 里面是这样的:(它有个强转)

    public EntityHumanNPC getHandle() {
        return (EntityHumanNPC) this.entity;
    }

而 org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer 的 getHandle 是这样: (在 CraftPlayer 的 teleport 且世界不同时被调用)

    @Override
    public ServerPlayer getHandle() {
        return (ServerPlayer) entity;
    }

EntityHumanNPC 继承 net.minecraft.server.level.EntityPlayer (spigot nms) image

ServerPlayer 是 (似乎是 forge 的 player 类) image

供参考,暂时没想到什么修复方案

i493052739 commented 5 months ago

突然想到,既然创建时不会报错,感觉可能可以参考创建时的逻辑

了解到 创建时的逻辑是调用 NMS 直接 setBukkitEntity:

HumanController 的 createEntity 返回的是 EntityHumanNPC.getBukkitEntity()

这样在实际 spawn 时就直接用 NMS addBukkitEntity to world 即可正常执行 spawn

故尝试将 CraftEntity 的 getUniqueId 方法从:

    @Override
    public UUID getUniqueId() {
        return getHandle().getUUID();
    }

改成如下:

    @Override
    public UUID getUniqueId() {
        Entity handle = getHandle();
        if (handle instanceof ServerPlayer) {
            return ((ServerPlayer) handle).getBukkitEntity().getUniqueId();
        } else {
            return handle.getUUID();
        }
    }

但实际 build 后登录会发现 getUniqueId 存在 StackOverFlow 问题: (getBukkitEntity 会调用 SimplePluginManager 去 remove WeakHashMap 从而调用 hashCode,而 hashCode 在 CraftPlayer 重写后用到了 getUniqueId)

        at org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity.getUniqueId(CraftEntity.java:756) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer.hashCode(CraftPlayer.java:1300) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at java.util.WeakHashMap.hash(WeakHashMap.java:303) ~[?:?]
        at java.util.WeakHashMap.remove(WeakHashMap.java:596) ~[?:?]
        at org.bukkit.plugin.SimplePluginManager.unsubscribeFromDefaultPerms(SimplePluginManager.java:823) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.permissions.PermissibleBase.clearPermissions(PermissibleBase.java:183) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.permissions.PermissibleBase.recalculatePermissions(PermissibleBase.java:159) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.permissions.PermissibleBase.<init>(PermissibleBase.java:28) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.craftbukkit.v1_18_R2.entity.CraftHumanEntity.<init>(CraftHumanEntity.java:69) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer.<init>(CraftPlayer.java:145) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity.getEntity(CraftEntity.java:107) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at net.minecraft.world.entity.Entity.getBukkitEntity(Entity.java:264) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at net.minecraft.world.entity.player.Player.getBukkitEntity(Player.java:2182) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at net.minecraft.server.level.ServerPlayer.getBukkitEntity(ServerPlayer.java:284) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity.getUniqueId(CraftEntity.java:756) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]

暂时还没想到更好的解决方案

i493052739 commented 5 months ago

发现 entity 在构造时即传入了 ServerPlayer 故改为

    @Override
    public UUID getUniqueId() {
        if (entity instanceof ServerPlayer) {
            return entity.getUUID();
        } else {
            return getHandle().getUUID();
        }
    }

可以修正该 issue 的问题,但没修正根本问题,因测试发现后续 其子类 CraftLivingEntity getMaxHealth() 也出现了 getHandle 的 cast 问题,

而后续在继承链修复全部的 getHandle 问题后,其他类又出现问题 org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftTask.run(CraftTask.java:81)

 java.lang.ClassCastException: class net.minecraft.server.level.ServerPlayer cannot be cast to class net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC (net.minecraft.server.level.ServerPlayer is in module minecraft@1.18.2 of loader 'TRANSFORMER' @3d7fb838; net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC is in unnamed module of loader org.bukkit.plugin.java.PluginClassLoader @1c95f8ec)
        at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC$PlayerNPC.getHandle(EntityHumanNPC.java:397) ~[?:?]
        at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC$PlayerNPC.getHandle(EntityHumanNPC.java:372) ~[?:?]
        at net.citizensnpcs.nms.v1_18_R2.util.NMSImpl.getHandle(NMSImpl.java:2051) ~[?:?]
        at net.citizensnpcs.nms.v1_18_R2.util.NMSImpl.isValid(NMSImpl.java:896) ~[?:?]
        at net.citizensnpcs.util.NMS.isValid(NMS.java:649) ~[?:?]
        at net.citizensnpcs.npc.CitizensNPC.isSpawned(CitizensNPC.java:168) ~[?:?]
        at net.citizensnpcs.npc.CitizensNPC.updateFlyableState(CitizensNPC.java:576) ~[?:?]
        at net.citizensnpcs.npc.CitizensNPC.isFlyable(CitizensNPC.java:162) ~[?:?]
        at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC.m_7023_(EntityHumanNPC.java:358) ~[?:?]
        at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC.moveWithFallDamage(EntityHumanNPC.java:284) ~[?:?]
        at net.citizensnpcs.nms.v1_18_R2.entity.EntityHumanNPC.m_9240_(EntityHumanNPC.java:129) ~[?:?]
        at net.citizensnpcs.util.NMS.lambda$playerTicker$2(NMS.java:708) ~[?:?]
        at net.citizensnpcs.util.PlayerUpdateTask$PlayerTick.run(PlayerUpdateTask.java:86) ~[?:?]
        at net.citizensnpcs.util.PlayerUpdateTask.run(PlayerUpdateTask.java:71) ~[?:?]
        at org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftTask.run(CraftTask.java:81) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:416) ~[forge-1.18.2-40.2.14-universal.jar%2359!/:?]
        at net.minecraft.server.MinecraftServer.m_5703_(MinecraftServer.java:1198) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at net.minecraft.server.dedicated.DedicatedServer.m_5703_(DedicatedServer.java:397) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at net.minecraft.server.MinecraftServer.m_5705_(MinecraftServer.java:1144) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at net.minecraft.server.MinecraftServer.m_130011_(MinecraftServer.java:984) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at net.minecraft.server.MinecraftServer.m_177918_(MinecraftServer.java:344) ~[server-1.18.2-20220404.173914-srg.jar%2354!/:?]
        at java.lang.Thread.run(Thread.java:833) [?:?]

感觉是死胡同

i493052739 commented 5 months ago

重看两个报错,发现一个相同点:都是从这里来的 task 触发的 cast 问题 org.bukkit.craftbukkit.v1_18_R2.scheduler.CraftTask.run(CraftTask.java:81)

其中 Citizens 的 net.citizensnpcs.api.npc.AbstractNPC 在切世界重生成实体时用的是如下方法:

  Bukkit.getScheduler().scheduleSyncDelayedTask(CitizensAPI.getPlugin(), () -> {
      entity.teleport(loc, cause);
  }, (long)(delay++));

触发了 craftBukkit 的 CraftTask,delay 过后,runnable 直接执行了 entity.teleport(loc, cause); 而其中的 entity 导致了无法强转

Kotori0629 commented 2 months ago

这个你用最新版再测试一遍 理论来说是没问题了 EnderDragonPart那个应该也是