Initialized openttd-client repo
This commit is contained in:
37
docs/ARCHITECTURE.md
Normal file
37
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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.
|
||||
28
docs/CONTRIBUTING.md
Normal file
28
docs/CONTRIBUTING.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Contributing to OpenTTD Python Client
|
||||
|
||||
We welcome contributions from the community! To maintain the high quality of this library, please follow these guidelines.
|
||||
|
||||
## Code Standards
|
||||
- **Clean OO Code:** Logic must be encapsulated within the `OpenTTDClient` or `OpenTTDProtocol` classes.
|
||||
- **Type Safety:** Use type hints where possible.
|
||||
- **Minimal Dependencies:** Only add new dependencies if absolutely necessary.
|
||||
|
||||
## Testing Mandate
|
||||
We enforce **100% test coverage**. Any new feature or bug fix must include corresponding tests.
|
||||
|
||||
### Running Tests
|
||||
Use `pytest` within the virtual environment:
|
||||
```bash
|
||||
PYTHONPATH=lib ./venv/bin/pytest --cov=openttd --cov-report=term-missing tests/
|
||||
```
|
||||
|
||||
### Types of Tests
|
||||
- **Logic Tests (`tests/test_logic.py`):** High-level client state and API behavior.
|
||||
- **Protocol Tests (`tests/test_protocol.py`):** Low-level binary parsing and encryption.
|
||||
- **E2E Tests (`tests/test_e2e.py`):** Integration tests against a live server.
|
||||
|
||||
## Submitting Changes
|
||||
1. **Fork the repo** and create your branch from `main`.
|
||||
2. **Add tests** for your changes.
|
||||
3. **Run the full test suite** to ensure no regressions and verify 100% coverage.
|
||||
4. **Open a Pull Request** with a detailed description of your changes.
|
||||
33
docs/PROTOCOL.md
Normal file
33
docs/PROTOCOL.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Protocol Internals
|
||||
|
||||
This client supports the modern OpenTTD Game Port protocol (TCP 3979), specifically as implemented in JGRPP.
|
||||
|
||||
## X25519 PAKE Authentication
|
||||
OpenTTD 14+ and JGRPP use a Password-Authenticated Key Exchange to prevent plaintext password leakage.
|
||||
|
||||
### Key Derivation (KDF)
|
||||
We use **Blake2b** (64-byte digest) to derive two 32-byte session keys.
|
||||
- **Input:** `SharedSecret (32)` + `ServerPublicKey (32)` + `ClientPublicKey (32)` + `Password (string)`
|
||||
- **Output:**
|
||||
- `0..31`: Client-to-Server Key
|
||||
- `32..63`: Server-to-Client Key
|
||||
|
||||
### Handshake Nonces
|
||||
The server provides a 24-byte nonce in the `ServerAuthenticationRequest`. This nonce is used for the AEAD challenge during the auth response and for the initial stream encryption setup.
|
||||
|
||||
## Stream Encryption (AEAD)
|
||||
Once `ServerEnableEncryption` is received, all subsequent packets use **XChaCha20-Poly1305** (Authenticated Encryption with Associated Data).
|
||||
|
||||
### Encrypted Packet Format
|
||||
On the wire, encrypted packets have the following structure:
|
||||
1. **Length (2 bytes):** Big-endian uint16 of the *entire* remaining packet.
|
||||
2. **MAC (16 bytes):** The Poly1305 authentication tag.
|
||||
3. **Ciphertext (variable):** The encrypted payload.
|
||||
|
||||
### Decryption Logic
|
||||
The `OpenTTDProtocol` layer uses an `IncrementalAuthenticatedEncryption` state from the Monocypher library. It maintains the nonce state internally. If a MAC check fails (indicating corruption or a wrong key), the client immediately closes the connection (`SocketClosed`).
|
||||
|
||||
## Keep-Alive (Simulation Synchronization)
|
||||
OpenTTD is a lockstep simulation. The server sends `ServerFrame` packets periodically.
|
||||
- **Client Requirement:** You must respond with a `ClientAck` containing the frame number and a one-time `token` provided in the frame packet.
|
||||
- **Timeout:** If the server does not receive an ACK for several in-game days, it will disconnect the client with error code 17 (`TimeoutComputer`).
|
||||
Reference in New Issue
Block a user