## ๐ Bug Fixes
- **Fixed Folia crash: `AsyncCatcher` error on `Chunk getEntities call`** โ The `ENTITY_SOUND_EFFECT` packet handler was calling `world.getEntities()` on the Netty IO thread, which Folia blocks. Replaced with a thread-safe entity position cache that requires zero Bukkit API calls on the network thread.
- **Fixed Folia raycast thread-safety violation** โ The raycast task was running on the `GlobalRegionScheduler` while accessing world block data across different region threads. Each player's raycast is now scheduled on their owning region thread.
## โก Performance Improvements
- **Early packet-type filtering** โ The packet listener now checks the packet type *before* any Bukkit API calls. Previously, `Bukkit.getPlayer()`, `player.getWorld()`, `player.hasPermission()`, and `player.getGameMode()` were executed for **every single outbound packet** (chat, tab list, keep-alive, etc.) โ even though we only handle ~10 specific packet types. This eliminates unnecessary overhead for ~90% of all outbound packets.
- **Bypass permission caching** โ `player.hasPermission()` and `player.getGameMode()` are no longer called on the Netty IO thread. A thread-safe bypass cache is updated on the main thread during player join, quit, world change, gamemode change, and `/fpac bypass`, so the packet handler only performs a fast map lookup.
- **Cached reflection calls** โ All scheduler reflection in `PlatformUtil` (`getMethod`, `invoke`) is now resolved once at class load instead of on every scheduler call. The tile entity reflection field in the chunk handler is also cached statically.
- **Reduced debug logging overhead** โ Debug string concatenation in the hot packet path is now properly guarded and only runs when debug mode is actually enabled.
## ๐ฆ Housekeeping
- Bumped config version to `4.2`
Developer information
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle.
maven { name = "Modrinth"; url = "https://api.modrinth.com/maven" }