@Override
    public Tristate luckperms$hasPermission(String permission, QueryOptions queryOptions) {
        if (permission == null) {
            throw new NullPointerException("permission");
        }
        if (queryOptions == null) {
            throw new NullPointerException("queryOptions");
        }

        final User user = this.luckperms$user;
        if (user == null || this.luckperms$queryOptions == null) {
            // "fake" players will have our mixin, but won't have been initialised.
            return Tristate.UNDEFINED;
        }

        PermissionCache data = user.getCachedData().getPermissionData(queryOptions);
        return data.checkPermission(permission, CheckOrigin.PLATFORM_API_HAS_PERMISSION).result();
    }
Hold a QueryOptionsCache instance on the player itself, so we can just cast instead of
having to maintain a map of Player->Cache.
fabric/src/main/java/me/lucko/luckperms/fabric/mixin/ServerPlayerEntityMixin.java