Legacy Web PHP Adapters
Relevant source files
The following files were used as context for generating this wiki page:
- src/osu_server/transports/stable/web_legacy/getscores.py
- src/osu_server/transports/stable/web_legacy/score_submit.py
- src/osu_server/transports/stable/web_legacy/replay_download.py
- tests/unit/transports/web_legacy/test_getscores_handler.py
- tests/unit/transports/web_legacy/test_getscores_status_mapper.py
- tests/integration/test_getscores_unavailable_paths.py
- src/athena_cli/stable_verification/score_submit.py
- AGENTS.md
The Legacy Web PHP Adapters system provides a compatibility layer for stable osu! clients by emulating the traditional PHP-based web endpoints (/web/*.php). This module ensures that core functionality—such as leaderboard retrieval, score submission, and registration—remains accessible to older client versions while delegating the underlying logic to modern Python-based domain services and repositories.
These adapters function as hexagonal runtime adapters within the transports/stable/web_legacy package. They handle wire parsing (pipe-delimited plain text or multipart forms), authentication verification, and response formatting to match the externally observable behavior of the original Bancho server.
Sources: AGENTS.md:14-18, README.md:104-108
Architecture and Data Flow
The legacy adapters sit at the edge of the system, transforming HTTP requests into domain-specific queries and commands. They are strictly decoupled from persistence implementations, relying on service interfaces and Dishka for dependency injection.
Sources: AGENTS.md:102-106, tests/unit/test_architecture_boundary_contract.py:112-123
Dependency Structure
This diagram shows the flow from a client request through the legacy adapter layer into the core service layer.
Leaderboard Retrieval (GetScores)
The GetscoresHandler manages requests to /web/osu-osz2-getscores.php. It coordinates authentication, beatmap resolution through the Mirror Service, and leaderboard querying.
Sources: tests/unit/transports/web_legacy/test_getscores_handler.py:273-305, tests/integration/test_getscores_unavailable_paths.py:236-240
Processing Pipeline
- Request Parsing: The
GetscoresQueryParserextracts fields likec(checksum),us(username), andha(password MD5 hash). - Authentication: Credentials are verified against the
SessionCredentialsQuery. Failure results in a401 Unauthorizedwith an empty body. - Beatmap Resolution: The system attempts to resolve the beatmap by checksum or filename. It may wait for a metadata fetch if the map is not yet known.
- Warmup: A
RequestBeatmapFileWarmupUseCaseis triggered to ensure the.osufile is available for PP calculation. - Response Construction: Results are formatted into a pipe-delimited string.
Sources: tests/unit/transports/web_legacy/test_getscores_handler.py:307-330, tests/integration/test_getscores_unavailable_paths.py:446-465
Status Mapping
The GetscoresStatusMapper translates internal BeatmapRankStatus values into legacy wire integers.
| BeatmapRankStatus | Wire Value | Description |
|---|---|---|
NOT_SUBMITTED / UNKNOWN | None (maps to -1) | Map not found or not on server |
PENDING / WIP / GRAVEYARD | 0 | Header returned, but no leaderboard |
RANKED | 2 | Fully ranked |
APPROVED | 3 | Approved (counts for score, not PP in some eras) |
QUALIFIED | 4 | Qualified for ranking |
LOVED | 5 | Loved status |
| Sources: tests/unit/transports/web_legacy/test_getscores_status_mapper.py:44-106 |
Response Format
- Success (Header):
status|failed|beatmap_id|beatmapset_id|count||followed by metadata lines. - Unavailable:
-1|false - Update Available:
1|false
Sources: tests/integration/test_getscores_unavailable_paths.py:252-276, tests/unit/transports/web_legacy/test_getscores_fixtures.py:65-75
Score Submission
The score submission adapter handles POST requests containing score data and replay files. It uses a multipart/form-data format and requires specific validation to ensure compatibility.
Sources: src/athena_cli/stable_verification/score_submit.py:20-65
Verification Requirements
The ScoreSubmitVerifier enforces strict metadata checks:
- Required Fields:
score,iv,pass,x,ft,osuver. - Secret Policy: Must use
metadata-onlypolicy. - Payload: Must include at least two
scorefields (one typically containing the replay).
Sources: src/athena_cli/stable_verification/score_submit.py:155-185
Submission Flow
The sequence above illustrates how the legacy adapter receives the multipart form and initiates the domain command.
Infrastructure and Configuration
The adapters rely on the AppConfig for domain-wide settings, such as beatmap_default_bounded_wait_seconds, which controls how long the system waits for a mirror to fetch beatmap metadata before returning a "not found" response.
Sources: tests/unit/transports/web_legacy/test_getscores_handler.py:408-410, tests/unit/transports/test_health.py:215-225
Diagnostic Endpoints
While not strictly "legacy," the adapters often reside alongside health checks that verify connectivity to:
- PostgreSQL: Primary persistence for users and scores.
- Valkey: Used for session state and real-time presence.
Sources: tests/unit/transports/test_health.py:133-165
Summary
The Legacy Web PHP Adapters are a critical compatibility component of Athena. By isolating legacy wire-format concerns from the core domain logic, they allow the project to support the stable osu! client while maintaining a clean, modern architecture. Key functionalities like GetScores and Score Submission are mapped from legacy pipe-delimited and multipart formats into typed Python dataclasses, ensuring type safety and architectural integrity across the transport boundary.
Sources: AGENTS.md:14-18, README.md:104-108