77use muqsit \invmenu \inventory \InvMenuInventory ;
88use muqsit \invmenu \session \network \PlayerNetwork ;
99use muqsit \invmenu \session \PlayerManager ;
10+ use muqsit \invmenu \session \PlayerWindowDispatcher ;
1011use pocketmine \event \inventory \InventoryCloseEvent ;
1112use pocketmine \event \inventory \InventoryTransactionEvent ;
1213use pocketmine \event \Listener ;
1314use pocketmine \event \server \DataPacketReceiveEvent ;
1415use pocketmine \inventory \transaction \action \SlotChangeAction ;
16+ use pocketmine \network \mcpe \protocol \ContainerClosePacket ;
1517use pocketmine \network \mcpe \protocol \NetworkStackLatencyPacket ;
18+ use pocketmine \network \mcpe \protocol \PacketViolationWarningPacket ;
1619
1720final class InvMenuEventHandler implements Listener{
1821
@@ -31,6 +34,38 @@ public function onDataPacketReceive(DataPacketReceiveEvent $event) : void{
3134 if ($ player !== null ){
3235 $ this ->player_manager ->getNullable ($ player )?->network->notify ($ packet ->timestamp );
3336 }
37+ }elseif ($ packet instanceof ContainerClosePacket){
38+ // these are not magic numbers. 255 (windowId) is supposed to be ContainerIds::NONE (-1) but it appears
39+ // either pocketmine or mojang wrongly encodes/decodes the packet. the same applies to 247 (windowType)
40+ // which actually is WindowTypes::NONE (-9).
41+ if (!$ packet ->server && $ packet ->windowId === 255 && $ packet ->windowType === 247 ){
42+ $ player = $ event ->getOrigin ()->getPlayer ();
43+ if ($ player !== null && $ this ->player_manager ->getNullable ($ player )?->dispatcher !== null ){
44+ $ event ->cancel ();
45+ }
46+ }
47+ }elseif ($ packet instanceof PacketViolationWarningPacket){
48+ // we (ab)use a packet violation as an ACK the inventory was successfully sent to the player. we expect to
49+ // receive the same number of violation packets as the number of excess ContainerOpenPackets that we sent.
50+ // digesting these excess violation packets is not necessary, but in this way we can intercept violations
51+ // from propagating further if existing plugins print these violations for debugging purposes.
52+ if ($ packet ->getPacketId () === PacketViolationWarningPacket::NETWORK_ID && $ packet ->getType () === -1 && $ packet ->getSeverity () === PacketViolationWarningPacket::SEVERITY_WARNING ){
53+ $ player = $ event ->getOrigin ()->getPlayer ();
54+ if ($ player !== null ){
55+ $ dispatcher = $ this ->player_manager ->getNullable ($ player )?->dispatcher;
56+ if ($ dispatcher !== null ){
57+ if ($ dispatcher ->state === PlayerWindowDispatcher::STATE_SENDING ){
58+ $ dispatcher ->setResult (true );
59+ $ event ->cancel ();
60+ }elseif ($ dispatcher ->state === PlayerWindowDispatcher::STATE_FINALIZING ){
61+ if (--$ dispatcher ->n_finalization_acks <= 0 ){
62+ $ dispatcher ->finalize ();
63+ }
64+ $ event ->cancel ();
65+ }
66+ }
67+ }
68+ }
3469 }
3570 }
3671
@@ -45,11 +80,13 @@ public function onInventoryClose(InventoryCloseEvent $event) : void{
4580 return ;
4681 }
4782
48- $ current = $ session ->getCurrent () ;
83+ $ current = $ session ->current ;
4984 if ($ current !== null && $ event ->getInventory () === $ current ->menu ->getInventory ()){
50- $ current ->menu ->onClose ($ player );
85+ $ current ?->graphic->remove ($ player );
86+ $ session ->current = null ;
5187 }
52- $ session ->network ->waitUntil (PlayerNetwork::DELAY_TYPE_ANIMATION_WAIT , 325 , static fn (bool $ success ) : bool => false );
88+ $ session ->network ->wait (PlayerNetwork::DELAY_TYPE_ANIMATION_WAIT , static fn ($ success ) => false );
89+ $ current ?->menu->onClose ($ player );
5390 }
5491
5592 /**
@@ -61,19 +98,27 @@ public function onInventoryTransaction(InventoryTransactionEvent $event) : void{
6198 $ player = $ transaction ->getSource ();
6299
63100 $ player_instance = $ this ->player_manager ->get ($ player );
64- $ current = $ player_instance ->getCurrent ();
65- $ inventory = $ current ?->menu->getInventory ();
66- $ network_stack_callbacks = [];
67- foreach ($ transaction ->getActions () as $ action ){
68- if (!($ action instanceof SlotChangeAction)){
69- continue ;
70- }
71101
72- if ($ action ->getInventory () !== $ inventory ){
73- if ($ action ->getInventory () instanceof InvMenuInventory){
102+ // cancel transaction if menu is still being sent
103+ if ($ player_instance ->dispatcher !== null && $ player_instance ->dispatcher ->state !== PlayerWindowDispatcher::STATE_FINALIZING ){
104+ $ inventory = $ player_instance ->dispatcher ->info ->menu ->getInventory ();
105+ foreach ($ transaction ->getActions () as $ action ){
106+ if ($ action instanceof SlotChangeAction && $ action ->getInventory () === $ inventory ){
74107 $ event ->cancel ();
75108 return ;
76109 }
110+ }
111+ }
112+
113+ $ current = $ player_instance ->current ;
114+ if ($ current === null ){
115+ return ;
116+ }
117+
118+ $ inventory = $ current ->menu ->getInventory ();
119+ $ network_stack_callbacks = [];
120+ foreach ($ transaction ->getActions () as $ action ){
121+ if (!($ action instanceof SlotChangeAction) || $ action ->getInventory () !== $ inventory ){
77122 continue ;
78123 }
79124
0 commit comments