Files
openttd-client/docs/ARCHITECTURE.md
kovagoadi cb6849ed55
Some checks failed
Continuous Integration / lint-and-security (push) Successful in 4m14s
Continuous Integration / tests-and-coverage (push) Successful in 33s
CodeQL Analysis / Analyze (python) (push) Failing after 4m17s
Initialized openttd-client repo
2026-06-05 23:18:09 +02:00

2.2 KiB

Architecture and Design

This project follows a strict Object-Oriented approach to manage the complexity of the OpenTTD network protocol.

Component Overview

1. OpenTTDProtocol (Low-Level)

Inherits from openttd_protocol.wire.tcp.TCPProtocol.

  • Responsibility: Binary serialization/deserialization and stream encryption.
  • Encryption Layer: It overrides send_packet and receive_packet to wrap/unwrap AEAD (XChaCha20-Poly1305) payloads.
  • Manual Dispatch: Uses a manual dispatch mechanism to ensure that even encrypted packets are correctly mapped to their high-level handlers.

2. OpenTTDClient (High-Level)

The primary API for developers.

  • Responsibility: Handshake orchestration, state management, and keep-alive.
  • Event-Driven: Uses asyncio.Event (like self.joined) to signal state changes to the calling code.
  • Callback System: Provides hooks like on_chat to allow users to react to game events without modifying the core library.

Handshake Flow

  1. Connection: TCP connection established to Port 3979.
  2. Information: ClientGameInfo sent to verify server version.
  3. Join: ClientJoin sent with the specific JGRPP revision string.
  4. Authentication: Server sends ServerAuthenticationRequest (Type 1: PAKE).
  5. PAKE Exchange:
    • Shared Secret derived via X25519.
    • Session Keys derived via Blake2b hashing of (SharedSecret + ServerPub + OurPub + Password).
    • Encrypted challenge sent back via ClientAuthenticationResponse.
  6. Encryption: Server sends ServerEnableEncryption. The Protocol layer activates the AEAD stream.
  7. Identification: ClientIdentify sent (now encrypted).
  8. Map Synchronization: ServerWelcome received -> ClientGetMap sent -> Map segments received -> ClientMapOk sent.
  9. Active State: client.joined is set. The client responds to ServerFrame with ClientAck to prevent timeouts.

Error Handling

  • Shutdown Event: Every fatal error or manual quit triggers a shutdown_event.
  • Graceful Exit: The quit() method sends a ClientQuit packet before closing the transport.
  • Fallback: Unknown packets are caught and mapped to receive_ServerUnused to prevent the simulation loop from crashing.