交互流程
玩家右键交互的处理过程相当复杂,也容易混淆。本篇会梳理整个流程,并说明各处应返回什么结果及其原因。
右键时会发生什么?
在世界任意位置右键时,会根据视线目标和双手中的 ItemStack 执行一系列操作。流程会调用多个方法,返回下文介绍的结果类型。多数方法在返回明确成功或明确失败时,会中止后续流程。为方便叙述,下文把这种结果称为“确定结果”。
- 以鼠标右键和主手触发
InputEvent.InteractionKeyMappingTriggered。若事件被取消,流程结束。 - 检查若干条件,例如玩家不在旁观模式、主手物品堆所需的功能标志全部启用等。任一检查失败,流程结束。
- 根据视线目标执行不同逻辑:
- 若目标是可触及且未超出世界边界的实体:
- 触发
PlayerInteractEvent.EntityInteractSpecific。若被取消,流程结束。 - 在目标实体上调用
Entity#interactAt。若返回确定结果,流程结束。- 为自有实体添加行为时,重写此方法;为原版实体添加行为时,使用事件。
- 如果实体打开了界面(例如村民交易或运输矿车界面),流程结束。
- 触发
PlayerInteractEvent.EntityInteract。若被取消,流程结束。 - 在目标实体上调用
Entity#interact。若返回确定结果,流程结束。- 为自有实体添加行为时,重写此方法;为原版实体添加行为时,使用事件。
- 对
Mob,Entity#interact的重写处理拴绳、使用主手刷怪蛋生成幼体等操作,再将生物专用逻辑交给Mob#mobInteract。Entity#interact的结果规则也适用于此处。
- 若目标是
LivingEntity,则对主手物品堆调用Item#interactLivingEntity。若返回确定结果,流程结束。
- 触发
- 若目标是可触及且未超出世界边界的方块:
- 触发
PlayerInteractEvent.RightClickBlock。若被取消,流程结束。也可以在该事件中单独禁止使用方块或物品。 - 调用
IItemExtension#onItemUseFirst。若返回确定结果,流程结束。 - 若玩家未潜行,且事件没有禁止方块使用,则触发
UseItemOnBlockEvent。若被取消,使用取消结果;否则调用Block#useItemOn。若返回确定结果,流程结束。 - 若
ItemInteractionResult为PASS_TO_DEFAULT_BLOCK_INTERACTION,且当前为主手,则调用Block#useWithoutItem。若返回确定结果,流程结束。 - 若事件没有禁止物品使用,则调用
Item#useOn。若返回确定结果,流程结束。
- 触发
- 若目标是可触及且未超出世界边界的实体:
- 调用
Item#use。若返回确定结果,流程结束。 - 上述流程再执行一次,这次改用副手。
结果类型
共有三种结果类型:InteractionResult、ItemInteractionResult 和 InteractionResultHolder<T>。多数位置使用 InteractionResult;只有 Item#use 使用 InteractionResultHolder<ItemStack>;只有 BlockBehaviour#useItemOn 和 CauldronInteraction#interact 使用 ItemInteractionResult。
InteractionResult 枚举包含五个值:SUCCESS、CONSUME、CONSUME_PARTIAL、PASS 和 FAIL。此外还有 InteractionResult#sidedSuccess。原文描述其在服务器返回 SUCCESS、客户端返回 CONSUME。
InteractionResultHolder<T> 包装 InteractionResult,并附加 T 类型的上下文。T 可以是任意类型,但几乎总是 ItemStack。它提供枚举值的包装方法(#success、#consume、#pass 和 #fail),也提供 #sidedSuccess;原文同样描述后者在服务器调用 #success,客户端调用 #consume。
ItemInteractionResult 类似于 InteractionResult,专门处理对方块使用物品的情况。它有六个枚举值:SUCCESS、CONSUME、CONSUME_PARTIAL、PASS_TO_DEFAULT_BLOCK_INTERACTION、SKIP_DEFAULT_BLOCK_INTERACTION 和 FAIL。通过 #result 可映射为 InteractionResult,其中两个带 DEFAULT_BLOCK_INTERACTION 的值均表示 InteractionResult#PASS。它也提供 #sidedSucess。
通常,各值的含义如下:
InteractionResult#sidedSuccess(需要时使用InteractionResultHolder#sidedSuccess或ItemInteractionResult#sidedSucess):操作视为成功,并希望手臂挥动。流程结束。InteractionResult#SUCCESS(或InteractionResultHolder#success、ItemInteractionResult#SUCCESS):操作视为成功,并希望手臂只在一端挥动。只有出于某种原因,需要在另一逻辑端返回不同结果时才使用。流程结束。InteractionResult#CONSUME(或InteractionResultHolder#consume、ItemInteractionResult#CONSUME):操作视为成功,但不希望手臂挥动。流程结束。InteractionResult#CONSUME_PARTIAL:与CONSUME基本相同,区别主要在Item#useOn中。ItemInteractionResult#CONSUME_PARTIAL在BlockBehaviour#useItemOn中也有类似用途。
InteractionResult.FAIL(或InteractionResultHolder#fail、ItemInteractionResult#FAIL):物品功能视为失败,不应继续其他交互。流程结束。任何位置都可使用,但在Item#useOn和Item#use之外应谨慎,许多时候InteractionResult.PASS更合适。InteractionResult.PASS(需要时使用InteractionResultHolder#pass):操作既不算成功,也不算失败,流程继续。除另有说明外,这是默认行为。ItemInteractionResult#PASS_TO_DEFAULT_BLOCK_INTERACTION允许主手调用BlockBehaviour#useWithoutItem;#SKIP_DEFAULT_BLOCK_INTERACTION则完全跳过该方法。除另有说明外,默认使用前者。
有些方法具有特殊行为或要求,下文会分别说明。
IItemExtension#onItemUseFirst
InteractionResult#sidedSuccess 和 InteractionResult.CONSUME 在这里不起作用。只应使用 InteractionResult.SUCCESS、InteractionResult.FAIL 或 InteractionResult.PASS。
Item#useOn
如果操作应视为成功,但不希望挥动手臂,也不希望增加 ITEM_USED 统计值,请使用 InteractionResult.CONSUME_PARTIAL。
Item#use
这是唯一返回 InteractionResultHolder<ItemStack> 的位置。如果结果中的物品堆发生了变化,它将替换发起使用操作的物品堆。
默认实现会在物品可食用且玩家能吃时(玩家饥饿,或物品始终可食用)返回 InteractionResultHolder#consume;物品可食用但玩家不能吃时返回 #fail;物品不可食用时返回 #pass。
处理主手时,如果在此返回 InteractionResultHolder#fail,将阻止副手行为。如果希望副手行为继续执行(通常如此),应返回 InteractionResultHolder#pass。
来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交
4a680cdd4d15。