class_name LocalAuthProvider extends AuthProvider ## Development identity: no accounts, no passwords, no Steam. ## ## The client generates a 64-bit id once, stores it in user://, and presents it ## as its own ticket. The server takes it at face value. ## ## This is NOT secure and is not meant to be. Anyone can present any id, so ## anyone can claim any account's characters. It is deliberately the same shape ## as the real thing -- opaque ticket in, 64-bit account id out -- so the Steam ## provider replaces it without touching the character store, the protocol, or ## anything that consumes an account id. ## ## Before this game is reachable from the internet, this must be swapped for a ## provider that actually verifies. See docs/ROADMAP.md. const ID_PATH := "user://account_id" var _cached: int = AuthProvider.NO_ACCOUNT func provider_name() -> String: return "local-dev (insecure)" ## Read this machine's id, generating and saving one on first run. func account_id() -> int: if _cached != AuthProvider.NO_ACCOUNT: return _cached if GameOpts.account_override != AuthProvider.NO_ACCOUNT: _cached = GameOpts.account_override return _cached if FileAccess.file_exists(ID_PATH): var f := FileAccess.open(ID_PATH, FileAccess.READ) if f != null: var parsed := int(f.get_as_text().strip_edges()) f.close() if parsed != AuthProvider.NO_ACCOUNT: _cached = parsed return _cached var rng := RandomNumberGenerator.new() rng.randomize() # Positive and comfortably inside 64 bits, so it round-trips through the # store's decimal-string keys without surprises. _cached = absi(rng.randi()) << 20 | (absi(rng.randi()) & 0xFFFFF) var out := FileAccess.open(ID_PATH, FileAccess.WRITE) if out != null: out.store_string(str(_cached)) out.close() GameLog.info("auth", "generated local account id %d" % _cached) return _cached func get_ticket() -> PackedByteArray: return str(account_id()).to_utf8_buffer() ## Accepts whatever it is given, which is the entire security model here. func validate(ticket: PackedByteArray) -> int: if ticket.is_empty() or ticket.size() > 64: return AuthProvider.NO_ACCOUNT var id := int(ticket.get_string_from_utf8().strip_edges()) return id if id > 0 else AuthProvider.NO_ACCOUNT