The short version

A player connects and the plugin checks its local cache. A fresh result means no network request. On a miss, the configured lookup service finds the country and network, returns a normalized answer, and the plugin applies your country, ASN and VPN rules.

Detailed flow

1. Plugin receives a login

The plugin listens to the platform's pre-login event. On Bukkit that's AsyncPlayerPreLoginEvent. On BungeeCord it's LoginEvent. On Velocity it's LoginEvent with the async continuation API. The event gives us the connection IP, the player's name, and (sometimes) their UUID. The plugin then submits the IP to its internal GeoRestrictService.checkIp(...) on a background thread.

2. Cache check

The cache is a ConcurrentHashMap in memory, mirrored to geo_cache.json on disk via a debounced async save (5s window). Saves are atomic: write to .tmp, then Files.move(... ATOMIC_MOVE), with a non-atomic fallback for filesystems that don't support it.

A cache hit short-circuits the entire network path. The cached GeoResponse is fed straight into the rule evaluator. A cache miss goes to step 3.

3. Lookup request

The plugin sends the IP to the configured Worker over HTTPS. The plugin does not contain a provider URL or provider credential. Those choices remain behind the Worker so one plugin config does not have to know every lookup service.

4. Lookup service result

The public Worker keeps successful answers for up to one day. On a full miss it tries its configured data sources and uses an independent fallback server only if they are unavailable. If no route can answer, the plugin follows blockOnLookupFailure. Full deployment details belong in the GitHub README.

5. Rule evaluation

Three rules, in order. First match wins:

  1. Country — list match against countryCode in the configured mode (ALLOWLIST / BLOCKLIST).
  2. ASN — same logic for AS numbers.
  3. VPN — boolean flags (isVpn, isHosting, isProxy) plus a case-insensitive keyword scan of the AS name.

6. Result

If a rule matches, the event is denied with the configured kick message. If nothing matches, the player proceeds normally. In both cases the Discord webhook fires if configured.

Privacy boundary

On a local cache miss, the player's IP leaves the Minecraft server for the configured Worker. A further miss can be sent to a geolocation provider or to the protected fallback server. Each external service has its own logging and retention rules. GeoRestrict does not run a player dashboard or analytics profile.

Read the Privacy Policy for the current services, retention periods and optional Discord data before installing the plugin for a public community.

Caching details

Folia-specific note

The plugin detects Folia at startup and switches to its async scheduler for configuration checks, update checks and cache maintenance. GeoRestrict does not touch world state, so none of these tasks needs region ownership.