在MinecraftForge中,无法获取已移除的实体的Capability

作者:

TL;DR:

实体会在被移除的时候让所有的Capability失效,需要手动调用Entity#reviveCapsgetCapability,完成以后调用Entity#invalidateCaps恢复之前的状态。

此前因为一些Bug导致在某些情况下即使不reviveCaps也能获取到,但Bug被修复以后就不能用了……


一个Bug是Bug,多个Bug能Work,修一个就Break(

在1.13(Forge 25.0),Forge存在实体(Entity)死亡(death)不会导致Capability失效的问题1,Lex推送了一个提交来修复它2,让Capabilities在实体被移除(remove)且不保存数据的时候失效(调用invalidateCaps()方法)。

实体被移除不完全等于死亡(LivingEntity的死亡是被移除的原因之一),此外玩家跳进末地的返回传送门等情况,也会让实体被移除。因此Forge给实体类上Patch了一个remove(boolean keepData)方法,当keepData是false时才会失效Caps。

在Forge的Capabilities系统中,Capability由CapabilityProvider持有,通过CapabilityProvider#getCapability方法获取,该方法返回一个Optional。实体就是一个CapabilityProvider,它有一个bool类型的失效标记,如果为true,则获取Capability必定得到一个空的Optional。

玩家跳进返回传送门时,在代码里被视为通关游戏(wonGame)的重生(respawn),而非像是通过下界传送门或者去往末地那样的移动(travelDimension)。前者会导致玩家实体(ServerPlayer)重生,也就是跳进返回传送门的玩家和在复活点生成的玩家不是同一个对象,虽然没有死亡,但也被移除了,有持久化需求的Capability要在新旧玩家实体之间复制。此时keepData是true,所以Capability仍然有效,以至于相安无事。


在1.16.5(Forge 36.0),Forge引入了一个新的事件——LivingConversionEvent来代表一个实体变成另一个实体的情况3。这个事件有Pre和Post的变种,前者能够取消或者拿到新实体的EntityType,后者可以拿到新的实体对象。但在Post事件被抛出的时候,并不保证转换前的实体仍然有效。例如:村民转变成僵尸村民时无效——原实体先被移除再抛出事件;而村民转变为女巫的时候有效——抛出事件后再移除原实体。

此时问题就初现端倪了:LivingEvtity被移除之后,getCapability会拿到一个空的Optional,即使实体仍然持有这个Caps的数据。因此村民变成僵尸村民的时候,直接从实体里拿不到Capability。但是由于村民一定是被杀死后才会变成僵尸村民,听上去也符合直觉。


在1.17.1(Forge 37.1),由于mojang实体移除机制发生变更,Forge移除了带有keepData参数的Entity#remove方法,现在实体移除时,一律会让实体的Caps失效,如果模组作者想要拿已经移除之实体的Caps,要先调用Entity#reviveCaps(),获取到之后再用Entity#invalidateCaps()使其失效。

这本来会造成复制玩家Caps的用例发生错误——跳进返回传送门也会导致Caps失效,但由于版本迁移过程中Patch的行号发生偏移,导致调用invalidateCaps方法的那一行Patch被打到实体类里面的别的方法上了4,所以问题并未发生,也没人注意到这个问题。


直到1.19.1(Forge 41.1),有人修复了Patch里行号的偏移5。对跳进末地返回传送门的玩家实体来说,reviveCaps和invalidateCaps成为了必须的步骤,而不知道这件事的模组作者就要面对玩家从末地回来以后数据消失奇怪的bug了(x


  1. https://github.com/MinecraftForge/MinecraftForge/issues/5307 ↩︎
  2. https://github.com/MinecraftForge/MinecraftForge/commit/e6a73ef36c1bdfb0053c2c122f1c71d376d44651 ↩︎
  3. https://github.com/MinecraftForge/MinecraftForge/commit/b64f4780c5f951da2eb46aed78590cdbb3220700 ↩︎
  4. https://github.com/MinecraftForge/MinecraftForge/commit/9ec1e24dc510875263c0a5a5070bb61b3b4833c7 ↩︎
  5. https://github.com/MinecraftForge/MinecraftForge/commit/0b1b3c68fa527c30006e1bfbf69eecccf311ef34 ↩︎

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注


The reCAPTCHA verification period has expired. Please reload the page.