Skip to content

Proposal to change async packet processing logic#2503

Merged
dmulloy2 merged 3 commits intodmulloy2:masterfrom
Imprex-Development:fix-async-packet-processing-chain
Aug 27, 2023
Merged

Proposal to change async packet processing logic#2503
dmulloy2 merged 3 commits intodmulloy2:masterfrom
Imprex-Development:fix-async-packet-processing-chain

Conversation

@Ingrim4
Copy link
Copy Markdown
Collaborator

@Ingrim4 Ingrim4 commented Aug 7, 2023

I got recently confronted with an incompatibility between my plugin Orebufscator and RealisticSeasons. Both plugins are using async packet processing and delay the packet events (increase the processing delay). The way multiple async packet listeners are currently handled is as follows:

  1. get all async listeners for the packet type
  2. create an iterator for said list and attach it to the event
  3. enqueue packet into first listener of iterator
  4. processes packet (sync only)
  5. enqueue packet into next listener of iterator
  6. process packet (sync only)
  7. goto step 5 if iterator isn't empty
  8. mark processing slot free and try send packet if processing delay hit zero

This means if the first listener increases the processing delay then the second listener will process the same packet in parallel. The only way listeners can acquire exclusive access to a packet is using the processingLock object of the packet's AsyncMarker. This way of handling multi-threaded access to the packet event isn't very convenient since it could lead to unnecessary locked threads (if a listener would hold the lock for the entire processing step) and potential deadlocks if multiple packets are needed to process a single packet. This is especially harmful to the performance of plugins that process the packet in their own scheduler system (Orebfuscator does that because we need to jump back and forth between processing and main thread in some instances) because this could clog up the entire scheduler (particularly if a plugin needs a long time to process a packet). Furthermore there is currently a way for badly written plugins to prematurely send a packet through multiple decreasings of the processing delay without given other listeners time to process the packet.

That's why I propose the simple changes in this PR to effectively change steps 4 and 6 to also process the packet async (with processing delay reduced to zero) before calling the next listener. This way only one listener at a time can process a packet meaning the packet would always be "stable". Another way of solving this problem would be a new method for a listeners to explicitly tell ProtocolLib to only process this packet "sequentially" in that only this listener can process it and other listeners can after it's done. I would also be open for other solutions and would gladly implement them.

@dmulloy2
Copy link
Copy Markdown
Owner

@derklaro thoughts on this?

Copy link
Copy Markdown
Contributor

@derklaro derklaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the description and by looking at the code this propasal lgtm. From the description I also got a feeling that using a more reactive approach here would be way better (kinda like netty does, the handler returns a future and when the future returns the next handler is called and so on - another approach would be via a context that knows the next handler to call). This would give plugins the ability to process the packets in their own scheduler system and take their time before passing to the next handler, and even cancelling would be easy: just don't pass it to the next handler 🤷

But this is something for later, for now this is fine 😄

@dmulloy2 dmulloy2 merged commit f0401ac into dmulloy2:master Aug 27, 2023
@Ingrim4 Ingrim4 deleted the fix-async-packet-processing-chain branch September 27, 2023 18:28
@Leymooo
Copy link
Copy Markdown

Leymooo commented Mar 24, 2025

This PR causes issues with Orebfuscator.

When there is lot of players online (150+) and they all start to load chunks (for example event spawned in world), chunk loading is VERY VERY slow. It can took ~20 seconds till new chunk will be sent to player. Also when teleporting to spawn where ALL chunks are already loaded it can took up to ~30 seconds for chunks to appear.

Reverting this PR resolves this issue. @Ingrim4 can you look into this.

@Ingrim4
Copy link
Copy Markdown
Collaborator Author

Ingrim4 commented Mar 24, 2025

This PR can't be reverted as it would brake any two plugins that use async listeners for the same packet type.

I can however look further into the issues you're experiencing. Can you please open a separate issue one Imprex-Development/orebfuscator with a ProtocolLib and Orebfuscator dump file attached? Please also run the /protocol timings command and attach the file, we might be able to determine a slow plugin using this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants