PlayerInteractEventの挙動
PlayerInteractEventについて
PlayerInteractEventは, EntityInteractEventと似たEventで, プレイヤーがブロックやアイテムを右クリック(左クリック)したときに割りこまれる.
このEventを使って, 特定のアイテムを持った状態で元々GUIを持つブロックを右クリックすると別のGUIが表示されるようにしたのだが, 素直に書いたコード
@ForgeSubscribe public void interrupt(PlayerInteractEvent event) { if (canInteract(event.entityPlayer, event.x event.y, event.z)) { event.entityPlayer.openGui(...); event.setCanceled(true); } }
条件が合えば(プレイヤーの現在持っているアイテムや, 右クリックしたブロックのIDなど), 別のGUIを開くが, GUIは開いてもインベントリ操作ができない. アイテムをつかんでも動かせなかった.
発生した問題
- 右クリック時のブロックIDが, 想定してるIDではない
- クライアント側しか実行されない
- GUIが開いてもすぐ閉じてしまう
IDのずれ
アイテムを持っていないときはちゃんとしたブロックIDに, アイテムを持っていると何故かクライアントは0, サーバーは7(bedrock). PlayerInteractEventには右クリックしたときのアクションがRIGHT_CLICK_AIRとRIGHT_CLICK_BLOCKの2つがある. アイテムを持っていないと後者のみ呼ばれ, そのブロックのIDがEventに渡されるのだが, アイテムを持っているときは両方呼ばれ, 前者のほうが優先される. それを回避するために以下のコードをEventの始めに挿入.
if (event.action == PlayerInteractEvent.Action.RIGHT_CLICK_AIR) return ;
これでIDが変わる問題は解決した.
クライアント側しか実行されない
Event自体はクライアント・サーバー両方で呼ばれるが, openGuiがクライアント側しか実行されない. 原因不明だったので, 素直にサーバーにパケットを送信して, サーバー側でopenGuiを実行したらうまくいった.
GUIがすぐ閉じる
なんてことはない. Container側のcanInteractWithの条件設定が不適切だった.
まとめ
ここまでやっておいて言うのもあれだが, バニラのブロックの挙動を変える以外は素直に追加BlockのonBlockActivateでopenGuiしたほうがいいかなって.
ただ面白いので, GUIを開くのではなく, 特定のツールで右クリックするとブロックを置換する, などのアイデアに期待するEvent.