如何修复Geyser的PlayerInteractEvent问题?求技术实现指导
背景说明
我曾在Geyser的GitHub仓库提交问题《Shooting Projectiles via Plugin is Glitchy #2913》,我的小游戏依赖PlayerInteractEvent,这个问题对我影响极大。之后有用户chrismwiggs提交了几个类似问题,其中第二个问题《Left click events not detected by other plugins when punching "air"... #2113》提到了一种可能的解决方案。当时我认为自行修复工作量过大,等待官方解决,后来发现该问题可能不会被修复,便取消了服务器的跨平台支持。现在我想恢复跨平台支持,打算fork Geyser自行修改,但我仅处于插件开发进阶新手阶段,特请教以下问题的解决方案:
问题解答
1. 是否需要使用ServerboundSwingPacket或其他组件以实现全Bedrock平台兼容
需要。Bedrock端空手左键点击空气时,不会像Java端那样触发PlayerInteractEvent,核心原因是Geyser默认没有将Bedrock的挥动手势转换为Java端的ServerboundSwingPacket。要让插件捕捉到这类交互事件,必须在Bedrock玩家做出挥动手势时,向Java服务器发送对应的挥包,同时还要处理投射物发射的同步逻辑,确保Bedrock端操作能正确触发Java端的PlayerInteractEvent。
2. 应在Geyser源码的何处添加相关代码
核心修改位置在Geyser的Bedrock交互事件处理模块:
- 找到处理Bedrock玩家挥动手势的类,通常是
BedrockPlayerSession或InteractionTranslator(具体路径可能因Geyser版本略有差异,比如org.geysermc.geyser.session.cache.InteractionCache或org.geysermc.geyser.translator.protocol.bedrock.PlayerActionTranslator) - 另外需要在向Java服务器发包的逻辑中添加挥包发送逻辑,相关代码位于
org.geysermc.geyser.network.session.GeyserSession的发包方法附近
3. 需要添加的具体代码内容
以下是基于Geyser常见版本的示例代码(需根据你fork的版本调整细节):
步骤1:捕获Bedrock挥动手势并发送对应数据包
在处理BedrockPlayerActionPacket的方法(比如PlayerActionTranslator的translate方法)中,添加对挥动手势动作的判断:
if (packet.getAction() == PlayerAction.START_SWING || packet.getAction() == PlayerAction.SWING_ARM) { // 向Java服务器发送挥包 ServerboundSwingPacket swingPacket = new ServerboundSwingPacket(Hand.MAIN_HAND); session.sendDownstreamPacket(swingPacket); // 额外处理投射物发射场景(对应你的#2913问题) if (session.getPlayerInventory().getItemInHand(Hand.MAIN_HAND).getType().isProjectileWeapon()) { // 发送使用物品包触发PlayerInteractEvent ServerboundUseItemPacket useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND); session.sendDownstreamPacket(useItemPacket); } }
步骤2:同步空手点击的交互状态
如果需要更精准触发PlayerInteractEvent,可在InteractionCache中添加空手点击的处理逻辑,同步Bedrock端交互状态到Java端:
// 在处理Bedrock交互更新的方法中 public void handleSwing(Hand hand) { // 检查是否为空手状态,发送交互包 if (session.getPlayer().isOnGround() && session.getPlayerInventory().getItemInHand(hand).isEmpty()) { ServerboundInteractPacket interactPacket = new ServerboundInteractPacket( session.getPlayerEntityId(), ServerboundInteractPacket.Action.ATTACK, session.getPlayer().getPosition() ); session.sendDownstreamPacket(interactPacket); } }
注意:以上代码仅为示例,实际需根据你fork的Geyser版本的代码结构调整,不同版本的数据包类名、枚举值可能存在差异,建议先通读Geyser的交互处理逻辑后再进行修改。
内容的提问来源于stack exchange,提问作者ThrownRedstone

