I wanted to eliminate the single NBT check from my Minecraft data pack, Detect AFK Players. It uses the player's head rotation, and currently, there isn't a command like /rotate query to get that information without using NBT.
NBT is the save format, and requires the game to convert all the player data to that format from the in-memory data structures, serialize it, parse the lookup, and finally return the data. That's pretty slow! Naturally, the community's motto is to stay away from NBT unless you absolutely have to. I tried a few different things to detect activity, including:
- Spawn a marker entity where the player is looking
- Ties rotation to position, which is not good
- Spawn an entity where the player is looking, "positioned at" a particular point
- Requires either force loading chunks or using known loaded chunks
- Use a predicate to detect input directly
- Runs into edge cases such as player-operated farms where you use the mouse but not any keyboard inputs, and being a boat/camel passenger
Overall, head rotation truly is the best at detecting activity.
After giving up, I had an idea: what about binary searching for the player's angle, since selectors can use y_rotation to select players? Well, it turns out this works super well! But after benchmarking a binary search, I found that while the execution time of each command was significantly faster than using /data get entity @s ..., the game was spending a lot of time in unspecified... whatever that is. My assumption was overhead of loading different functions. To test this, I added more parameters to the generation script, allowing it to do more than 2 checks per function file, and specifying the number of iterations of search that should be done. It doesn't seem to be entirely down to function overhead, but I don't know enough about the game's internals to know exactly what was doing that.
Ultimately, I ended up with something that's fairly optimized. With 4 searches and 3 iterations, that's 360/4^3 = 5.625 degrees of precision (good enough for me!) with a best case scenario of 3 checks, and a worst case scenario of 4x3 checks per player.
Now, the speed. This is, usually, slightly faster than NBT - on my system, that's about 0.05ms faster. But it can also be worse. Sometimes noticeably better. It's highly variable depending on exactly what angle the player is facing. My conclusion from this is that while yes, there is probably extra optimization that could be done, maybe finding an even better configuration than 4x3, it's not worth it for a tiny performance bump and a way more complicated build system.
Maybe someone else would like this, so I'm making it public. Use this if you want!
The functions all use the new return and return run function syntax, so minimum Minecraft 1.20.3 is required.
You can use it like so:
execute store result score @s rotation run function MY_NAMESPACE:head_rotation/1__-180_to_180