Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GeyserOfflineShim

A small standalone Spigot/Paper plugin that restores an unsigned Bedrock player's missing name from BedrockClientData.ThirdPartyName, without rebuilding Geyser. No commands, external services, or shim configuration file are needed.

1.0.1: multiple unsigned iPads

The live 1.0.0 test restored Makena successfully, but a second iPad was rejected with is already logged in!. Geyser checks duplicate XUIDs, including empty ones, before SessionLoginEvent. A unique Java UUID cannot solve that earlier check.

Version 1.0.1 also listens to SessionInitializeEvent. For blank-name unsigned OFFLINE sessions it restores the name and replaces the shared empty/zero XUID with a deterministic negative decimal internal ID derived from the sanitized name. Real Xbox IDs are untouched. The next iPad therefore no longer matches the first session's empty XUID. Java offline UUID generation still uses the restored name, so Makena's Java identity from 1.0.0 stays the same.

Install with a full server restart so no 1.0.0 sessions with empty XUIDs remain. Remove the old shim jar before adding the replacement.

Geyser fires this earliest suitable public event just after its duplicate-XUID check and pending-set insertion. This fixes sequential joins and permits multiple unsigned players to remain connected, but two handshakes overlapping that small interval could still produce a rejection; retry the rejected client. Eliminating that race completely would need an earlier Geyser hook or a more invasive packet interception patch. The shim does not fabricate authenticated Xbox accounts; Floodgate and Xbox services are not supported for synthetic IDs.

Install

  1. Build with mvn clean package, or use target/GeyserOfflineShim-1.0.1.jar.
  2. Stop the server. Place the shim jar beside the official Geyser-Spigot.jar in plugins/.
  3. Configure the server and Geyser as below, then start the server. Use a full restart, not /reload or a plugin hot-loader.
  4. Give each iPad a distinct local profile name, ideally STEM01, STEM02, etc. Join and check for: Restored Bedrock local name "STEM 01" -> Java username "STEM_01"

plugin.yml declares a hard dependency on Geyser-Spigot; it works on Spigot and Paper. A separate paper-plugin.yml is deliberately unnecessary. The shim targets Java 17 bytecode; run the Java version required by your current server/Geyser (typically Java 21 or newer). This project is a Maven project and can be opened directly in IntelliJ.

Server and Geyser configuration

This is for an intentionally offline, access-controlled classroom/LAN server. Local names are client supplied, not authenticated: anybody who can reach the server can impersonate one. Restrict both Java and Bedrock access to your trusted network. Geyser officially does not support disabling Bedrock authentication for this use case.

In server.properties:

online-mode=false

For the current Geyser configuration format (build 1234), merge these settings into the existing plugins/Geyser-Spigot/config.yml sections, without duplicating YAML keys:

java:
  auth-type: offline
advanced:
  bedrock:
    validate-bedrock-login: false

Geyser's older format calls the first section remote instead of java; use the format generated by your installed version. Keep the rest of the generated configuration. Verify the effective authentication mode, especially if Floodgate is installed: automatic Floodgate detection can select floodgate, in which case this shim intentionally does nothing.

The shim cannot rescue a connection rejected before SessionLoginEvent, such as an unsupported client version, invalid payload, Xbox validation failure, or guest/splitscreen account. It does not disable authentication itself or bypass online Java authentication.

