Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ZemerTeam/zemer-cipher/llms.txt
Use this file to discover all available pages before exploring further.
PlayerConfigStore owns the player config table at runtime. It merges the bundled asset with a remote copy fetched from GitHub, provides lock-free reads via an immutable @Volatile map, and self-heals on player rotations by refreshing the remote config when a hash miss occurs.
Package: com.zemer.cipher
The remote config is fetched from:
PlayerConfigParser. Only validated payloads ever replace the in-memory map or touch the disk cache.
Members
initialize
player_configs.json asset and overlays the last-good cached remote copy on top of it. Must be called before any get() lookups are made. Called automatically by ZemerCipher.initialize() — you do not need to call this directly.
If the bundled asset is missing or invalid, the config table starts empty and a warning is logged. If the cached remote overlay fails to load for any reason, both the cache body and the ETag meta file are deleted together. Leaving a stale ETag beside a corrupt body would cause every subsequent conditional GET to receive a 304 Not Modified without ever re-downloading the fixed file, locking the device on bundled-only configs until the remote content changes.
scheduleStartupRefresh
refreshIfStale(). Non-blocking — returns immediately. Called automatically by ZemerCipher.initialize().
If the last successful fetch is within the 6-hour TTL, no network request is made. When a fetch does occur, ETag caching (If-None-Match / 304 Not Modified) keeps bandwidth usage minimal. The TTL check uses a signed-range comparison so a backward NTP correction or manual clock change never accidentally holds the TTL open.
get
HardcodedPlayerConfig for the given 8-hex player hash, or null if the hash is not in the current config table.
Alias hashes (populated from the aliases array of each entry) are registered under the same config object as the primary hash, so a lookup by alias returns the same result as a lookup by the primary hash.
The 8-character lowercase hex player hash to look up (e.g.
"445213fb").knownHashes
configEpoch
@Volatile integer. Increments every time a remote refresh changes the in-memory config table. CipherDeobfuscator reads this value when deciding whether an existing CipherWebView needs to be discarded and rebuilt: if the epoch recorded when the WebView was created is lower than the current epoch, the config has been updated and the WebView must be rebuilt to use the new values.
forceRefresh
true if missingHash is now present in the table — whether this call’s fetch did the work, or a concurrent refresh that ran while this call was waiting on refreshMutex already landed the entry.
The cooldown exists to protect the config host from repeated hits when a player hash is unknown both locally and remotely (i.e. the entry simply does not exist yet). The cooldown only arms when the server was actually reached (HTTP 2xx/304/404); a pure network failure resets the stamp so the next trigger retries immediately.
The 8-character lowercase hex hash that was not found in the current table.
true if missingHash is in the table after this call, false if it is still missing (cooldown blocked the fetch, or the remote doesn’t have it either).
refreshAfterStreamRejection
CipherDeobfuscator.onStreamRejected() when a CDN 403 response is received. Unlike forceRefresh, this path does not short-circuit when the current hash is already present in the table — the entry may be present but contain wrong values, producing a signature that is non-throwing but rejected downstream.
Has its own independent cooldown (also 5 minutes) so stream-rejection refreshes cannot arm a cooldown that blocks the unknown-hash forceRefresh self-heal path, and vice versa.
Returns: true if the config table changed as a result of this refresh (i.e. configEpoch has incremented and the cipher WebView will rebuild).
Both
forceRefresh and refreshAfterStreamRejection are single-flight: they serialize on a shared refreshMutex so only one network request is in flight at a time. However, they use independent cooldown stamps. A stream-rejection refresh cannot arm a cooldown that blocks unknown-hash recovery, and an unknown-hash refresh cannot arm a cooldown that blocks stream-rejection recovery. A concurrent call that waits on the mutex will benefit from the work done by the holder without triggering a second fetch.