Fixed BukkitCommandManager registerCommandImpl to prevent async issue… - #32
Fixed BukkitCommandManager registerCommandImpl to prevent async issue…#32lunar-lunatic wants to merge 1 commit into
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 5 |
| Duplication | 0 |
TIP This summary will be updated as you push new changes. Give us feedback
|
I appreciate the focus on user-friendliness, but I have a couple of concerns with this PR: First, I have made it very clear (or so I think) in PySpigot’s documentation that any interaction with the Bukkit API, and with PySpigot itself, should only occur on the main server thread (synchronously). While I certainly value user-friendliness, I also think it's important to encourage good programming practices, including an understanding of thread safety and multithreaded programming. I would much rather a user make this mistake and learn both why it failed and how to fix it, rather than have PySpigot silently correct it for them. Could you think of a situation where this might be useful outside of user error (I.E. trying to register a command on an asynchronous thread)? And second, adding this to the command manager would also imply adding similar handling to all of the other managers, since in theory, none of those are thread-safe either (aside from the task manager). That would be a substantial amount of additional work for a relatively minor issue. |
I understand your concerns, so let me explain clearly what is happening and why its out of the user's hands: COMMAND_SENDING_POOL.execute(() -> this.sendAsync(player, commandNodes));
...
public static final java.util.concurrent.ExecutorService COMMAND_SENDING_POOL = new java.util.concurrent.ThreadPoolExecutorIt even directly states "Paper start - Perf: Async command map building", so it is Paper running in async to the Bukkit.getScheduler().runTaskTimer() that causes the issue, the person developing PySpigot scripts (user) cant prevent this. The current behaviour follows these steps:
The PR would fix this by delaying registrations by one tick, ensuring Paper's background threads finish before the tree is mutated again, it would look like this:
In my opinion an easily corrupted brigadier command tree poses a noticeable issue to the users, and the fix essentially involves adding a delay to each execution, which should not be too hard to port over. Please do let me know if you have further concerns or if something I explained above requires further clarification. |
|
Ahh, I see the issue now! This has actually been a bug I've encountered a few times over the past couple of years, but I was never able to pinpoint the cause. Thank you! I like your fix, but it assumes that Paper's background thread takes <=1 tick to complete. Suppose Paper's asynchronous task to rebuild the command map takes more than one tick, then I think we would have the same issue: another command would be registered when the command map is still being rebuilt, since the delay wouldn't be long enough. Honestly, I don't know how long it takes to rebuild the command map, so maybe this isn't an issue. Back when I first wrote this code I was testing with Spigot only, which, as far as I know, rebuilds the command map synchronously, so it wasn't an issue at the beginning. Maybe Paper has another thread-safe way to register commands? I will need to look into it. |
|
I believe this pr is related to the issue: PaperMC/Paper#13626 and seems to be a long standing bug with paper if it is the same issue that I'm referring to. Also for some context brigadier command map building should be very fast. Whenever a player switches worlds the command map is rebuilt and then sent, due to this I worked on a patch awhile ago to solve a performance bottleneck we encountered the patch can be found here: Mojang/brigadier@242de3f |
Changed BukkitCommandManager.java to execute command registration on the main thread only, preventing async command tree issues when reloading, especially on PaperMC