Behavior and player identity

  • Only OFFLINE connections with blank AuthData.name and absent/blank/0 XUID are candidates. Named users and users with a nonzero XUID stay unchanged; ONLINE and FLOODGATE connections are untouched.
  • Read getClientData().getUsername() (the ThirdPartyName field), strip surrounding whitespace, replace each non-ASCII letter/digit/underscore code point with _, and keep at most 16 resulting characters. Case is preserved; one-character names are allowed by the Java offline protocol.
  • Missing/blank local names are rejected instead of assigning a shared fallback identity.
  • At initialization, replace the name and absent/zero xuid components of the immutable AuthData record. Bedrock UUID, issued-at time, PlayFab ID, and future record components are copied through dynamically. The internal synthetic XUID is stable for the sanitized name.
  • Normally Java profile creation now receives the restored name. The retained SessionLoginEvent fallback can update an already-created Java protocol profile too. Retain the existing protocol object, codec, and connection settings. Refuse to overwrite a Java profile with an unrelated nonblank name or an access token (underscores created by Geyser from an original whitespace-only name are accepted).
  • Log the original local name and restored Java name. Client text is bounded and escaped for logs; no tokens or IP addresses are logged.
  • On incompatible internals, block only candidate blank/unsigned/offline logins with an explanatory disconnect. Other users still pass through. Runtime restoration failures cancel the affected login and log the first failure until restart. Listeners are unregistered on disable.

On an ordinary offline Spigot/Paper server, the final Java UUID is derived from OfflinePlayer:<sanitized-name>. Geyser's Bedrock AuthData.uuid is intentionally unchanged. The Java UUID remains stable only while the final name (including case) remains the same. Proxy forwarding or another identity plugin can change this behavior.

Names must be unique after sanitization and truncation. For example, STEM 01 and STEM-01 both become STEM_01; names sharing the first 16 sanitized characters also collide. The shim does not allocate suffixes or migrate inventories. A collision shares offline identity and can disconnect an existing player. Renaming a device changes its normal offline player identity; back up player data before switching, and migrate old inventories separately if needed.

Why AuthData alone is insufficient

Current GeyserSession.authenticate(String) constructs MinecraftProtocol(username) before connectDownstream() fires SessionLoginEvent. Merely replacing AuthData at the event leaves the Java login packet using the blank name.

The initialization handler now restores identity before Java authentication constructs its profile; the login handler retains the following fallback for blank identities. IdentityBridge isolates all internal reflection: public Lombok getters/setter for session data, the AuthData canonical record constructor, the private session protocol field, and the non-final MinecraftProtocol.profile field. It obtains the new profile using MCProtocolLib's own offline constructor and assigns that profile before the downstream session is created. No recursive authentication call or Geyser fork is used. Added/reordered AuthData components are supported automatically; changed accessor names, class structure, or login timing may require a shim update.

Verification

Verified on 2026-09-08 against official Geyser 2.11.2, Spigot build 1234, commit 9b65a39fc1b37f35655c0b2ab0196e231bc37b4e.

The build pins the published Geyser API snapshot 2.11.2-20260902.202549-8; dependencies are provided by the server, not bundled. Run the unit tests and optional real-binary compatibility smoke test with:

mvn clean package
mvn -Dgeyser.jar=/absolute/path/to/Geyser-Spigot.jar clean package

The optional profile adds server-provided Netty/logging libraries only to the test classpath. The verified official Geyser-Spigot jar has SHA-256 58c4c6cd890c682df74b1bd7f1278fd0d51c999b45b91b3b0ff1e49c42b74b8a.

The 1.0.1 build passed 11 tests, including the official binary compatibility check. Unit tests cover early XUID differentiation for two unsigned clients, signed/online/floodgate preservation, sanitization, signed/named users, null/zero XUID, missing names, preservation of extra record components, prevention of Java identity overwrites, and rollback on setter failure. The optional test resolves reflection against the actual Geyser binary and checks offline profile creation. These do not substitute for a real Bedrock connection test.

Before classroom deployment, join with two unsigned iPads (STEM01 and STEM02) simultaneously, check distinct Java names/UUIDs, give each a different item, and reconnect to confirm persistence. Also join with a signed-in account and confirm its name is unchanged. The user verified one successful live iPad login with 1.0.0. The user also verified two concurrent players on 1.0.1 and stable, distinct Java UUIDs after both disconnected and reconnected in the opposite order. Inventory persistence was not established by the available logs.

About

Small Spigot/Paper shim restoring local Bedrock identities for unsigned offline-mode Geyser players.

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages